app
DEPRECATED: Bridge SDK (moved into Adom Bridge)
Public Made by Adomby adom
Bridge SDK guide: bridge.json schema, kind:python/node/exe, hello-python + hello-rust reference templates, packaging + lifecycle commands.
← Commit history
SDK: caller provenance (X-Adom-Caller-* headers), AD 1.9.180
3 files changed
+396
README.md+132@@ -82,6 +82,138 @@ curl -s -X POST "$ADOM_DIRECT_API_URL/command" -H 'Content-Type: application/jso -d '{"command":"notify_user","args":{"title":"Heads up","body":"…"}}' ``` +### Notify the user AND get their answer back (`notify_user` — two separate axes)++Your bridge can prompt the user with a native toast and read their reply — the same `notify_user` verb the AI uses. It has **two independent axes; don't conflate them** (full ref: the `adom-desktop-notify` skill / `skills/NOTIFY.md`):++- **Axis 1 — get the answer back.** There is no server-push, so either **`wait:true`** (the `notify_user` call blocks and returns the user's choice inline — best for a one-shot prompt) or fire-and-continue then poll **`notify_events {sinceSeq}`** (drains every click) / **`notify_response {id}`**. The click is recorded durably, so it survives even on a headless box.+- **Axis 2 — reveal a window (`focus`).** A LOCAL desktop action, not a reply. `focus:{hwnd|cacheKey|titleContains|app|host}` raises a window on the user's screen when they click. **`focus:{app:"<your-exe>"}`** points the click at a window YOUR bridge drives; **`focus:{host:true}`** points it at the app AD is embedded under (Hydrogen Desktop). Pass `focus` whenever the toast is *about* a window, so the click lands there instead of opening AD.++```bash+# Ask, and get the answer back inline:+curl -s -X POST "$ADOM_DIRECT_API_URL/command" -H "X-Adom-Bridge-Token: $ADOM_BRIDGE_TOKEN" \+ -d '{"command":"notify_user","args":{"title":"Overwrite board?","scenario":"reminder",+ "buttons":[{"label":"Overwrite"},{"label":"Cancel"}],"wait":true,"timeoutSec":90}}'+# -> { "pending": false, "action": "Overwrite" }+```++The response always carries `_capabilities` / `_couldHaveDoneBetter` when a call left something on the table — read them; they teach the verb from use.++---++## Caller provenance: WHO asked (v1.9.180)++A user runs roughly **20 AI threads against one desktop**. Until v1.9.180 AD relayed every command indistinguishably, so a bridge received **no caller identity at all**. Three consequences, all real:++- You could not **arbitrate concurrent agents** — two threads grabbing the same session looked identical to you.+- No bridge log could **attribute a verb** to the thread that issued it, so interference was impossible to reconstruct afterwards.+- Any agent's `bridge_kill` **destroyed every other agent's work**, silently, with no way for anyone to see who did it.++Filed as wiki issue `adom/adom-desktop#34` by AdityaAngajala, with the pup half at `adom/adom-desktop-puppeteer-bridge#22`.++### What you now receive, for free++AD stamps three headers on **every relayed bridge request** — all four request paths (browser, kicad, fusion, and the dynamic third-party dispatch):++| Header | What it carries |+|---|---|+| `X-Adom-Caller-Thread` | The AI thread / conversation tab name, self-reported by the caller |+| `X-Adom-Caller-Container` | The container it runs in |+| `X-Adom-Caller-Reason` | The per-call justification, when the caller supplied one (control chars stripped, truncated to 300 chars) |++**You do nothing to receive these.** No manifest field, no signature change, no opt-in. Reading them is the opt-in part.++### Trust rule — read this before you use them for anything++These values are **SELF-ASSERTED**, exactly like `reason`. Use them for **attribution, logging, UX and arbitration hints**. **NEVER for authorization, and never as a security boundary.**++AD's own security model is transport-based (the `adom-desktop-permissions` skill has the full model): a caller is trusted because of *how it reached AD*, not because of what it *says about itself*. A header a caller supplies about itself cannot gate anything — if you write `if (thread === "admin-tab") allowDangerousThing()`, you have written a hole, not a check.++One asymmetry worth knowing: the container name in `X-Adom-Caller-Container` is the one the **caller claimed**. AD separately knows a **verified** container (the authenticated relay connection name) and shows the user **both** when they disagree. You only get the claimed one, so treat it as a label, not an identity.++### What to do with it++**1. Log it.** Put the thread in your per-verb log line. This is the single highest-value use, and it is three lines of code:++```javascript+// node — read once, at the top of your request handler+const caller = {+ thread: req.headers['x-adom-caller-thread'] || 'unidentified',+ container: req.headers['x-adom-caller-container'] || 'unknown',+ reason: req.headers['x-adom-caller-reason'] || null,+};+console.log(`[verb] ${verb} session=${sessionId} caller="${caller.thread}"`);+```++```python+# python — same idea; header lookup is case-insensitive on BaseHTTPRequestHandler+caller_thread = self.headers.get('X-Adom-Caller-Thread', 'unidentified')+caller_container = self.headers.get('X-Adom-Caller-Container', 'unknown')+caller_reason = self.headers.get('X-Adom-Caller-Reason')+log.info('[verb] %s session=%s caller="%s"', verb, session_id, caller_thread)+```++That one field is what makes an interference report reconstructable: `[verb] browser_eval session=rev caller="chip-fetcher tab 3"` answers "who moved my page?" instantly, where a bare `[verb] browser_eval session=rev` never can.++**2. Arbitrate.** If your bridge has **sessions or resources an agent can own** — pup sessions are the motivating case — record the owning thread at creation time and check it on every later use:++```javascript+// on create+sessions.set(id, { ...state, ownerThread: caller.thread });++// on use by a DIFFERENT thread+const s = sessions.get(id);+if (s.ownerThread && s.ownerThread !== caller.thread) {+ // warn loudly by default; refuse only if this is destructive+ result._warning =+ `session ${id} is owned by "${s.ownerThread}" — you are "${caller.thread}". ` ++ `Two agents driving one session will interfere. Coordinate, or open your own session.`;+}+```++The recommended posture:++- **Warn loudly by default.** A second agent reading or screenshotting a session is usually fine, and hard-blocking it turns a shared machine into a locked one.+- **Refuse only for destructive operations** — closing, resetting, navigating away, deleting.+- **Always name the current owner in the message.** The whole point is that the second agent can now go coordinate with a thread it can identify. An error that says "session busy" without a name is barely better than no header at all.++**3. Label the UI.** If your bridge drives a visible window, put the owning thread where the **user** can see it — window title, taskbar tooltip, or jump-list name — via `desktop_set_window_identity` (and `desktop_taskbar`'s `thumbnailTooltip` for uncapped hover text). The user is the one with 20 tabs open; showing which tab owns which window is the fix for their side of the problem. Full guide: the `adom-desktop-window-identity` skill, and the "Branding + driving a window you borrowed" section immediately below.++**4. Report it back.** Include the owning thread in your **status / list verbs**, so an agent can *discover* it is not alone before it stomps on something:++```jsonc+// <prefix>_list_sessions+{ "sessions": [ { "id": "rev", "ownerThread": "chip-fetcher tab 3", "url": "…" } ],+ "_hint": "A session with an ownerThread other than yours is being driven by another AI thread. Open your own session rather than sharing one." }+```++### Notifications already carry it++`notify_user` derives its toast attribution line from the caller automatically, so a toast your bridge sends **on an agent's behalf already names the thread** — you do not have to build that string. Pass an explicit `attribution` string only when you want to override it.++### Where it comes from (the caller side)++Useful to understand, though none of it is your responsibility:++```bash+# The AI exports this ONCE per session, in its container:+export ADOM_AI_THREAD="chip-fetcher tab 3"+export ADOM_CONTAINER_NAME="galliaApril" # optional+```++The `adom-desktop` CLI then attaches a `caller` block to **every** verb automatically. A relay call with **no `aiThread` is REFUSED by AD** with `errorCode: caller_identity_required` before it ever reaches a bridge — the refusal names exactly what is missing and how to set it permanently, so callers fix it once and move on.++**So by the time a request reaches you, identity is normally present.** The exception is **loopback callers** — HD, another bridge, or the local CLI — which are trusted by transport and are not required to identify. Code for the absent case (`|| 'unidentified'`), never assume the header exists.++### Self-audit++- [ ] Does every verb log line include the **caller thread**?+- [ ] Do your **session-creating** verbs record the owning thread on the session?+- [ ] Do your **session-using** verbs warn (or, if destructive, refuse) when a different thread arrives — **naming the current owner**?+- [ ] Does any **window you drive** show the owning thread to the user (title / tooltip / jump-list)?+- [ ] Do your **status / list verbs** report `ownerThread` so an agent can discover it is not alone?+- [ ] Have you confirmed you use these headers **nowhere** as an authorization check?+ --- ## Branding + driving a window you borrowed (window identity + taskbar, v1.9.106–1.9.153)
SKILL.md+132@@ -82,6 +82,138 @@ curl -s -X POST "$ADOM_DIRECT_API_URL/command" -H 'Content-Type: application/jso -d '{"command":"notify_user","args":{"title":"Heads up","body":"…"}}' ``` +### Notify the user AND get their answer back (`notify_user` — two separate axes)++Your bridge can prompt the user with a native toast and read their reply — the same `notify_user` verb the AI uses. It has **two independent axes; don't conflate them** (full ref: the `adom-desktop-notify` skill / `skills/NOTIFY.md`):++- **Axis 1 — get the answer back.** There is no server-push, so either **`wait:true`** (the `notify_user` call blocks and returns the user's choice inline — best for a one-shot prompt) or fire-and-continue then poll **`notify_events {sinceSeq}`** (drains every click) / **`notify_response {id}`**. The click is recorded durably, so it survives even on a headless box.+- **Axis 2 — reveal a window (`focus`).** A LOCAL desktop action, not a reply. `focus:{hwnd|cacheKey|titleContains|app|host}` raises a window on the user's screen when they click. **`focus:{app:"<your-exe>"}`** points the click at a window YOUR bridge drives; **`focus:{host:true}`** points it at the app AD is embedded under (Hydrogen Desktop). Pass `focus` whenever the toast is *about* a window, so the click lands there instead of opening AD.++```bash+# Ask, and get the answer back inline:+curl -s -X POST "$ADOM_DIRECT_API_URL/command" -H "X-Adom-Bridge-Token: $ADOM_BRIDGE_TOKEN" \+ -d '{"command":"notify_user","args":{"title":"Overwrite board?","scenario":"reminder",+ "buttons":[{"label":"Overwrite"},{"label":"Cancel"}],"wait":true,"timeoutSec":90}}'+# -> { "pending": false, "action": "Overwrite" }+```++The response always carries `_capabilities` / `_couldHaveDoneBetter` when a call left something on the table — read them; they teach the verb from use.++---++## Caller provenance: WHO asked (v1.9.180)++A user runs roughly **20 AI threads against one desktop**. Until v1.9.180 AD relayed every command indistinguishably, so a bridge received **no caller identity at all**. Three consequences, all real:++- You could not **arbitrate concurrent agents** — two threads grabbing the same session looked identical to you.+- No bridge log could **attribute a verb** to the thread that issued it, so interference was impossible to reconstruct afterwards.+- Any agent's `bridge_kill` **destroyed every other agent's work**, silently, with no way for anyone to see who did it.++Filed as wiki issue `adom/adom-desktop#34` by AdityaAngajala, with the pup half at `adom/adom-desktop-puppeteer-bridge#22`.++### What you now receive, for free++AD stamps three headers on **every relayed bridge request** — all four request paths (browser, kicad, fusion, and the dynamic third-party dispatch):++| Header | What it carries |+|---|---|+| `X-Adom-Caller-Thread` | The AI thread / conversation tab name, self-reported by the caller |+| `X-Adom-Caller-Container` | The container it runs in |+| `X-Adom-Caller-Reason` | The per-call justification, when the caller supplied one (control chars stripped, truncated to 300 chars) |++**You do nothing to receive these.** No manifest field, no signature change, no opt-in. Reading them is the opt-in part.++### Trust rule — read this before you use them for anything++These values are **SELF-ASSERTED**, exactly like `reason`. Use them for **attribution, logging, UX and arbitration hints**. **NEVER for authorization, and never as a security boundary.**++AD's own security model is transport-based (the `adom-desktop-permissions` skill has the full model): a caller is trusted because of *how it reached AD*, not because of what it *says about itself*. A header a caller supplies about itself cannot gate anything — if you write `if (thread === "admin-tab") allowDangerousThing()`, you have written a hole, not a check.++One asymmetry worth knowing: the container name in `X-Adom-Caller-Container` is the one the **caller claimed**. AD separately knows a **verified** container (the authenticated relay connection name) and shows the user **both** when they disagree. You only get the claimed one, so treat it as a label, not an identity.++### What to do with it++**1. Log it.** Put the thread in your per-verb log line. This is the single highest-value use, and it is three lines of code:++```javascript+// node — read once, at the top of your request handler+const caller = {+ thread: req.headers['x-adom-caller-thread'] || 'unidentified',+ container: req.headers['x-adom-caller-container'] || 'unknown',+ reason: req.headers['x-adom-caller-reason'] || null,+};+console.log(`[verb] ${verb} session=${sessionId} caller="${caller.thread}"`);+```++```python+# python — same idea; header lookup is case-insensitive on BaseHTTPRequestHandler+caller_thread = self.headers.get('X-Adom-Caller-Thread', 'unidentified')+caller_container = self.headers.get('X-Adom-Caller-Container', 'unknown')+caller_reason = self.headers.get('X-Adom-Caller-Reason')+log.info('[verb] %s session=%s caller="%s"', verb, session_id, caller_thread)+```++That one field is what makes an interference report reconstructable: `[verb] browser_eval session=rev caller="chip-fetcher tab 3"` answers "who moved my page?" instantly, where a bare `[verb] browser_eval session=rev` never can.++**2. Arbitrate.** If your bridge has **sessions or resources an agent can own** — pup sessions are the motivating case — record the owning thread at creation time and check it on every later use:++```javascript+// on create+sessions.set(id, { ...state, ownerThread: caller.thread });++// on use by a DIFFERENT thread+const s = sessions.get(id);+if (s.ownerThread && s.ownerThread !== caller.thread) {+ // warn loudly by default; refuse only if this is destructive+ result._warning =+ `session ${id} is owned by "${s.ownerThread}" — you are "${caller.thread}". ` ++ `Two agents driving one session will interfere. Coordinate, or open your own session.`;+}+```++The recommended posture:++- **Warn loudly by default.** A second agent reading or screenshotting a session is usually fine, and hard-blocking it turns a shared machine into a locked one.+- **Refuse only for destructive operations** — closing, resetting, navigating away, deleting.+- **Always name the current owner in the message.** The whole point is that the second agent can now go coordinate with a thread it can identify. An error that says "session busy" without a name is barely better than no header at all.++**3. Label the UI.** If your bridge drives a visible window, put the owning thread where the **user** can see it — window title, taskbar tooltip, or jump-list name — via `desktop_set_window_identity` (and `desktop_taskbar`'s `thumbnailTooltip` for uncapped hover text). The user is the one with 20 tabs open; showing which tab owns which window is the fix for their side of the problem. Full guide: the `adom-desktop-window-identity` skill, and the "Branding + driving a window you borrowed" section immediately below.++**4. Report it back.** Include the owning thread in your **status / list verbs**, so an agent can *discover* it is not alone before it stomps on something:++```jsonc+// <prefix>_list_sessions+{ "sessions": [ { "id": "rev", "ownerThread": "chip-fetcher tab 3", "url": "…" } ],+ "_hint": "A session with an ownerThread other than yours is being driven by another AI thread. Open your own session rather than sharing one." }+```++### Notifications already carry it++`notify_user` derives its toast attribution line from the caller automatically, so a toast your bridge sends **on an agent's behalf already names the thread** — you do not have to build that string. Pass an explicit `attribution` string only when you want to override it.++### Where it comes from (the caller side)++Useful to understand, though none of it is your responsibility:++```bash+# The AI exports this ONCE per session, in its container:+export ADOM_AI_THREAD="chip-fetcher tab 3"+export ADOM_CONTAINER_NAME="galliaApril" # optional+```++The `adom-desktop` CLI then attaches a `caller` block to **every** verb automatically. A relay call with **no `aiThread` is REFUSED by AD** with `errorCode: caller_identity_required` before it ever reaches a bridge — the refusal names exactly what is missing and how to set it permanently, so callers fix it once and move on.++**So by the time a request reaches you, identity is normally present.** The exception is **loopback callers** — HD, another bridge, or the local CLI — which are trusted by transport and are not required to identify. Code for the absent case (`|| 'unidentified'`), never assume the header exists.++### Self-audit++- [ ] Does every verb log line include the **caller thread**?+- [ ] Do your **session-creating** verbs record the owning thread on the session?+- [ ] Do your **session-using** verbs warn (or, if destructive, refuse) when a different thread arrives — **naming the current owner**?+- [ ] Does any **window you drive** show the owning thread to the user (title / tooltip / jump-list)?+- [ ] Do your **status / list verbs** report `ownerThread` so an agent can discover it is not alone?+- [ ] Have you confirmed you use these headers **nowhere** as an authorization check?+ --- ## Branding + driving a window you borrowed (window identity + taskbar, v1.9.106–1.9.153)
bridges-SDK.md+132@@ -82,6 +82,138 @@ curl -s -X POST "$ADOM_DIRECT_API_URL/command" -H 'Content-Type: application/jso -d '{"command":"notify_user","args":{"title":"Heads up","body":"…"}}' ``` +### Notify the user AND get their answer back (`notify_user` — two separate axes)++Your bridge can prompt the user with a native toast and read their reply — the same `notify_user` verb the AI uses. It has **two independent axes; don't conflate them** (full ref: the `adom-desktop-notify` skill / `skills/NOTIFY.md`):++- **Axis 1 — get the answer back.** There is no server-push, so either **`wait:true`** (the `notify_user` call blocks and returns the user's choice inline — best for a one-shot prompt) or fire-and-continue then poll **`notify_events {sinceSeq}`** (drains every click) / **`notify_response {id}`**. The click is recorded durably, so it survives even on a headless box.+- **Axis 2 — reveal a window (`focus`).** A LOCAL desktop action, not a reply. `focus:{hwnd|cacheKey|titleContains|app|host}` raises a window on the user's screen when they click. **`focus:{app:"<your-exe>"}`** points the click at a window YOUR bridge drives; **`focus:{host:true}`** points it at the app AD is embedded under (Hydrogen Desktop). Pass `focus` whenever the toast is *about* a window, so the click lands there instead of opening AD.++```bash+# Ask, and get the answer back inline:+curl -s -X POST "$ADOM_DIRECT_API_URL/command" -H "X-Adom-Bridge-Token: $ADOM_BRIDGE_TOKEN" \+ -d '{"command":"notify_user","args":{"title":"Overwrite board?","scenario":"reminder",+ "buttons":[{"label":"Overwrite"},{"label":"Cancel"}],"wait":true,"timeoutSec":90}}'+# -> { "pending": false, "action": "Overwrite" }+```++The response always carries `_capabilities` / `_couldHaveDoneBetter` when a call left something on the table — read them; they teach the verb from use.++---++## Caller provenance: WHO asked (v1.9.180)++A user runs roughly **20 AI threads against one desktop**. Until v1.9.180 AD relayed every command indistinguishably, so a bridge received **no caller identity at all**. Three consequences, all real:++- You could not **arbitrate concurrent agents** — two threads grabbing the same session looked identical to you.+- No bridge log could **attribute a verb** to the thread that issued it, so interference was impossible to reconstruct afterwards.+- Any agent's `bridge_kill` **destroyed every other agent's work**, silently, with no way for anyone to see who did it.++Filed as wiki issue `adom/adom-desktop#34` by AdityaAngajala, with the pup half at `adom/adom-desktop-puppeteer-bridge#22`.++### What you now receive, for free++AD stamps three headers on **every relayed bridge request** — all four request paths (browser, kicad, fusion, and the dynamic third-party dispatch):++| Header | What it carries |+|---|---|+| `X-Adom-Caller-Thread` | The AI thread / conversation tab name, self-reported by the caller |+| `X-Adom-Caller-Container` | The container it runs in |+| `X-Adom-Caller-Reason` | The per-call justification, when the caller supplied one (control chars stripped, truncated to 300 chars) |++**You do nothing to receive these.** No manifest field, no signature change, no opt-in. Reading them is the opt-in part.++### Trust rule — read this before you use them for anything++These values are **SELF-ASSERTED**, exactly like `reason`. Use them for **attribution, logging, UX and arbitration hints**. **NEVER for authorization, and never as a security boundary.**++AD's own security model is transport-based (the `adom-desktop-permissions` skill has the full model): a caller is trusted because of *how it reached AD*, not because of what it *says about itself*. A header a caller supplies about itself cannot gate anything — if you write `if (thread === "admin-tab") allowDangerousThing()`, you have written a hole, not a check.++One asymmetry worth knowing: the container name in `X-Adom-Caller-Container` is the one the **caller claimed**. AD separately knows a **verified** container (the authenticated relay connection name) and shows the user **both** when they disagree. You only get the claimed one, so treat it as a label, not an identity.++### What to do with it++**1. Log it.** Put the thread in your per-verb log line. This is the single highest-value use, and it is three lines of code:++```javascript+// node — read once, at the top of your request handler+const caller = {+ thread: req.headers['x-adom-caller-thread'] || 'unidentified',+ container: req.headers['x-adom-caller-container'] || 'unknown',+ reason: req.headers['x-adom-caller-reason'] || null,+};+console.log(`[verb] ${verb} session=${sessionId} caller="${caller.thread}"`);+```++```python+# python — same idea; header lookup is case-insensitive on BaseHTTPRequestHandler+caller_thread = self.headers.get('X-Adom-Caller-Thread', 'unidentified')+caller_container = self.headers.get('X-Adom-Caller-Container', 'unknown')+caller_reason = self.headers.get('X-Adom-Caller-Reason')+log.info('[verb] %s session=%s caller="%s"', verb, session_id, caller_thread)+```++That one field is what makes an interference report reconstructable: `[verb] browser_eval session=rev caller="chip-fetcher tab 3"` answers "who moved my page?" instantly, where a bare `[verb] browser_eval session=rev` never can.++**2. Arbitrate.** If your bridge has **sessions or resources an agent can own** — pup sessions are the motivating case — record the owning thread at creation time and check it on every later use:++```javascript+// on create+sessions.set(id, { ...state, ownerThread: caller.thread });++// on use by a DIFFERENT thread+const s = sessions.get(id);+if (s.ownerThread && s.ownerThread !== caller.thread) {+ // warn loudly by default; refuse only if this is destructive+ result._warning =+ `session ${id} is owned by "${s.ownerThread}" — you are "${caller.thread}". ` ++ `Two agents driving one session will interfere. Coordinate, or open your own session.`;+}+```++The recommended posture:++- **Warn loudly by default.** A second agent reading or screenshotting a session is usually fine, and hard-blocking it turns a shared machine into a locked one.+- **Refuse only for destructive operations** — closing, resetting, navigating away, deleting.+- **Always name the current owner in the message.** The whole point is that the second agent can now go coordinate with a thread it can identify. An error that says "session busy" without a name is barely better than no header at all.++**3. Label the UI.** If your bridge drives a visible window, put the owning thread where the **user** can see it — window title, taskbar tooltip, or jump-list name — via `desktop_set_window_identity` (and `desktop_taskbar`'s `thumbnailTooltip` for uncapped hover text). The user is the one with 20 tabs open; showing which tab owns which window is the fix for their side of the problem. Full guide: the `adom-desktop-window-identity` skill, and the "Branding + driving a window you borrowed" section immediately below.++**4. Report it back.** Include the owning thread in your **status / list verbs**, so an agent can *discover* it is not alone before it stomps on something:++```jsonc+// <prefix>_list_sessions+{ "sessions": [ { "id": "rev", "ownerThread": "chip-fetcher tab 3", "url": "…" } ],+ "_hint": "A session with an ownerThread other than yours is being driven by another AI thread. Open your own session rather than sharing one." }+```++### Notifications already carry it++`notify_user` derives its toast attribution line from the caller automatically, so a toast your bridge sends **on an agent's behalf already names the thread** — you do not have to build that string. Pass an explicit `attribution` string only when you want to override it.++### Where it comes from (the caller side)++Useful to understand, though none of it is your responsibility:++```bash+# The AI exports this ONCE per session, in its container:+export ADOM_AI_THREAD="chip-fetcher tab 3"+export ADOM_CONTAINER_NAME="galliaApril" # optional+```++The `adom-desktop` CLI then attaches a `caller` block to **every** verb automatically. A relay call with **no `aiThread` is REFUSED by AD** with `errorCode: caller_identity_required` before it ever reaches a bridge — the refusal names exactly what is missing and how to set it permanently, so callers fix it once and move on.++**So by the time a request reaches you, identity is normally present.** The exception is **loopback callers** — HD, another bridge, or the local CLI — which are trusted by transport and are not required to identify. Code for the absent case (`|| 'unidentified'`), never assume the header exists.++### Self-audit++- [ ] Does every verb log line include the **caller thread**?+- [ ] Do your **session-creating** verbs record the owning thread on the session?+- [ ] Do your **session-using** verbs warn (or, if destructive, refuse) when a different thread arrives — **naming the current owner**?+- [ ] Does any **window you drive** show the owning thread to the user (title / tooltip / jump-list)?+- [ ] Do your **status / list verbs** report `ownerThread` so an agent can discover it is not alone?+- [ ] Have you confirmed you use these headers **nowhere** as an authorization check?+ --- ## Branding + driving a window you borrowed (window identity + taskbar, v1.9.106–1.9.153)