Hermes Agent Archive / Skills

Posting a live message

From Official Docs · October 21, 2026

A plugin can post one message into the conversation it is working for and update that same message later, on any channel, with no per-platform code. Use it for live progress (a checklist while a long tool runs, a plan card that changes as the plan is revised): Signatures: ctx.post_message(text: str) -> PluginMessage and PluginMessage.edit(text: str) -> dict . From async code that already runs on the gateway's event loop (an async hook or slash-command handler) use await ctx.apost_message(text) and await handle.aedit(text) instead; the sync forms refuse there with error: "on_event_loop" rather than deadlocking the loop. Which conversation. The current conversation is the session bound to the calling code: a tool handler or hook running in that session's turn, its slash command, or a hook fired for it. The plugin never names a chat, so it cannot post anywhere else. A thread the plugin starts itself does not inherit that binding (start it with contextvars.copy_context().run(...) if it should), and a timer, a cron run or a process with no live gateway has no conversation at all. Never raises. Every call returns a result. handle.ok , handle.message_id and handle.error describe the last…

From the official Hermes Agent documentation.

def run_migration(args, **kwargs):
    status = ctx.post_message("Migrating: 0/3 tables")
    for i, table in enumerate(TABLES, 1):
        migrate(table)
        status.edit(f"Migrating: {i}/3 tables")
    status.edit("Migration done ✓")
    return tool_result({"migrated": len(TABLES)})