Per-tool emoji on tool cards (gateway-resolved via hermes get_tool_emoji)
CI / Gateway plugin tests (push) Successful in 5m6s
CI / Kotlin tests (android host + desktop) (push) Successful in 6m45s

tool.start gains an optional cosmetic 'emoji' field, resolved server-side
through hermes' own display layer (active-skin overrides, then the tool
registry's per-tool emoji) so icons track the user's hermes theme and
new/plugin tools get their glyph for free. Omitted for unknown tools so
the app falls back to its default wrench.

- protocol.py: tool_start(emoji=...) kwarg, payload field when set
- adapter.py: _tool_emoji() helper (lazy import, None on unknown/failure)
- frames.schema.json + docs/04: field documented
- app: ToolStartPayload.emoji -> ToolItem.emoji -> ToolCard header
- tests: frame shape, resolution/fallback, end-to-end tool.start emoji
This commit is contained in:
ARIA committed 2026-08-22 20:26:38 +02:00
1 parent e6015033b6
commit 7f0bdcbbc1
8 files changed
+98 -3

No files matched your search

+6 -1
View File
@@ -126,7 +126,7 @@ truncated / nothing).
```json
{"type":"tool.start","chat_id":"…","payload":{
"index":3,"name":"terminal","preview":"pytest -q","args":{"command":"pytest -q"}}}
"index":3,"name":"terminal","preview":"pytest -q","args":{"command":"pytest -q"},"emoji":"💻"}}
{"type":"tool.progress","chat_id":"…","payload":{"index":3,"name":"terminal","note":"running…"}}
{"type":"tool.end","chat_id":"…","payload":{"index":3,"name":"terminal","ok":true,"duration":12.4,
"output_preview":"12 passed"}}
@@ -135,6 +135,11 @@ truncated / nothing).
`args` may be large; the app truncates per its setting. `output_preview` is a
short tail (full output is not streamed — it lives in agent history).
`emoji` is a cosmetic per-tool glyph resolved server-side via hermes'
`get_tool_emoji` (active-skin overrides, then the tool registry's per-tool
`emoji` — e.g. `read_file` 📖, `write_file` ✍️, `terminal` 💻). Omitted when
the tool is unknown, so the app falls back to its own default glyph.
### `typing` / `typing.stop`
```json
+1 -1
View File
@@ -47,7 +47,7 @@
"message.stop": { "payload": { "message_id": { "type": "string" }, "final_text": { "type": "string" }, "reasoning": { "type": "string" }, "model": { "type": "string" }, "tokens": { "type": "integer" }, "runtime": { "$ref": "#/definitions/runtime" }, "ts": { "type": "integer" } } },
"message.deleted": { "description": "The given message(s) were deleted from a chat/thread. Response to a message.delete request (id set) and broadcast to every device so all drop them from their cache; also outboxed so an offline device learns of the deletion on its next sync.", "payload": { "message_ids": { "type": "array", "items": { "type": "string" } } } },
"commentary": { "description": "Intermediate assistant beat.", "payload": { "message_id": { "type": "string" }, "text": { "type": "string" } } },
"tool.start": { "payload": { "index": { "type": "integer" }, "name": { "type": "string" }, "preview": { "type": "string" }, "args": { "type": "object" } } },
"tool.start": { "description": "Cosmetic per-tool emoji (resolved server-side via hermes' get_tool_emoji: active-skin overrides, then the tool registry's per-tool emoji); omitted when the tool is unknown so the app falls back to its own default glyph.", "payload": { "index": { "type": "integer" }, "name": { "type": "string" }, "preview": { "type": "string" }, "args": { "type": "object" }, "emoji": { "type": "string" } } },
"tool.progress": { "payload": { "index": { "type": "integer" }, "name": { "type": "string" }, "note": { "type": "string" } } },
"tool.end": { "payload": { "index": { "type": "integer" }, "name": { "type": "string" }, "ok": { "type": "boolean" }, "duration": { "type": "number" }, "output_preview": { "type": "string" } } },
"typing": { "payload": { "on": { "type": "boolean" } } },