{"repo":"NousResearch/hermes-agent","public_id":"67052","display_label":"computer_use alignment with cua-driver verify/escalate ladder","summary":"The members relate to computer_use and cua-driver behavior: one reports that foreground delivery is unsupported and that Chromium background input leads to a dead end on Windows, while the other two concern following cua-driver's verify → escalate ladder (one titled as a feature/PR, one as a limitation). The titles do not show whether these are fully the same underlying problem.","subsystem":null,"counts":{"members":3,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":2,"merged_prs":1,"attempt_prs":0},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":67123,"merged_at":"2026-07-18T20:59:36+00:00"},"resolution":{"state":"partially_resolved","by":"pr-67123"},"freshness":{"state":"verified","last_verified_at":"2026-07-18T20:59:36+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.4533333333333333},"fix_edges":[{"issue_number":59731,"pr_number":67123,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false,"pr_merged_at":"2026-07-18T20:59:36+00:00"},{"issue_number":67052,"pr_number":67123,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-07-18T20:59:36+00:00"}],"members":[{"kind":"issue","number":67052,"title":"computer_use cannot follow cua-driver's verify/escalate ladder","url":null,"state":"closed"},{"kind":"issue","number":68471,"title":"computer_use: foreground delivery unsupported — Chromium background input creates dead end on Windows","url":null,"state":"closed"},{"kind":"pr","number":67123,"title":"feat(computer_use): follow cua-driver's verify → escalate ladder (#67052)","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/prompt_builder.py","skills/computer-use/SKILL.md","tests/tools/test_computer_use_delivery_ladder.py","tools/computer_use/backend.py","tools/computer_use/cua_backend.py","tools/computer_use/schema.py","tools/computer_use/tool.py"],"fix_hunks":[{"file":"agent/prompt_builder.py","hunks":["@@ -557,6 +557,29 @@ def computer_use_guidance(platform_name: Optional[str] = None) -> str:\n         \"4. After any state-changing action, re-capture to verify. You can \"\n         \"pass `capture_after=true` to get the follow-up screenshot in one \"\n         \"round-trip.\\n\\n\"\n+        \"## Verify → escalate ladder (background-first, NOT background-only)\\n\"\n+        \"Background delivery is the DEFAULT and the co-work path, but it is \"\n+        \"the first rung, not the only one. Read each action's structured \"\n+        \"result and climb only when the driver tells you to:\\n\"\n+        \"- `effect: 'confirmed'` + `verified: true` — the driver read the \"\n+        \"result back. Done.\\n\"\n+        \"- `effect: 'unverifiable'` — the input was delivered but the driver \"\n+        \"can't confirm it. Re-capture and check the screenshot/tree yourself \"\n+        \"before deciding it worked.\\n\"\n+        \"- `effect: 'suspected_noop'`, `code: 'background_unavailable'`, or an \"\n+        \"`escalation.recommended` field — the action did NOT land. Follow \"\n+        \"`escalation.recommended`:\\n\"\n+        \"  - `'px'` → re-issue addressing the target by `coordinate=[x,y]` \"\n+        \"read off the screenshot instead of `element`.\\n\"\n+        \"  - `'foreground'` (or a pixel click still didn't land) → re-issue \"\n+        \"the SAME action with `delivery_mode='foreground'`. This briefly \"\n+        \"raises the window; it needs its own approval and is only appropriate \"\n+        \"when the user isn't actively working. Common for Electron/Chromium \"\n+        \"consent dialogs, DirectInput games, and raw-input canvases.\\n\"\n+        \"- Escalate to foreground as a REACTION to a returned signal, never \"\n+        \"as a prediction from the app being Electron/Chromium/GTK. Do not \"\n+        \"silently retry the same rung expecting a different result, and do \"\n+        \"not conclude 'cua-driver can't drive this app' — climb the ladder.\\n\\n\"\n         \"## Background mode rules\\n\"\n         \"- Do NOT use `raise_window=true` on `focus_app` unless the user \"\n         \"explicitly asked you to bring a window to front. Input routing to \""]},{"file":"skills/computer-use/SKILL.md","hunks":["@@ -101,6 +101,56 @@ All actions accept optional `capture_after=True` to get a follow-up\n screenshot in the same tool call. All actions that target an element\n accept `modifiers=[…]` for held keys.\n \n+The input actions (`click`, `double_click`, `right_click`, `middle_click`,\n+`drag`, `scroll`, `type`, `key`) also accept `delivery_mode` and\n+`bring_to_front` — see \"The verify → escalate ladder\" below.\n+\n+## The verify → escalate ladder (background-first)\n+\n+cua-driver delivers input in the **background** by default (no focus steal),\n+but that is the first rung, not the only one. Every input action returns a\n+structured verdict; read it and climb only when the driver tells you to.\n+\n+Returned fields (present when the driver supports them):\n+- `effect`: `\"confirmed\"` (driver read the result back — done), `\"unverifiable\"`\n+  (delivered, but confirm it yourself by re-capturing), or `\"suspected_noop\"`\n+  (ran but almost certainly did nothing).\n+- `escalation`: `{recommended: \"px\" | \"foreground\" | \"page\", reason}` — present\n+  only when there's a next rung to try.\n+- `code`: a structured refusal like `\"background_unavailable\"` or\n+  `\"foreground_unsupported\"`.\n+- `verified`: `true` only on AX read-back.\n+\n+Walk it in order:\n+\n+1. **Element, background (default).** `click(element=N)`. If `effect:\"confirmed\"`,\n+   you're done.\n+2. **Pixel, background.** On `escalation.recommended == \"px\"` (or a `degraded`\n+   capture with an empty element list), click by `coordinate=[x,y]` read off the\n+   screenshot instead of `element`.\n+3. **Foreground.** On `escalation.recommended == \"foreground\"`,\n+   `code:\"background_unavailable\"`, or a pixel click that still didn't land,\n+   re-issue the SAME action with `delivery_mode=\"foreground\"`. This briefly\n+   raises the window and restores focus after; pair with `bring_to_front=True`\n+   for a short sequence to avoid per-call flashes. It needs its own approval\n+   (it's a visible focus change) and is only appropriate when the user isn't\n+   actively working. Classic cases: Electron/Chromium consent dialogs (e.g.\n+   tldraw offline's \"Run Script\"), DirectInput games, raw-input canvases.\n+\n+```\n+computer_use(action=\"click\", element=7)\n+# → {effect: \"suspected_noop\", escalation: {recommended: \"foreground\", ...}}\n+computer_use(action=\"click\", element=7, delivery_mode=\"foreground\")\n+# → {effect: \"unverifiable\", path: \"x11_pixel_fg\"}   then re-capture to confirm\n+```\n+\n+**Escalate to foreground as a REACTION to a returned signal, never as a\n+prediction** from the app being Electron/Chromium/GTK. Different controls in\n+the same app behave differently. Do NOT silently retry the same rung, and do\n+NOT conclude \"cua-driver can't drive this app\" — climb the ladder. If\n+`delivery_mode=\"foreground\"` returns `code:\"foreground_unsupported\"`, the\n+driver is too old; tell the user to update cua-driver.\n+\n ### Key shortcuts vary per platform\n \n Use the host's idiomatic modifier:","@@ -205,7 +255,7 @@ in your conversation context.\n | `cua-driver not installed` | Run `hermes computer-use install`, or `hermes tools` and enable Computer Use |\n | Captures consistently return empty / \"no on-screen window\" | On Linux: DISPLAY may not be set (X11) or you're on pure Wayland — ask the user to run `hermes computer-use doctor`. On Windows: you may be in Session 0 (SSH session) instead of the interactive desktop — see the cua-driver `WINDOWS.md` deep-dive |\n | Element index stale (\"Element N not in cache\") | SOM indices are only valid until the next `capture`. Re-capture before clicking. The wrapper carries opaque `element_token`s for stale-detection; you'll see an explicit error rather than a wrong click |\n-| Click had no effect | Re-capture and verify. A modal that wasn't visible before may be blocking input. Dismiss it (usually `escape` or click its close button) before retrying |\n+| Click had no effect | Read the structured verdict, don't just recapture. `effect:\"unverifiable\"` → re-capture and confirm yourself. `effect:\"suspected_noop\"` / `code:\"background_unavailable\"` / `escalation.recommended` → climb the ladder: try `coordinate=[x,y]` (px), then `delivery_mode=\"foreground\"`. A modal (e.g. an Electron consent dialog) may be blocking input — foreground delivery is how you dismiss it. Don't conclude the app is undrivable |\n | Type text disappears into a terminal emulator | cua-driver detects terminals (Ghostty, iTerm2, Terminal.app, Windows Terminal, mintty, etc.) and routes through key-event synthesis — should \"just work\" on a recent cua-driver. If it doesn't, ask the user to run `hermes computer-use doctor` |\n | `blocked pattern in type text` | You tried to `type` a shell command matching the dangerous-pattern block list (`curl ... \\| bash`, `sudo rm -rf`, etc.). Break the command up or reconsider |\n | Anything else weird | **First action: ask the user to run `hermes computer-use doctor`.** It runs the cua-driver `health_report` MCP tool and prints a structured per-check matrix. Their output tells you (and them) exactly what's wrong |"]},{"file":"tests/tools/test_computer_use_delivery_ladder.py","hunks":["@@ -0,0 +1,291 @@\n+\"\"\"Regression tests for the cua-driver verify → escalate ladder.\n+\n+Covers NousResearch/hermes-agent#67052:\n+  - Phase A: cua-driver structured verdicts (verified/effect/escalation/code/\n+    degraded/path) are preserved through ActionResult and surfaced in the\n+    model-facing response, additively (old drivers omit them cleanly).\n+  - Phase B: delivery_mode is model-reachable, capability-gated, and refuses\n+    with foreground_unsupported on an old driver rather than silently\n+    downgrading to background.\n+  - Phase C: foreground approval is scoped by (action, delivery_mode) and by\n+    session_id, so a background approval never silently authorizes foreground\n+    and one run's unlock never leaks into another.\n+\n+Stdlib + pytest + unittest.mock only. No live cua-driver, no network.\n+\"\"\"\n+\n+from __future__ import annotations\n+\n+import json\n+import os\n+from typing import Any, Dict, Optional\n+from unittest.mock import patch\n+\n+import pytest\n+\n+\n[redacted:email](autouse=True)\n+def _reset():\n+    from tools.computer_use.tool import reset_backend_for_tests\n+    reset_backend_for_tests()\n+    yield\n+    reset_backend_for_tests()\n+\n+\n+# ---------------------------------------------------------------------------\n+# Phase A — structured verdict normalization (_action_result_from)\n+# ---------------------------------------------------------------------------\n+\n+class _FakeSession:\n+    \"\"\"Minimal cua-driver session stub returning a canned tool result.\"\"\"\n+\n+    def __init__(self, out: Dict[str, Any], capabilities: Optional[set] = None):\n+        self._out = out\n+        self._caps = capabilities or set()\n+        self.last_args: Dict[str, Any] = {}\n+\n+    def call_tool(self, name: str, args: Dict[str, Any], timeout: float = 30.0):\n+        self.last_args = args\n+        return self._out\n+\n+    def supports_capability(self, capability: str, tool: Optional[str] = None) -> bool:\n+        return capability in self._caps\n+\n+\n+def _make_backend(session: _FakeSession):\n+    from tools.computer_use.cua_backend import CuaDriverBackend\n+    be = CuaDriverBackend.__new__(CuaDriverBackend)\n+    be._session = session               # type: ignore[attr-defined]\n+    be._session_id = \"test-run\"          # type: ignore[attr-defined]\n+    be._snapshot_tokens = {}             # type: ignore[attr-defined]\n+    be._active_pid = 4242                # type: ignore[attr-defined]\n+    be._active_window_id = 7             # type: ignore[attr-defined]\n+    return be\n+\n+\n+def test_confirmed_verdict_is_preserved():\n+    out = {\n+        \"isError\": False, \"data\": {\"message\": \"ok\"},\n+        \"structuredContent\": {\"verified\": True, \"effect\": \"confirmed\", \"path\": \"ax\"},\n+    }\n+    be = _make_backend(_FakeSession(out))\n+    res = be.click(element=3)\n+    assert res.ok is True\n+    assert res.verified is True\n+    assert res.effect == \"confirmed\"\n+    assert res.path == \"ax\"\n+    assert res.escalation is None\n+\n+\n+def test_suspected_noop_carries_escalation():\n+    out = {\n+        \"isError\": False, \"data\": {},\n+        \"structuredContent\": {\n+            \"effect\": \"suspected_noop\",\n+            \"escalation\": {\"recommended\": \"foreground\", \"reason\": \"occluded renderer\"},\n+            \"code\": \"background_unavailable\",\n+        },\n+    }\n+    be = _make_backend(_FakeSession(out))\n+    res = be.click(element=3)\n+    assert res.effect == \"suspected_noop\"\n+    assert res.escalation == {\"recommended\": \"foreground\", \"reason\": \"occluded renderer\"}\n+    assert res.code == \"background_unavailable\"\n+    # transport ok, but semantically not confirmed\n+    assert res.verified is None\n+\n+\n+def test_unverifiable_distinct_from_success_and_failure():\n+    out = {\n+        \"isError\": False, \"data\": {},\n+        \"structuredContent\": {\"effect\": \"unverifiable\", \"verified\": False, \"path\": \"x11_pixel\"},\n+    }\n+    be = _make_backend(_FakeSession(out))\n+    res = be.click(x=10, y=20)\n+    assert res.ok is True            # transport succeeded\n+    assert res.verified is False     # ... but not confirmed\n+    assert res.effect == \"unverifiable\"\n+\n+\n+def test_degraded_capture_signal_preserved():\n+    out = {\n+        \"isError\": False, \"data\": {},\n+        \"structuredContent\": {\"effect\": \"suspected_noop\", \"degraded\": True,\n+                              \"escalation\": {\"recommended\": \"px\", \"reason\": \"empty tree\"}},\n+    }\n+    be = _make_backend(_FakeSession(out))\n+    res = be.scroll(direction=\"down\", element=1)\n+    assert res.degraded is True\n+    assert res.escalation[\"recommended\"] == \"px\"\n+\n+\n+def test_old_driver_without_structured_content_is_clean():\n+    \"\"\"A driver that returns no structuredContent leaves every verdict field\n+    None — unchanged behavior, no crash.\"\"\"\n+    out = {\"isError\": False, \"data\": {\"message\": \"done\"}, \"structuredContent\": None}\n+    be = _make_backend(_FakeSession(out))\n+    res = be.click(element=3)\n+    assert res.ok is True\n+    assert res.message == \"done\"\n+    assert res.verified is None\n+    assert res.effect is None\n+    assert res.escalation is None\n+    assert res.code is None\n+    assert res.path is None\n+\n+\n+def test_text_response_surfaces_fields_additively():\n+    from tools.computer_use.backend import ActionResult\n+    from tools.computer_use.tool import _text_response\n+\n+    # Full verdict → all fields present.\n+    r = ActionResult(ok=True, action=\"click\", effect=\"suspected_noop\",\n+                     escalation={\"recommended\": \"foreground\"}, code=\"background_unavailable\",\n+                     path=\"ax\", verified=False)\n+    payload = json.loads(_text_response(r))\n+    assert payload[\"effect\"] == \"suspected_noop\"\n+    assert payload[\"escalation\"] == {\"recommended\": \"foreground\"}\n+    assert payload[\"code\"] == \"background_unavailable\"\n+    assert payload[\"verified\"] is False\n+\n+    # Bare result (old driver) → only ok/action, no None noise.\n+    r2 = ActionResult(ok=True, action=\"click\")\n+    payload2 = json.loads(_text_response(r2))\n+    assert payload2 == {\"ok\": True, \"action\": \"click\"}\n+    for k in (\"effect\", \"escalation\", \"code\", \"verified\", \"path\", \"degraded\", \"delivery_mode\"):\n+        assert k not in payload2\n+\n+\n+# ---------------------------------------------------------------------------\n+# Phase B — delivery_mode threading + capability gating\n+# ---------------------------------------------------------------------------\n+\n+def test_background_is_default_no_flag_sent():\n+    out = {\"isError\": False, \"data\": {}, \"structuredContent\": {\"effect\": \"confirmed\"}}\n+    sess = _FakeSession(out)\n+    be = _make_backend(sess)\n+    be.click(element=1)  # no delivery_mode\n+    assert \"delivery_mode\" not in sess.last_args\n+\n+\n+def test_foreground_sent_when_capability_present():\n+    out = {\"isError\": False, \"data\": {}, \"structuredContent\": {\"effect\": \"unverifiable\"}}\n+    sess = _FakeSession(out, capabilities={\"input.delivery_mode\"})\n+    be = _make_backend(sess)\n+    res = be.click(element=1, delivery_mode=\"foreground\", bring_to_front=True)\n+    assert sess.last_args.get(\"delivery_mode\") == \"foreground\"\n+    assert sess.last_args.get(\"bring_to_front\") is True\n+    assert res.delivery_mode == \"foreground\"\n+\n+\n+def test_foreground_refused_on_old_driver():\n+    \"\"\"Old driver lacking the capability must NOT silently downgrade — it\n+    returns a structured foreground_unsupported result.\"\"\"\n+    out = {\"isError\": False, \"data\": {}, \"structuredContent\": {}}\n+    sess = _FakeSession(out, capabilities=set())  # no input.delivery_mode\n+    be = _make_backend(sess)\n+    res = be.click(element=1, delivery_mode=\"foreground\")\n+    assert res.ok is False\n+    assert res.code == \"foreground_unsupported\"\n+    # crucially: no tool call was made with a silent background downgrade\n+    assert sess.last_args == {}\n+\n+\n+def test_bad_delivery_mode_rejected():\n+    out = {\"isError\": False, \"data\": {}, \"structuredContent\": {}}\n+    sess = _FakeSession(out, capabilities={\"input.delivery_mode\"})\n+    be = _make_backend(sess)\n+    res = be.type_text(\"hi\", delivery_mode=\"sideways\")\n+    assert res.ok is False\n+    assert res.code == \"bad_delivery_mode\"\n+\n+\n+def test_dispatcher_threads_delivery_mode_to_backend():\n+    \"\"\"End-to-end through the tool dispatcher with the noop backend.\"\"\"\n+    from tools.computer_use import tool as cu\n+    with patch.dict(os.environ, {\"HERMES_COMPUTER_USE_BACKEND\": \"noop\"}, clear=False):\n+        cu.reset_backend_for_tests()\n+        be = cu._get_backend()\n+        cu.handle_computer_use({\"action\": \"click\", \"element\": 5,\n+                                \"delivery_mode\": \"foreground\"})\n+        # noop records kwargs; find the click call\n+        clicks = [kw for (name, kw) in be.calls if name == \"click\"]  # type: ignore[attr-defined]\n+        assert clicks and clicks[-1].get(\"delivery_mode\") == \"foreground\"\n+\n+\n+# ---------------------------------------------------------------------------\n+# Phase C — foreground approval scoping (action + delivery_mode + session)\n+# ---------------------------------------------------------------------------\n+\n+def test_background_approval_does_not_authorize_foreground():\n+    from tools.computer_use import tool as cu\n+\n+    seen = []\n+\n+    def cb(action, args, summary):\n+        seen.append((action, args.get(\"delivery_mode\")))\n+        return \"approve_session\"\n+\n+    cu.set_approval_callback(cb)\n+    try:\n+        # Background click, approve for session.\n+        assert cu._request_approval(\"click\", {}, \"sess-A\") is None\n+        # A second background click needs no prompt (cached).\n+        assert cu._request_approval(\"click\", {}, \"sess-A\") is None\n+        assert len(seen) == 1\n+        # Foreground click on the SAME action must prompt again — the\n+        # background approval does not cover it.\n+        assert cu._request_approval(\"click\", {\"delivery_mode\": \"foreground\"}, \"sess-A\") is None\n+        assert len(seen) == 2\n+        assert seen[-1] == (\"click\", \"foreground\")\n+    finally:\n+        cu.set_approval_callback(None)\n+\n+\n+def test_approval_state_is_session_scoped():\n+    from tools.computer_use import tool as cu\n+\n+    calls = []\n+\n+    def cb(action, args, summary):\n+        calls.append((action, args.get(\"delivery_mode\")))\n+        return \"approve_session\"\n+\n+    cu.set_approval_callback(cb)\n+    try:\n+        # Run A approves foreground click.\n+        cu._request_approval(\"click\", {\"delivery_mode\": \"foreground\"}, \"run-A\")\n+        # Run B has NOT — it must prompt independently.\n+        n_before = len(calls)\n+        cu._request_approval(\"click\", {\"delivery_mode\": \"foreground\"}, \"run-B\")\n+        assert len(calls) == n_before + 1\n+    finally:\n+        cu.set_approval_callback(None)\n+\n+\n+def test_always_approve_covers_foreground():\n+    from tools.computer_use import tool as cu\n+\n+    calls = []\n+\n+    def cb(action, args, summary):\n+        calls.append(action)\n+        return \"always_approve\"\n+\n+    cu.set_approval_callback(cb)\n+    try:\n+        # First call unlocks everything for this session.\n+        cu._request_approval(\"click\", {}, \"run-C\")\n+        # Foreground now sails through without another prompt.\n+        cu._request_approval(\"click\", {\"delivery_mode\": \"foreground\"}, \"run-C\")\n+        assert len(calls) == 1\n+    finally:\n+        cu.set_approval_callback(None)\n+\n+\n+def test_foreground_summary_warns_about_focus_change():\n+    from tools.computer_use.tool import _summarize_action\n+    s = _summarize_action(\"click\", {\"element\": 3, \"delivery_mode\": \"foreground\"})\n+    assert \"FOREGROUND\" in s\n+    bg = _summarize_action(\"click\", {\"element\": 3})\n+    assert \"FOREGROUND\" not in bg"]},{"file":"tools/computer_use/backend.py","hunks":["@@ -69,7 +69,16 @@ class CaptureResult:\n \n @dataclass\n class ActionResult:\n-    \"\"\"Result of any action (click / type / scroll / drag / key / wait).\"\"\"\n+    \"\"\"Result of any action (click / type / scroll / drag / key / wait).\n+\n+    Beyond the transport-level ``ok`` flag, this carries cua-driver's\n+    structured action verdict so the model can follow the documented\n+    verify → escalate ladder (NousResearch/hermes-agent#67052). ``ok`` stays\n+    tool/transport success only — it is NOT the semantic verdict. Read\n+    ``effect`` / ``escalation`` to decide the next rung. All structured\n+    fields are optional and additive: an older driver that omits\n+    ``structuredContent`` leaves them ``None`` and behavior is unchanged.\n+    \"\"\"\n \n     ok: bool\n     action: str","@@ -79,6 +88,24 @@ class ActionResult:\n     capture: Optional[CaptureResult] = None\n     # Arbitrary extra fields for debugging / telemetry.\n     meta: Dict[str, Any] = field(default_factory=dict)\n+    # ── cua-driver structured verdict (additive; None on old drivers) ──\n+    # AX read-back verification: True = driver read the effect back,\n+    # False = ran but unconfirmed, None = tool doesn't carry the field.\n+    verified: Optional[bool] = None\n+    # Confidence signal: \"confirmed\" | \"unverifiable\" | \"suspected_noop\".\n+    effect: Optional[str] = None\n+    # Machine-readable next-rung hint: {\"recommended\": \"px\"|\"foreground\"|\"page\",\n+    # \"reason\": str} — present only when the driver recommends climbing.\n+    escalation: Optional[Dict[str, Any]] = None\n+    # Delivery rung that actually ran (e.g. \"ax\", \"x11_pixel\", \"cgevent_fg\").\n+    path: Optional[str] = None\n+    # True when an AX walk found no actionable elements (act by px instead).\n+    degraded: Optional[bool] = None\n+    # The delivery_mode the caller requested for this action, echoed back.\n+    delivery_mode: Optional[str] = None\n+    # A structured refusal code (e.g. \"background_unavailable\",\n+    # \"foreground_unsupported\", \"desktop_scope_disabled\") when present.\n+    code: Optional[str] = None\n \n \n class ComputerUseBackend(ABC):","@@ -118,6 +145,8 @@ def click(\n         button: str = \"left\",           # left | right | middle\n         click_count: int = 1,\n         modifiers: Optional[List[str]] = None,\n+        delivery_mode: Optional[str] = None,   # background (default) | foreground\n+        bring_to_front: bool = False,\n     ) -> ActionResult: ...\n \n     @abstractmethod","@@ -130,6 +159,8 @@ def drag(\n         to_xy: Optional[Tuple[int, int]] = None,\n         button: str = \"left\",\n         modifiers: Optional[List[str]] = None,\n+        delivery_mode: Optional[str] = None,\n+        bring_to_front: bool = False,\n     ) -> ActionResult: ...\n \n     @abstractmethod","@@ -142,14 +173,18 @@ def scroll(\n         x: Optional[int] = None,\n         y: Optional[int] = None,\n         modifiers: Optional[List[str]] = None,\n+        delivery_mode: Optional[str] = None,\n+        bring_to_front: bool = False,\n     ) -> ActionResult: ...\n \n     # ── Keyboard ────────────────────────────────────────────────────\n     @abstractmethod\n-    def type_text(self, text: str) -> ActionResult: ...\n+    def type_text(self, text: str, *, delivery_mode: Optional[str] = None,\n+                  bring_to_front: bool = False) -> ActionResult: ...\n \n     @abstractmethod\n-    def key(self, keys: str) -> ActionResult:\n+    def key(self, keys: str, *, delivery_mode: Optional[str] = None,\n+            bring_to_front: bool = False) -> ActionResult:\n         \"\"\"Send a key combo, e.g. 'cmd+s', 'ctrl+alt+t', 'return'.\"\"\"\n \n     # ── Introspection ───────────────────────────────────────────────"]},{"file":"tools/computer_use/cua_backend.py","hunks":["@@ -59,6 +59,70 @@\n logger = logging.getLogger(__name__)\n \n \n+def _action_result_from(\n+    name: str,\n+    ok: bool,\n+    message: str,\n+    meta: Dict[str, Any],\n+    structured: Dict[str, Any],\n+    *,\n+    requested_delivery: Optional[str] = None,\n+) -> ActionResult:\n+    \"\"\"Build an ActionResult, lifting cua-driver's structured verdict.\n+\n+    All structured fields are additive: a driver that omits\n+    ``structuredContent`` (or any individual field) leaves the corresponding\n+    ActionResult attribute ``None``, so callers and tests see unchanged\n+    behavior on old drivers. See the action response shape in\n+    cua-driver's mcp-tool-notes and NousResearch/hermes-agent#67052.\n+    \"\"\"\n+    sc = structured if isinstance(structured, dict) else {}\n+\n+    def _pick(key: str) -> Any:\n+        # structuredContent is canonical; fall back to a flattened meta copy.\n+        if key in sc:\n+            return sc.get(key)\n+        return meta.get(key)\n+\n+    verified = _pick(\"verified\")\n+    if not isinstance(verified, bool):\n+        verified = None\n+    effect = _pick(\"effect\")\n+    if not isinstance(effect, str):\n+        effect = None\n+    escalation = _pick(\"escalation\")\n+    if not isinstance(escalation, dict):\n+        escalation = None\n+    path = _pick(\"path\")\n+    if not isinstance(path, str):\n+        path = None\n+    degraded = _pick(\"degraded\")\n+    if not isinstance(degraded, bool):\n+        degraded = None\n+    # Refusal/limitation code — drivers spell it \"code\" or \"reason_code\".\n+    code = _pick(\"code\") or _pick(\"reason_code\")\n+    if not isinstance(code, str):\n+        code = None\n+    # Echo the delivery mode the caller actually requested (the driver's\n+    # `path` records the rung that ran; this records what we asked for).\n+    delivery_mode = requested_delivery if isinstance(requested_delivery, str) else None\n+\n+    return ActionResult(\n+        ok=ok,\n+        action=name,\n+        message=message,\n+        meta=meta,\n+        verified=verified,\n+        effect=effect,\n+        escalation=escalation,\n+        path=path,\n+        degraded=degraded,\n+        delivery_mode=delivery_mode,\n+        code=code,\n+    )\n+\n+\n+\n # ---------------------------------------------------------------------------\n # Update checking\n # ---------------------------------------------------------------------------","@@ -1766,6 +1830,48 @@ def _gws_is_empty(out: Dict[str, Any]) -> bool:\n         )\n \n     # ── Pointer ────────────────────────────────────────────────────\n+    def _apply_delivery(\n+        self,\n+        action: str,\n+        args: Dict[str, Any],\n+        delivery_mode: Optional[str],\n+        bring_to_front: bool,\n+    ) -> Optional[ActionResult]:\n+        \"\"\"Attach delivery_mode to an input-action args dict.\n+\n+        Background is the default and never needs a flag. Foreground is only\n+        sent when the driver advertises support for it; on an older driver\n+        that lacks the capability we refuse with a structured\n+        ``foreground_unsupported`` result instead of silently downgrading to\n+        background (which would land the input somewhere the model didn't\n+        expect). Returns an ActionResult to short-circuit on refusal, or None\n+        to proceed. See NousResearch/hermes-agent#67052 phase B.\n+        \"\"\"\n+        if not delivery_mode or delivery_mode == \"background\":\n+            return None\n+        if delivery_mode != \"foreground\":\n+            return ActionResult(\n+                ok=False, action=action, code=\"bad_delivery_mode\",\n+                message=f\"unknown delivery_mode {delivery_mode!r} — use background|foreground.\",\n+            )\n+        # Foreground requested. Only send it if the driver understands it.\n+        if not self._session.supports_capability(\n+            \"input.delivery_mode\", tool=action\n+        ):\n+            return ActionResult(\n+                ok=False, action=action, code=\"foreground_unsupported\",\n+                delivery_mode=\"foreground\",\n+                message=(\n+                    \"This cua-driver build does not support foreground \"\n+                    \"delivery (no `input.delivery_mode` capability). Update \"\n+                    \"cua-driver to escalate to the foreground rung.\"\n+                ),\n+            )\n+        args[\"delivery_mode\"] = \"foreground\"\n+        if bring_to_front:\n+            args[\"bring_to_front\"] = True\n+        return None\n+\n     def click(\n         self,\n         *,","@@ -1775,6 +1881,8 @@ def click(\n         button: str = \"left\",\n         click_count: int = 1,\n         modifiers: Optional[List[str]] = None,\n+        delivery_mode: Optional[str] = None,\n+        bring_to_front: bool = False,\n     ) -> ActionResult:\n         pid = self._active_pid\n         if pid is None:","@@ -1815,6 +1923,9 @@ def click(\n         if modifiers:\n             args[\"modifier\"] = modifiers\n \n+        refusal = self._apply_delivery(tool, args, delivery_mode, bring_to_front)\n+        if refusal is not None:\n+            return refusal\n         return self._action(tool, args)\n \n     def drag(","@@ -1826,6 +1937,8 @@ def drag(\n         to_xy: Optional[Tuple[int, int]] = None,\n         button: str = \"left\",\n         modifiers: Optional[List[str]] = None,\n+        delivery_mode: Optional[str] = None,\n+        bring_to_front: bool = False,\n     ) -> ActionResult:\n         pid = self._active_pid\n         if pid is None:","@@ -1849,6 +1962,9 @@ def drag(\n         else:\n             return ActionResult(ok=False, action=\"drag\",\n                                 message=\"drag requires from_element/to_element or from_coordinate/to_coordinate.\")\n+        refusal = self._apply_delivery(\"drag\", args, delivery_mode, bring_to_front)\n+        if refusal is not None:\n+            return refusal\n         return self._action(\"drag\", args)\n \n     def scroll(","@@ -1860,6 +1976,8 @@ def scroll(\n         x: Optional[int] = None,\n         y: Optional[int] = None,\n         modifiers: Optional[List[str]] = None,\n+        delivery_mode: Optional[str] = None,\n+        bring_to_front: bool = False,\n     ) -> ActionResult:\n         pid = self._active_pid\n         if pid is None:","@@ -1889,18 +2007,27 @@ def scroll(\n                 args[\"x\"] = x\n                 args[\"y\"] = y\n             args[\"window_id\"] = self._active_window_id\n+        refusal = self._apply_delivery(\"scroll\", args, delivery_mode, bring_to_front)\n+        if refusal is not None:\n+            return refusal\n         return self._action(\"scroll\", args)\n \n     # ── Keyboard ───────────────────────────────────────────────────\n-    def type_text(self, text: str) -> ActionResult:\n+    def type_text(self, text: str, *, delivery_mode: Optional[str] = None,\n+                  bring_to_front: bool = False) -> ActionResult:\n         pid = self._active_pid\n         window_id = self._active_window_id\n         if pid is None or window_id is None:\n             return ActionResult(ok=False, action=\"type_text\",\n                                 message=\"No active window — call capture() first.\")\n-        return self._action(\"type_text\", {\"pid\": pid, \"window_id\": window_id, \"text\": text})\n-\n-    def key(self, keys: str) -> ActionResult:\n+        args: Dict[str, Any] = {\"pid\": pid, \"window_id\": window_id, \"text\": text}\n+        refusal = self._apply_delivery(\"type_text\", args, delivery_mode, bring_to_front)\n+        if refusal is not None:\n+            return refusal\n+        return self._action(\"type_text\", args)\n+\n+    def key(self, keys: str, *, delivery_mode: Optional[str] = None,\n+            bring_to_front: bool = False) -> ActionResult:\n         pid = self._active_pid\n         window_id = self._active_window_id\n         if pid is None or window_id is None:","@@ -1914,11 +2041,18 @@ def key(self, keys: str) -> ActionResult:\n \n         if modifiers:\n             # hotkey requires at least one modifier + one key.\n-            return self._action(\"hotkey\", {\"pid\": pid, \"window_id\": window_id,\n-                                           \"keys\": modifiers + [key_name]})\n+            args: Dict[str, Any] = {\"pid\": pid, \"window_id\": window_id,\n+                                    \"keys\": modifiers + [key_name]}\n+            refusal = self._apply_delivery(\"hotkey\", args, delivery_mode, bring_to_front)\n+            if refusal is not None:\n+                return refusal\n+            return self._action(\"hotkey\", args)\n         else:\n-            return self._action(\"press_key\", {\"pid\": pid, \"window_id\": window_id,\n-                                              \"key\": key_name})\n+            args = {\"pid\": pid, \"window_id\": window_id, \"key\": key_name}\n+            refusal = self._apply_delivery(\"press_key\", args, delivery_mode, bring_to_front)\n+            if refusal is not None:\n+                return refusal\n+            return self._action(\"press_key\", args)\n \n     # ── Value setter ────────────────────────────────────────────────\n     def set_value(self, value: str, element: Optional[int] = None) -> ActionResult:","@@ -2325,11 +2459,22 @@ def _action(self, name: str, args: Dict[str, Any]) -> ActionResult:\n             logger.exception(\"cua-driver %s call failed\", name)\n             return ActionResult(ok=False, action=name, message=f\"cua-driver error: {e}\")\n         ok = not out[\"isError\"]\n-        message = \"\"\n         data = out[\"data\"]\n+        structured = out.get(\"structuredContent\") or {}\n+        message = \"\"\n         if isinstance(data, dict):\n             message = str(data.get(\"message\", \"\"))\n         elif isinstance(data, str):\n             message = data\n-        return ActionResult(ok=ok, action=name, message=message,\n-                            meta=data if isinstance(data, dict) else {})\n+        if not message and isinstance(structured, dict):\n+            message = str(structured.get(\"message\", \"\"))\n+        # Merge data + structuredContent into meta for debugging, structured\n+        # winning on key overlap (it is the canonical verdict surface).\n+        meta: Dict[str, Any] = {}\n+        if isinstance(data, dict):\n+            meta.update(data)\n+        if isinstance(structured, dict):\n+            meta.update(structured)\n+        return _action_result_from(name, ok, message, meta, structured,\n+                                   requested_delivery=args.get(\"delivery_mode\"))\n+"]},{"file":"tools/computer_use/schema.py","hunks":["@@ -218,6 +218,35 @@\n                     \"matching the background co-work model.\"\n                 ),\n             },\n+            # ── delivery (verify → escalate ladder) ────────────────\n+            \"delivery_mode\": {\n+                \"type\": \"string\",\n+                \"enum\": [\"background\", \"foreground\"],\n+                \"description\": (\n+                    \"How input is delivered, for the input actions (click, \"\n+                    \"double_click, right_click, drag, scroll, type, key). \"\n+                    \"`background` (DEFAULT) routes input to the target without \"\n+                    \"raising it or stealing focus — the co-work model. \"\n+                    \"`foreground` briefly fronts the window, acts, then \"\n+                    \"restores the prior frontmost app. Only escalate to \"\n+                    \"`foreground` when a background attempt did NOT land — i.e. \"\n+                    \"a prior result had `effect: 'suspected_noop'`, \"\n+                    \"`code: 'background_unavailable'`, or \"\n+                    \"`escalation.recommended: 'foreground'`. Do not predict it \"\n+                    \"from the app being Electron/Chromium; react to the \"\n+                    \"returned signal. Foreground is a visible focus change and \"\n+                    \"needs its own approval.\"\n+                ),\n+            },\n+            \"bring_to_front\": {\n+                \"type\": \"boolean\",\n+                \"description\": (\n+                    \"Optional, pairs with delivery_mode='foreground'. Keep the \"\n+                    \"target fronted after the action instead of restoring the \"\n+                    \"previous app, to avoid a per-call flash across a short \"\n+                    \"sequence of foreground actions. Default false.\"\n+                ),\n+            },\n             # ── return shape ───────────────────────────────────────\n             \"capture_after\": {\n                 \"type\": \"boolean\","]},{"file":"tools/computer_use/tool.py","hunks":["@@ -139,9 +139,16 @@ def _is_blocked_type(text: str) -> Optional[str]:\n # Per-process cached backend; lazily instantiated on first call.\n _backend_lock = threading.Lock()\n _backend: Optional[ComputerUseBackend] = None\n-# Session-scoped approval state.\n-_session_auto_approve = False\n-_always_allow: set = set()  # action names the user unlocked for the session\n+# Approval state, scoped per conversation/run (keyed by session_id) so a\n+# gateway serving concurrent sessions can't leak one run's \"always approve\"\n+# unlock into another. Falls back to a shared \"\" bucket for callers that\n+# don't pass a session_id (e.g. the classic single-run CLI). Values:\n+#   _session_auto_approve[sid] -> bool   (\"always_approve everything\")\n+#   _always_allow[sid]         -> set of (action, delivery_mode) scope keys\n+# See NousResearch/hermes-agent#67052 gap 4.\n+_approval_lock = threading.Lock()\n+_session_auto_approve: Dict[str, bool] = {}\n+_always_allow: Dict[str, set] = {}\n \n \n def _get_backend() -> ComputerUseBackend:","@@ -169,8 +176,8 @@ def _get_backend() -> ComputerUseBackend:\n \n \n def reset_backend_for_tests() -> None:  # pragma: no cover\n-    \"\"\"Test helper — tear down the cached backend.\"\"\"\n-    global _backend, _session_auto_approve, _always_allow\n+    \"\"\"Test helper — tear down the cached backend and per-session state.\"\"\"\n+    global _backend\n     with _backend_lock:\n         if _backend is not None:\n             try:","@@ -178,8 +185,9 @@ def reset_backend_for_tests() -> None:  # pragma: no cover\n             except Exception:\n                 pass\n         _backend = None\n-    _session_auto_approve = False\n-    _always_allow = set()\n+    with _approval_lock:\n+        _session_auto_approve.clear()\n+        _always_allow.clear()\n \n \n class _NoopBackend(ComputerUseBackend):  # pragma: no cover","@@ -219,12 +227,12 @@ def scroll(self, **kw) -> ActionResult:\n         self.calls.append((\"scroll\", kw))\n         return ActionResult(ok=True, action=\"scroll\")\n \n-    def type_text(self, text: str) -> ActionResult:\n-        self.calls.append((\"type\", {\"text\": text}))\n+    def type_text(self, text: str, **kw) -> ActionResult:\n+        self.calls.append((\"type\", {\"text\": text, **kw}))\n         return ActionResult(ok=True, action=\"type\")\n \n-    def key(self, keys: str) -> ActionResult:\n-        self.calls.append((\"key\", {\"keys\": keys}))\n+    def key(self, keys: str, **kw) -> ActionResult:\n+        self.calls.append((\"key\", {\"keys\": keys, **kw}))\n         return ActionResult(ok=True, action=\"key\")\n \n     def list_apps(self) -> List[Dict[str, Any]]:","@@ -257,6 +265,8 @@ def handle_computer_use(args: Dict[str, Any], **kwargs) -> Any:\n     action = (args.get(\"action\") or \"\").strip().lower()\n     if not action:\n         return json.dumps({\"error\": \"missing `action`\"})\n+    # Per-run key for approval-state isolation across concurrent sessions.\n+    session_id = str(kwargs.get(\"session_id\") or \"\")\n \n     # Safety: validate actions before approval prompt.\n     if action == \"type\":","@@ -280,7 +290,7 @@ def handle_computer_use(args: Dict[str, Any], **kwargs) -> Any:\n \n     # Approval gate (destructive actions only).\n     if action in _DESTRUCTIVE_ACTIONS:\n-        err = _request_approval(action, args)\n+        err = _request_approval(action, args, session_id)\n         if err is not None:\n             return err\n ","@@ -301,13 +311,26 @@ def handle_computer_use(args: Dict[str, Any], **kwargs) -> Any:\n         return json.dumps({\"error\": f\"{action} failed: {e}\"})\n \n \n-def _request_approval(action: str, args: Dict[str, Any]) -> Optional[str]:\n-    \"\"\"Return None if approved, or a JSON error string if denied.\"\"\"\n-    global _session_auto_approve, _always_allow\n-    if _session_auto_approve:\n-        return None\n-    if action in _always_allow:\n-        return None\n+def _request_approval(action: str, args: Dict[str, Any],\n+                      session_id: str = \"\") -> Optional[str]:\n+    \"\"\"Return None if approved, or a JSON error string if denied.\n+\n+    Approval is scoped by (action, delivery_mode) AND by session_id.\n+    Foreground delivery is a visible focus change, so a prior background\n+    approval — even ``approve_session`` on the same action — must NOT\n+    silently authorize it (NousResearch/hermes-agent#67052).\n+    ``always_approve`` (the blanket \"auto-approve everything\" unlock) still\n+    covers foreground, since the user explicitly opted into unattended\n+    operation. State is keyed on session_id so concurrent runs don't leak\n+    unlocks into one another.\n+    \"\"\"\n+    is_foreground = args.get(\"delivery_mode\") == \"foreground\"\n+    scope_key = (action, \"foreground\" if is_foreground else \"background\")\n+    with _approval_lock:\n+        if _session_auto_approve.get(session_id):\n+            return None\n+        if scope_key in _always_allow.get(session_id, set()):\n+            return None\n     cb = _approval_callback\n     if cb is None:\n         # No CLI approval wired — default allow. Gateway approval is handled","@@ -322,35 +345,38 @@ def _request_approval(action: str, args: Dict[str, Any]) -> Optional[str]:\n     if verdict == \"approve_once\":\n         return None\n     if verdict == \"approve_session\" or verdict == \"always_approve\":\n-        _always_allow.add(action)\n-        if verdict == \"always_approve\":\n-            _session_auto_approve = True\n+        with _approval_lock:\n+            _always_allow.setdefault(session_id, set()).add(scope_key)\n+            if verdict == \"always_approve\":\n+                _session_auto_approve[session_id] = True\n         return None\n     return json.dumps({\"error\": \"denied by user\", \"action\": action})\n \n \n def _summarize_action(action: str, args: Dict[str, Any]) -> str:\n+    fg = \" [FOREGROUND — briefly raises the window / changes focus]\" \\\n+        if args.get(\"delivery_mode\") == \"foreground\" else \"\"\n     if action in {\"click\", \"double_click\", \"right_click\", \"middle_click\"}:\n         if args.get(\"element\") is not None:\n-            return f\"{action} element #{args['element']}\"\n+            return f\"{action} element #{args['element']}{fg}\"\n         coord = args.get(\"coordinate\")\n         if coord:\n-            return f\"{action} at {tuple(coord)}\"\n-        return action\n+            return f\"{action} at {tuple(coord)}{fg}\"\n+        return action + fg\n     if action == \"drag\":\n         src = args.get(\"from_element\") or args.get(\"from_coordinate\")\n         dst = args.get(\"to_element\") or args.get(\"to_coordinate\")\n-        return f\"drag {src} → {dst}\"\n+        return f\"drag {src} → {dst}{fg}\"\n     if action == \"scroll\":\n-        return f\"scroll {args.get('direction', '?')} x{args.get('amount', 3)}\"\n+        return f\"scroll {args.get('direction', '?')} x{args.get('amount', 3)}{fg}\"\n     if action == \"type\":\n         text = args.get(\"text\", \"\")\n-        return f\"type {text[:60]!r}\" + (\"...\" if len(text) > 60 else \"\")\n+        return f\"type {text[:60]!r}\" + (\"...\" if len(text) > 60 else \"\") + fg\n     if action == \"key\":\n-        return f\"key {args.get('keys', '')!r}\"\n+        return f\"key {args.get('keys', '')!r}{fg}\"\n     if action == \"focus_app\":\n         return f\"focus {args.get('app', '')!r}\" + (\" (raise)\" if args.get(\"raise_window\") else \"\")\n-    return action\n+    return action + fg\n \n \n def _dispatch(backend: ComputerUseBackend, action: str, args: Dict[str, Any]) -> Any:","@@ -389,6 +415,11 @@ def _dispatch(backend: ComputerUseBackend, action: str, args: Dict[str, Any]) ->\n         res = backend.focus_app(app, raise_window=bool(args.get(\"raise_window\")))\n         return _maybe_follow_capture(backend, res, capture_after)\n \n+    # delivery_mode / bring_to_front thread through every input action so the\n+    # model can escalate background → foreground per cua-driver's ladder.\n+    delivery_mode = args.get(\"delivery_mode\")\n+    bring_to_front = bool(args.get(\"bring_to_front\"))\n+\n     if action in {\"click\", \"double_click\", \"right_click\", \"middle_click\"}:\n         button = args.get(\"button\")\n         click_count = 1","@@ -407,6 +438,7 @@ def _dispatch(backend: ComputerUseBackend, action: str, args: Dict[str, Any]) ->\n             element=element if element is not None else None,\n             x=x, y=y, button=button or \"left\", click_count=click_count,\n             modifiers=args.get(\"modifiers\"),\n+            delivery_mode=delivery_mode, bring_to_front=bring_to_front,\n         )\n         return _maybe_follow_capture(backend, res, capture_after)\n ","@@ -424,6 +456,7 @@ def _dispatch(backend: ComputerUseBackend, action: str, args: Dict[str, Any]) ->\n             to_xy=tuple(args[\"to_coordinate\"]) if args.get(\"to_coordinate\") else None,\n             button=args.get(\"button\", \"left\"),\n             modifiers=args.get(\"modifiers\"),\n+            delivery_mode=delivery_mode, bring_to_front=bring_to_front,\n         )\n         return _maybe_follow_capture(backend, res, capture_after)\n ","@@ -436,15 +469,18 @@ def _dispatch(backend: ComputerUseBackend, action: str, args: Dict[str, Any]) ->\n             x=coord[0] if coord and coord[0] is not None else None,\n             y=coord[1] if coord and coord[1] is not None else None,\n             modifiers=args.get(\"modifiers\"),\n+            delivery_mode=delivery_mode, bring_to_front=bring_to_front,\n         )\n         return _maybe_follow_capture(backend, res, capture_after)\n \n     if action == \"type\":\n-        res = backend.type_text(args.get(\"text\", \"\"))\n+        res = backend.type_text(args.get(\"text\", \"\"),\n+                                delivery_mode=delivery_mode, bring_to_front=bring_to_front)\n         return _maybe_follow_capture(backend, res, capture_after)\n \n     if action == \"key\":\n-        res = backend.key(args.get(\"keys\", \"\"))\n+        res = backend.key(args.get(\"keys\", \"\"),\n+                          delivery_mode=delivery_mode, bring_to_front=bring_to_front)\n         return _maybe_follow_capture(backend, res, capture_after)\n \n     if action == \"set_value\":","@@ -465,6 +501,24 @@ def _text_response(res: ActionResult) -> str:\n     payload: Dict[str, Any] = {\"ok\": res.ok, \"action\": res.action}\n     if res.message:\n         payload[\"message\"] = res.message\n+    # Surface cua-driver's structured verdict additively so the model can\n+    # follow the verify → escalate ladder. Only include fields the driver\n+    # actually returned (None = old driver / not carried). ok is transport\n+    # success; effect/escalation are the semantic verdict.\n+    if res.verified is not None:\n+        payload[\"verified\"] = res.verified\n+    if res.effect is not None:\n+        payload[\"effect\"] = res.effect\n+    if res.escalation is not None:\n+        payload[\"escalation\"] = res.escalation\n+    if res.path is not None:\n+        payload[\"path\"] = res.path\n+    if res.degraded is not None:\n+        payload[\"degraded\"] = res.degraded\n+    if res.delivery_mode is not None:\n+        payload[\"delivery_mode\"] = res.delivery_mode\n+    if res.code is not None:\n+        payload[\"code\"] = res.code\n     if res.meta:\n         payload[\"meta\"] = res.meta\n     return json.dumps(payload)\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}