ELPA Logo NonGNU-devel ELPA: hermes

hermes Atom Feed

Description
Emacs frontend for Hermes Agent
Latest
hermes-0.9.0.0.20261013.97.tar (.sig), 2026-Oct-13, 2.66 MiB
Maintainer
Thanos Apollo <public@thanosapollo.org>
Other versions:
release version
Website
https://git.thanosapollo.org/emacs-hermes
ELPA's Repository
CGit or Gitweb
All Dependencies
keymap-popup (.tar)
websocket (.tar)
markdown-mode (.tar)

To install this package from Emacs, use package-install or list-packages.

Full description

hermes.svg

An Emacs front-end for Hermes Agent, driven over the dashboard/TUI gateway.

1. Installation

Hermes Agent with dashboard/TUI gateway support is required.

1.1. NonGNU ELPA

hermes is available via NonGNU ELPA.

Install it with M-x package-install RET hermes.

1.2. package-vc (Emacs 30+)

(use-package hermes
  :vc (:url "https://git.thanosapollo.org/emacs-hermes" :lisp-dir "lisp")
  :custom (hermes-dashboard-transport-url "http://127.0.0.1:9119"))

2. Usage

M-x hermes opens the dashboard. M-x hermes-project-chat switches to a live chat for the current project or creates one at its root; with C-u it always creates another. Chat buffers are named after their project, using project-name when the directory exists locally and its basename otherwise; a linked Git worktree takes its main repository's name. A project chat is named after its launching project; any other chat after the project of the first gateway working directory it reports. Later gateway directory changes, such as the agent moving into a worktree, never rename the buffer: the header shows the abbreviated cwd whenever it differs from the project root. Customize hermes-chat-buffer-name-function to replace the default complete naming convention. A direct or resumed remote chat uses the editor directory in its initial buffer name while its gateway cwd is unknown. Its header stays detached, and the editor path is not sent to the gateway. M-x hermes-switch-to-chat offers chats grouped by project, most recently used first, annotated with session title, state and any divergent cwd. M-x hermes-chat always opens a new chat buffer:

  • RET to send.
  • / for slash commands.
  • C-c C-o for the actions menu; in the transcript or an empty composer, ? opens it too. An empty composer shows these key hints until you type (hermes-chat-composer-placeholder); the hint is never buffer text.
  • In the transcript, n and p move between your turns, and M-x imenu lists them by first line. SPC and DEL (or S-SPC) scroll like view-mode. w copies: with an active region it is kill-ring-save; inside a fenced code block it copies just the code, without fences; elsewhere it copies the Markdown source of the reply or message at point. In the composer these keys keep their ordinary editing meaning.
  • The menu's Show group hides or shows expanded tool output (O) and reasoning (E) in the whole chat at once, without moving point, and expands (+) or collapses (-) every disclosure. Hiding wins over an entry's own expansion until you expand an entry of that kind by hand. When the backend profile does not capture reasoning (its display.show_reasoning is off), showing reasoning says so; M-x hermes-chat-enable-backend-reasoning turns it on in that profile's saved config, for all of its sessions, after confirmation.
  • C-c C-c interrupts the running turn, as in shell-mode; when nothing is running it only says so. C-c C-k still interrupts and sends the draft.
  • The menu's Aside group offers the side question (y, /btw), a background task (G, /bg) and Stop processes (!, /stop). Session (S) holds New, Rename and List sessions plus a Fork group: Branch (b), Hand off (H) and Compress (z, /compress; C-u asks for a focus topic). Activity (B) lists queue, workers, tasks and output; Directory (w) sets the chat's working directory.
  • C-c C-g (or Session menu, g) shows this session's goal, loop and heartbeat as the backend reports them. n and p move through sections and criteria, g refreshes (resuming a disconnected session), and ? offers only the actions valid now: pause, resume, release a wait, add or remove criteria, stop or clear. Clearing asks first; resuming a goal sends its next step, as /goal resume does. The header shows a running or paused loop and goal with budget and cadence, e.g. Loop 2/5 · every 10m · next 14:05 · Goal 3/20 turns, marks paused, waiting and parked ones, and shows nothing when none runs. It catches up after each turn, tick notice and due tick. Start them with /goal and /loop as usual. A chat whose session was running automation resumes it when the socket reconnects, retrying briefly while the backend settles; other chats stay lazy. The released backend can add and remove criteria but not mark one done.
  • Stop processes asks the connected Hermes instance to stop every terminal process registered with it, across all of its chats, not only this one. It does not cancel the running model turn or delegated workers; use C-c C-c or the Workers view for those.
  • Approval prompts name the tool and show the full command. Opened with C-c C-a or the [Answer] button they take one key: o=nce, =s=ession, =a=lways or =d=eny; =q or C-g leaves the request pending. An approval that opens by itself instead needs a choice submitted with RET (empty input leaves it pending), so typing meant for the composer cannot approve anything. Clarification readers ask the actual question. While a prompt is pending, its transcript line keeps an [Answer] button that reopens the reader.
  • Each chat pins its resolved spawned or remote transport mode. A spawned chat starts from the editor's default-directory; a remote chat does not.
  • Passive gateway cwd updates change the header, but leave the buffer name and default-directory alone.
  • “Set directory” browses or accepts a path in the gateway's namespace. On success, the backend-returned path becomes both the gateway cwd and the buffer's default-directory; the buffer keeps its project name.
  • C-c C-o M m (or bare /model) fetches model choices from the owning dashboard each time. /model argument completion stays cached and nonblocking; opening the picker refreshes that cache after backend changes. The profile browser's model picker also fetches fresh choices each time. Failed fetches do not offer stale cached choices. Choices are one completion list grouped by provider (with completions-group, Vertico or similar), annotated with the context window, price and capabilities the catalogue reports. The current model is marked • current and is the default; exact values such as claude · OpenRouter (openrouter) keep same-named models from different providers distinct. The dashboard may maintain its own provider catalogue cache.
  • M-x hermes-close closes local connections and Hermes buffers for restart, discarding the frontend model catalogue cache. Reconnecting alone retains completion data; the picker still fetches anew.

Hermes commands declare their buffer contexts for native M-x completion. To filter completion candidates by context, set read-extended-command-predicate to command-completion-default-include-p. Hermes leaves this user option unchanged; with nil, all commands remain listed. Dashboard, chat, browser-opening and setup entry points stay available globally. Context filtering does not change key bindings or prevent direct invocation; commands retain their existing runtime checks.

Point hermes-dashboard-transport-url at your running dashboard:

hermes dashboard --no-open --tui --host 127.0.0.1 --port 9119

To keep a local dashboard independent of Emacs restarts, run it under an external supervisor (for example, a systemd user service), and explicitly attach rather than spawn:

(setq hermes-dashboard-transport-start-mode 'remote
      hermes-dashboard-transport-url "http://127.0.0.1:9119")

Here remote means externally managed, even on loopback. Closing Emacs or reconnecting only closes the client socket; it does not stop the dashboard. If the service is absent, attachment fails without starting a replacement. Start/check your service explicitly, then retry or use hermes-dashboard-reconnect. hermes-dashboard-restart deliberately refuses externally managed dashboards. The default auto mode still spawns a local child for loopback endpoints; it is not the service-attachment setting.

To use more than one dashboard, configure named instances:

(setq hermes-instances
      '(("local" . "http://127.0.0.1:9119")
        ("remote" . "https://dashboard.example.org")))

Commands prompt for an instance only when the current buffer does not already own one. Chat buffers retain their original instance and resolved transport mode, so later configuration changes cannot reroute them. Chats against different dashboards can stay open at the same time. Browser views retain their chosen instance until explicitly reopened for another one; with more than one instance configured, the mode line names it next to the buffer name. List browsers keep their column titles in the header line.

2.1. Kanban task bodies

Use E in a Kanban board or task detail to open the selected task's body in an independent multiline draft. C-c C-c saves only the body and reads the same task back; C-c C-k discards the draft. Empty bodies and Unicode are preserved. Saving uses the backend's last-write-wins behavior and can overwrite concurrent body edits; it does not resend title, status or assignee fields.

The draft retains its original board, task and backend. Changing or closing that source view retires save authority; copy the retained draft into a newly opened editor if needed. Failed or unverified saves retain the text and are never automatically retried. Check the board before manually retrying an uncertain update. A deleted task is not recreated.

Task details display literal Markdown with guarded programming-language highlighting. Fence labels cannot select arbitrary minor or global modes; copying preserves markup, and outline navigation remains available.

2.2. Agent plugins

M-x hermes-list-plugins opens the installed agent-plugin inventory. Use C for the curated catalog, RET for details, a to install the selected catalog entry, and I to return to installed plugins. The existing i command still accepts a plugin identifier or Git URL.

Details show repository, subdirectory, tier, full catalog pin, declared tools/hooks/middleware, environment requirements, and supported platforms. Tier describes provenance, not a safety guarantee. Installation can install dependencies and run code on the backend. It targets that dashboard's launch-profile home, not a profile selected in another view or chat.

Catalog installation resolves the entry again on the backend. The displayed pin is informational: Hermes Agent 0.21.4 does not honor ref for catalog installs, so Emacs does not send an override. Check the returned Installed SHA in details after installation. Emacs reads back both the catalog and installed inventory; it does not force replacement or automatically enable the plugin. Existing enablement can persist. Configured state is not proof of runtime activation.

If Update reports that new capabilities need consent, the Plugins header shows Awaiting capability consent. RET displays an inert handoff with the candidate SHA and declared additions. The receipt reports that the update was not applied. Emacs sends no consent retry: review the fresh candidate using the backend's native workflow, or leave the review to decline. Use g to refresh explicitly; a refresh never replays the update.

2.3. Named custom endpoints

M-x hermes-endpoints lists named endpoints on the owning dashboard. It retains the profile of a chat or onboarding view; other origins use the dashboard's launch profile. Use c for a new draft or RET to edit a saved row, then e to edit individual fields and k to choose Preserve, Replace or Clear for the API key. Keys use masked input, never minibuffer history or list text. Preserve omits the key from Save; only Clear sends an empty key. Stored keys are not fetched. Replace refuses values that would become empty under Hermes Agent 0.21.4 storage: boundary whitespace is stripped, then CR/LF and non-ASCII characters are removed. Refusal retains the previous key policy and draft. Accepted keys are sent literally, but backend storage can still change them; saved metadata is not proof of a usable credential or successful authentication.

In a draft, t requests explicit consent before Test. The dashboard sends the entered credential to the destination, may probe alternate /v1 paths, and may perform chargeable inference: one output token for Chat Completions or Messages, or up to 16 for Responses. Test neither saves nor activates. Its observations are not proof of successful inference or authentication: the transport check accepts 401/429 responses and inconclusive timeouts. The resolved URL is shown without changing the draft; u explicitly adopts it and the advertised model metadata.

s confirms Save with make_default:false. This is last-write-wins upsert, not an atomic edit: it can recreate a concurrently deleted endpoint. The backend merges unknown settings and existing model metadata. Unedited API mode and context length are omitted to preserve them; model discovery is sent explicitly. Editing an already-active provider can affect future runtime use even without Activate. Inspect the saved list and active model configuration read back below the retained draft; backend normalization can change names, URLs, IDs and model aliases.

Back in the list, a separately confirms Activate; d confirms Delete and possible detachment of the active provider's URL and credentials. g reconciles state, including after a failed or uncertain write; writes are never automatically retried. ? shows actions in either view.

Legacy/direct-config rows remain read-only. Stored IDs such as has.dot, -edge and edge_ support Activate/Delete and are sent unchanged. Hermes Agent 0.21.4 can leave the active-model mirror attached for IDs containing ASCII spaces (for example Mixed endpoint) or beginning with custom: (case-insensitive). Boundary whitespace can select a different stored key; slash-containing IDs and the whole IDs . and .. are not addressable by this route. Activate/Delete refuse those hazards before confirmation. The client does not rename IDs or repair backend configuration silently.

2.4. Gateway restart handoff

H in Configuration, Agent plugins, Messaging, or Status/Logs opens hermes-system-restart-handoff. It is also in the dashboard's System menu. The handoff retains the selected backend and explains the external operator action; it does not save, restart, run a shell command, or replay a configuration write.

Saved-value readback and runtime adoption are separate. Adoption and actual service/profile impact remain unverified: a named profile may share a multiplexer with other profiles and active sessions. Open the displayed dashboard URL separately and use its gateway controls, or ask that backend's operator to identify the service and arrange a safe restart.

The handoff's s (Status) and l (Logs) open read-only observations for the captured endpoint. After external action, g explicitly refreshes them; reported fields are not proof of a scoped restart or configuration adoption. If the originating view or handoff is replaced or retargeted, reopen the handoff rather than using its stale actions. Observation actions attach to the captured URL; they never auto-start a local service, even for loopback addresses.

2.5. Foreign histories

In Sessions, F opens the serving backend's Claude Code and Codex histories. Choose the destination profile and source. This does not scan the Emacs host. > requests the next page, including after an unreadable-only page. RET opens an inert tail preview, bounded by the backend to 40 turns and 8000 characters per turn; backend directory labels are not local links.

Previewing never imports. i asks for confirmation before importing into the displayed destination. Only after the returned stored session is read back from that profile does RET offer native resume. Preview refresh and canceling another import confirmation preserve that verified session; changing the preview's backend, destination or history does not. If the legacy default backend URL changes, reopen from Sessions before previewing or importing again. Once resume opens a chat, acquisition and reconnect retain that chat's selected backend, even if a mode or display hook changes the legacy default URL. An interrupted import can have an uncertain outcome: use g to inspect before explicitly retrying. There is no automatic import retry.

2.6. Context budget

From a connected chat, C-c C-o X b (or M-x hermes-chat-context) opens an on-demand context snapshot for that exact runtime session. g explicitly refreshes it; there is no polling. Computing a snapshot can rebuild backend prompt blocks and invoke memory hooks, even when computation fails.

The view keeps backend category estimates separate from reported usage and its source. Unbuilt categories and missing file information remain unknown, not zero-cost claims. Context-file paths are inert backend labels; they are not visited on the Emacs host.

2.7. Local dictation and read-aloud

C-c C-o A opens local audio actions in an attached chat: r records, s stops and transcribes, c cancels, and a reads a chosen settled assistant reply aloud. Nothing listens or plays automatically.

Configure hermes-audio-recorder-command and hermes-audio-player-command with trusted local foreground programs. Both default to nil; chat works without them. The recorder must write audio to stdout and finalize on SIGINT. The player receives a private local file in place of its %f argument. For example, with ALSA and ffplay installed on the Emacs machine:

(setq hermes-audio-recorder-command
      '("arecord" "-q" "-t" "wav" "-f" "S16_LE" "-r" "16000")
      hermes-audio-recording-mime-type "audio/wav"
      hermes-audio-player-command
      '("ffplay" "-nodisp" "-autoexit" "-loglevel" "quiet" "%f"))

Recording and read-aloud ask consent and identify the backend and profile. Devices belong to the Emacs machine, even with a remote dashboard. Audio bytes, not local paths, go to that chat's backend transcription relay. The result is a draft, never an automatic message. If you edit the draft while waiting, the transcript opens separately for manual copying. Silence leaves the draft unchanged. Read-aloud uploads only the reply you choose and plays the returned bytes locally; it does not retrieve provider credentials.

The defaults allow 8 MiB and 120 seconds per recording or playback. WAV, FLAC, Ogg, MPEG and WebM are supported; the recorder's MIME must match its output and the player must support the returned format. A time limit cancels rather than uploads. Stop gives the recorder two seconds to finalize. Cancellation or chat retirement stops owned local processes, removes private playback files and ignores late replies. It cannot revoke an upload or cancel a provider request already running. Limits need Emacs event-loop progress; response size is checked only after HTTP retrieval. Audio captures an absolute, local temporary-file-directory before consent. Later changes to that option cannot redirect recording or playback. Handler-managed scratch is refused, including when a file-name handler appears while waiting. Backend audio providers must be configured separately. These relays require Hermes Agent 0.21.4 or a compatible API.

2.8. Browser-vault prompts

Hermes Agent 0.21.4 browser-vault requests use native minibuffers: masked password-manager unlock, save-login password and verification code readers, plus an identifier reader without input history. Use C-c C-a to answer a pending request. Each reader shows the owning backend endpoint and the site/origin or password manager supplied by that backend. Empty password/code or C-g declines a current request; expired or disconnected requests cannot send or recover an answer.

Credentials go directly to the original backend request, never to chat input. The backend owns origin verification, credential storage and browser filling; Emacs does not run a local password manager or control its local browser. Minibuffer input, including indirect aliases sharing its text, is excluded from capability listings and reads even when the optional buffer-denial policy is disabled. Native kill/copy operations use a private kill ring and yank menu. Expiry revokes response authority immediately; an unrelated nested reader or recursive edit can finish before the expired vault reader is dismissed. This is not memory zeroization or a sandbox against trusted Emacs Lisp. Card/address collection is unsupported.

2.9. Headless prompts

(hermes-request REQUEST RESOLVE REJECT) returns a cancellation thunk. Require hermes-request for use without the UI. For example:

(hermes-request '(:prompt "Explain spaced repetition." :profile "study-eval")
                (lambda (text) (message "%s" text))
                (lambda (reason) (message "Request failed: %s" reason)))

The profile must exist in the selected backend catalogue with a configured model and provider. Each request captures those exact choices from a fresh catalogue read, overriding backend launch defaults without changing profile configuration. Missing or malformed choices fail before session creation; credentials and any configured fallback policy remain backend-owned. Each call creates a fresh hidden session; no current chat history is used. Only a successfully completed final response reaches RESOLVE; cancellation, timeout, disconnect, or required interaction reaches REJECT. hermes-request-timeout bounds the request. Closing the session is best effort and does not delete backend history. A fresh session and profile/model selection isolate conversation context, not tools or filesystem access. This client does not verify the agent's effective tool set; an empty configured tool selection is not a no-tools guarantee on the supported gateway. For evaluation that must not affect the host, use an independently isolated backend. Profile configuration alone is not a sandbox.

2.10. Local text/source attachments

Use Compose → Attachments → Upload text\/source or M-x hermes-chat-attach-file in an already connected, attached chat. This separate action leaves image attachment and path completion unchanged. It accepts up to 2 MiB of local UTF-8 text/source with a supported suffix; PDF extraction and arbitrary binary files are unsupported. Local reads are bounded; the underlying HTTP response retrieval is not streaming-bounded.

The command captures the owning session's gateway-host working directory, requires managed Files policy admission, and asks before attempting a write there. Upload checks write permission. This does not establish equivalence with a remote terminal backend. A generated filename reduces collisions but overwrite:false is a non-atomic backend precheck, not exclusive creation. Exact byte readback precedes the owning session's file.attach(path) call. Only its validated reference is appended to an unchanged composer; nothing is sent. Paths refer to current content and can change before a later Send.

A native recovery view retains the original literal draft, bounded bytes and any returned reference. Oversized files retain only an explicitly labelled incomplete prefix of 2 MiB plus one byte; the unread tail is not recovered. Newer draft edits, cancellation, ownership changes and uncertain failures never replace text, retry an upload, or delete a remote file. Use s in recovery to save exact bytes to a new local file, then copy a reference manually if appropriate. Attachments → Reopen retained recovery opens retained data afresh without repainting a view reused for notes. Data is retained in memory only: save it before closing recovery views or Emacs. At most eight live recovery views are retained per chat. Uploaded files may remain after failure or cancellation; cleanup is a separate explicit action.

2.11. Output previews

Completed tool outputs with published file paths, and closed HTML/SVG code blocks in assistant replies, offer [Preview] buttons. The chat actions menu also provides Activity → Preview output. Previewing never reads a backend path from the local filesystem: it requests current bytes from the owning chat's managed-files API, with a 4 MiB limit. The server's file policy may refuse paths outside its managed root. Failed reads offer retry and copy-target commands; remote URLs are copyable but never fetched automatically.

Previews are inert. HTML loses scripts, links, forms, attributes and external resources; SVG renders only an explicitly supported passive subset. Use v to switch to retained source, s to save the exact bytes to a new local file, g to retry, y to copy the target, and q to quit. Raster display depends on Emacs image support; unavailable previews retain source or binary metadata and the save action. For rendered raster images and passive SVG, + and - zoom between 10% and 400% of the initial displayed size; 0 restores that size. Zoom requires native image scaling and never fetches or changes saved bytes. Source toggling and zoom wait for a pending read; use C-g to cancel it first. File paths refer to current content, not historical versions: the backend does not publish reliable artifact revisions.

2.12. Skills: installed content and Hub previews

In the Skills inventory, RET or f opens the selected skill's SKILL.md from that backend's launch profile. The text is read-only until C-c C-e (Edit). Use C-c C-c to save, C-c C-k to discard local edits, and C-c C-h for help. Save replaces the whole document: it can overwrite concurrent changes. A matching backend readback confirms the save; errors, validation refusals and uncertain outcomes leave your draft intact. Nothing retries a write automatically. C-c C-g reads again after local edits have been saved or discarded. Backend paths are labels, not local file targets. These controls do not create skills or edit supporting files, and saving does not claim to activate changes in a running session.

H in inventory, or M-x hermes-skills-hub, opens the separate optional Skill Hub. Use n/p to move, f or RET to preview, b to return, s to search configured sources, S to inspect source availability, and P to choose a backend profile. Rows retain their source and identifier, even when names match. Search is bounded; timed-out sources are reported as partial results. Preview shows inert SKILL.md text and supporting file names only—not a review of those files.

In a preview, S requests a scan only after confirmation. Scanning can fetch and quarantine files on the backend and run an optional external scanner. Allow, ask and block are policy results, not safety guarantees; an absent or failed advisory scan is not a pass. Browsing and previewing never install, enable or execute a skill. These views require backend REST support and have no local command fallback.

2.13. Live tasks

Use Activity → Live tasks in the chat actions menu, or M-x hermes-chat-show-todos, to open the session-owned checklist. It follows backend task snapshots without changing the chat draft or moving focus. The panel is read-only: pending tasks stay pending when a turn ends, with settled or stale labels rather than invented completion. Backends without a restored task snapshot can still populate the panel from live tool results.

When a turn produces a task list, the transcript gains one collapsed ▸ Tasks 3/5 · now: ITEM line for that turn, updated in place as the list changes. The arrow expands it to the items (☑ done, ▶ in progress, ☐ pending); activating the label opens the live task panel.

2.14. Turn status and queued messages

While a turn runs, the rule above the composer shows its state, elapsed time, current tool, task progress and queued count, for example ● Running 0:42 · terminal: make check · ☐ 3/5 · 2 queued. Narrow windows drop the tool first, then the task progress. After a turn ends the rule shows its duration (✓ 12s) for hermes-chat-status-rule-linger seconds. The clock ticks only while the chat is visible.

Queued messages are listed as dim lines just above the rule, one per message; activate a line to open the queue panel on that message.

2.15. Conversation commands and file references

/btw QUESTION asks a side question over the current conversation without changing its main history. /bg TASK (also /background) instead starts an independent background task. Both deliver persistent result links while the main turn can continue. A rejected or uncertain side question is retained in a separate *Hermes side question* buffer, never in the send queue. Inspect results before manually copying it into a new /btw command; recovery does not retry the question or replace a newer draft.

/branch [NAME] branches the attached, idle conversation through the backend and opens the child with its history in a separate chat. The parent and its draft remain available. If a branch fails after backend creation, the child may still exist; inspect Sessions before retrying.

Composer @ completion reads the attached session's backend workspace, not Emacs's local project. Try @src/mo, @file:src/mo or @folder:src and press TAB; when the asynchronous lookup finishes, completion reopens once if the composer, point and session are unchanged. Accepted candidates become explicit, quoted backend references. Paths that cannot be represented by the backend parser are not offered. This is reference completion, not a local-file upload; literal bare @path text does not attach file contents.

New remote chats with no selected directory inherit the selected profile's backend workspace. Emacs records the returned directory without changing its own default-directory. Deliberately selected directories remain explicit.

2.16. Session pins and named workspaces

In M-x hermes-list-sessions, k toggles the selected session's server pin. If the pin is unknown, the first press reads it without changing it. The same action is available in session details; M-x hermes-sessions-pin and M-x hermes-sessions-unpin set it explicitly. Emacs reads the stored session back after each change. A failed read leaves the pin unknown. Use l for a profile's stored catalogue and > to load its next window.

M-x hermes-list-projects opens the backend's named, multi-folder workspaces in that dashboard's launch profile, also available from the dashboard and command palette. Use RET for details, P to change backend profile, and ? for create, rename, archive/restore, folder, and active-project actions. Explicit profile names must match that backend's current profile catalogue exactly; unavailable catalogues block those scoped requests. This preflight cannot prevent a profile from being deleted on the backend during dispatch. Folder prompts accept backend paths, without local file completion. Grouped sessions come from the backend's bounded subset, not a complete session archive.

Projects are metadata: selecting an active project does not move sessions, and deleting a project keeps its directories and sessions. Named backend projects are separate from the editor project used by hermes-project-chat. To change an attached chat's working directory, use its Set directory command. Stored-session moves are unavailable because the backend can confuse live sessions with the same stored ID in different profiles. Older backends without project methods show an unsupported status; ordinary session browsing remains available.

2.17. Connector authorization during chat

A running connector tool can request authorization or repair. Its card shows backend status and the account alias beneath that tool call, or as a separate transcript entry until the tool row arrives. Activate Authorize with RET or the mouse to open the browser. On a card, ? lists currently valid actions: o opens the link, g checks authorization now, a approves a pending install, r retries a failed or expired link, d skips that connector, and c lets the tool continue with the outcomes reached so far. The backend watches authorization; opening a link does not mark it connected. An install that needs credentials names them; enter those in Hermes Desktop.

Pending operations are restored from session.resume after reconnecting a running chat. Operation sequence numbers reject older updates. Connector credentials are neither collected nor persisted by the client. This feature requires the backend connector service; provider OAuth, MCP setup, and the account/catalog/policy browsers are separate features.

2.18. Hosted Group Chats

M-x hermes-list-groups reads rooms hosted by the selected backend. The dashboard's Browse menu also offers H for Group Chats. Use RET or f to open a room, m for the next list page, and g to refresh or reconnect. Public messages show their speakers in sequence order; member activity, task counts, and pending approvals appear separately. With a ready hosted driver, c creates a room from two to six existing profiles on that same backend. Open the room and use s to send a literal message in a named thread. The roster shows mention handles; the backend owns mention policy, rounds, member sessions and durable work, not Emacs.

Send acceptance is separate from completed member turns. If a receipt is lost, R reconciles the retained submission against the room log before offering an explicit retry with the same event ID. No submission or task is retried automatically on reconnect. Keep the view open to retain an uncertain message's recovery identity; closing it discards that local copy.

a answers an exact pending approval (once or deny). x requests cancellation for this room and reads state back; it does not terminate the gateway or claim that every process stopped. r explicitly retries a named uncertain task, including work the backend has deferred, after warning that the prior execution may have had effects. Before offering or answering an approval, Emacs reads complete room history and excludes retired tasks and executions, so historical failures do not block fresh requests. Incomplete history, ambiguous task authority, or stopping work refuses approval; use g to continue reading. State, history and approval are separate backend calls, not an atomic transaction with other clients. Closing the view or Emacs only stops local monitoring: backend work can continue.

The backend must advertise the protocol-2 room-reading methods and features. Driver availability is shown separately: stored history can remain readable when the worker is unavailable. F toggles five-second activity refresh and keeps its own connection reference, even without an open chat. Turning it off, closing or repurposing the view, transport retirement, terminal room state, or three consecutive read failures releases that reference. g resumes from the last rendered cursor; failed rendering retains the accepted text and cursor. Each refresh reads at most 20 log pages; g continues larger histories.

Authority changes restart replay explicitly. A sequence gap triggers a state check, not a skipped range. Disbanded history stays marked as such; expired history is a retained local snapshot, never a revived room. These are gateway-owned rooms, not the desktop's legacy localStorage rooms.

3. Bot Chats and routines

In M-x hermes-list-profiles, RET opens the selected profile's canonical Bot Chat. Emacs looks up that exact title on the selected backend, including hidden history and its compression continuation. Only confirmed absence offers creation. A new conversation stays hidden and follows profile configuration; its title must be persisted and read back before the chat opens. Failed or contradictory lookup never creates another conversation. Opening sends no introduction, though the backend can prewarm the agent and discover MCP tools.

N opens a separate ordinary scratch chat. Within a canonical Bot Chat, /new offers compression or explicit scratch creation; it does not silently replace the relationship. Ordinary chats retain their existing /new behavior. m still edits the profile's default model.

R opens that backend/profile's existing cron jobs in the ordinary cron editor. Opening or refreshing Routines only reads jobs. Creating, editing, pausing and running a job remain explicit cron actions; there is no separate scheduler or automatic Bot Chat delivery preset.

Canonical chats, scratch chats explicitly opened by their /new command, and Routines retain the backend selected when opened, including after reconnect or changes to the default dashboard URL. Routines actions and run transcripts stay on that backend. Reopen from Profiles to choose another backend; ordinary chats and the all-jobs cron browser keep their existing endpoint-selection policy.

4. Dashboard authentication

hermes-dashboard-transport-remote-auth-method defaults to auto:

  • With auto start mode, loopback dashboards spawn without extra credentials.
  • With remote start mode, an ungated dashboard at 127.0.0.1 can supply its session token automatically. Emacs reads the dashboard's HTML asynchronously; no shell command or environment change is needed.
  • Remote or gated dashboards probe /api/status. Auto authentication uses valid stored basic credentials first, otherwise native PKCE when advertised, and reports missing basic credentials for a basic-only dashboard.

Automatic discovery accepts only 127.0.0.1, never DNS names (including localhost), other addresses, or redirects. Use 127.0.0.1 rather than localhost for credential-free attach. Explicit token arguments, auth-source entries, and HERMES_DASHBOARD_SESSION_TOKEN take precedence, in that order. Discovered tokens stay in memory for their dashboard endpoint. Reconnect reads a fresh token after a backend restart; a REST GET rejected with HTTP 401 may retry once. Writes are never replayed automatically. Failed basic/native login never falls back to discovery.

Supported gated attach paths:

  1. Basic/password — auth-source entry with port hermes-dashboard-basic, login username, and password secret. Emacs posts password-login cookies and mints a WebSocket ticket.
  2. Native PKCE OAuth — when /api/status advertises native_pkce, Emacs opens the system browser, completes the official /auth/native/* loopback flow, stores access/refresh tokens in auth-source under login/port hermes-dashboard-native, authenticates REST with Authorization: Bearer, and mints a short-lived WebSocket ticket. Failed or cancelled login does not overwrite prior stored tokens.
  3. Legacy session token — auth-source entry with login/port hermes-dashboard-token and the token as secret, or environment variable HERMES_DASHBOARD_SESSION_TOKEN. Used for ungated dashboards and forced token mode.

Force native, basic, or token to bypass auto selection:

(setq hermes-dashboard-transport-remote-auth-method 'native) ; or 'basic / 'token / 'auto

Generic auth-source examples (replace host/port/values; never commit real secrets):

machine https://dashboard.example.org:9119 login hermes-dashboard-native password {"access_token":"…","refresh_token":"…","expires_at":0,"provider":"oauth","user_id":""}
machine https://dashboard.example.org:9119 login admin password s3cret port hermes-dashboard-basic
machine https://dashboard.example.org:9119 login hermes-dashboard-token password SESSIONTOKEN port hermes-dashboard-token

If a gated dashboard advertises neither native_pkce nor a basic provider, Emacs refuses attach with an actionable error. Cookie-only browser OAuth without native_pkce remains unsupported.

5. Optional Emacs bridges

The dashboard/TUI connection above drives chat and management. Two separate, optional paths let Hermes call into Emacs:

  • hermes-capabilities is the native dashboard capability-provider path.
  • hermes-exec is the HTTP eval endpoint used by the external stdio MCP bridge, hermes-emacs-plugin.

To use the stdio MCP bridge, install it from Git, enable the endpoint, then copy its registration command:

pipx install git+https://git.thanosapollo.org/hermes-emacs-plugin
(require 'hermes-exec)
(setq hermes-exec-enabled t
      hermes-exec-host "127.0.0.1"
      hermes-exec-require-approval t)
(hermes-exec-start)
;; M-x hermes-exec-show-bridge-command

The generated command registers the packaged hermes-emacs-mcp entry point. For a non-loopback private address, also set the same EMACS_EXEC_TOKEN for Emacs and the bridge. Do not expose the eval endpoint on a public interface.

Desktop notifications default to completed chat replies, terminal chat errors, input requests, background-task results, and Kanban states that need attention. Cron failures use the same policy when cron failure monitoring is enabled. They are suppressed only when the target buffer is in the selected window of the focused frame. Customize the event set, or set it to nil to disable notifications:

(setq hermes-notifications-events
      '(chat-reply chat-error prompt background
        kanban-attention cron-failure kanban-done))

While any chat awaits an approval, clarification or other input, the global mode line shows a segment such as [Hermes: Clarify]. It names the prompt kind and count, never the prompt itself; clicking it answers from the owning chat. Customize hermes-chat-prompt-indicator to nil to hide it.

Old versions

hermes-0.9.0.0.20261013.82.tar.lz2026-Oct-13 444 KiB
hermes-0.9.0.0.20261012.50.tar.lz2026-Oct-12 428 KiB
hermes-0.8.0.0.20261011.28.tar.lz2026-Oct-11 380 KiB
hermes-0.8.0.0.20261001.1.tar.lz2026-Oct-02 370 KiB
hermes-0.7.1.0.20260922.20.tar.lz2026-Sep-22 297 KiB
hermes-0.6.0.0.20260912.6.tar.lz2026-Sep-13 541 KiB
hermes-0.5.0.0.20260908.13.tar.lz2026-Sep-08 498 KiB
hermes-0.4.3.0.20260901.0.tar.lz2026-Sep-01 369 KiB
hermes-0.3.3.0.20260824.6.tar.lz2026-Aug-26 357 KiB
hermes-0.2.1.0.20260813.1.tar.lz2026-Aug-13 299 KiB

News

1. Version 0.9.0 (unreleased)

Incompatible changes:

  • Chat buffer names follow the chat's project rather than the gateway's live working directory (see below), and the DIRECTORY argument of hermes-chat-buffer-name-function is now that stable naming directory. Review custom naming functions that expected the live directory.
  • Chat, profile and cron model completion candidates now lead with the model, for example claude · Anthropic (anthropic). Update any code or completion configuration that matched the old candidate text.
  • List browsers keep their native column titles in the header line. With several instances configured, the instance name now follows the buffer name in the mode line, and the scope or status text of the Pairing, Webhooks, Tool Setup, Agent Plugins, Foreign histories and Skill Hub views moved to the mode line.
  • In the Workers view RET now opens the row's details; the published worker log moved to L.

New features and fixes:

  • The chat header's work count follows the Hermes TUI: 🤖 N counts running and queued workers and ⚙ M running background processes, with no trailing ?. Each chat keeps the last successful roster read; hiding the chat, a failed read or a timeout no longer clears or doubts it. Live subagent.* events update the count between polls, a background process notice triggers an immediate read, and finished workers and exited processes stay listed for 60 seconds. The Workers view lists workers in spawn-tree order and shows depth, delegation, tool count, last tool and a process's last output line; process elapsed time now comes from the backend's uptime.
  • The Workers view is now an outline with Agents and Processes sections. Each running worker shows its latest tool on a second line and each process its last output line or exit verdict. TAB folds a section or opens a row's card with the full goal, recent tools and the error or summary; n=/=p, f=/=b and ^ move by row, sibling and parent. RET reuses one detail buffer per row, and k on a process kills it after confirmation. Live updates keep point, folds, cards and each window's start.
  • Completed paragraphs, closed code fences and complete table rows render as Markdown while a reply is still streaming; unfinished text stays literal until the turn ends. hermes-chat-stream-format-delay (default 0.1 seconds) throttles the formatting.
  • Streaming no longer slows down as a reply or transcript grows: the read-only protection rescans only changed text, and echo detection no longer renormalizes the whole reply on every chunk.
  • C-c C-c in a chat interrupts the running turn (hermes-chat-interrupt-turn); when idle it only reports that nothing is running. C-c C-k is unchanged.
  • Chat actions menu (C-c C-o): Work is now Activity (B) and Workspace is now Directory (w), so neither reads like Workers (W). The Session menu splits into Session (New, Rename, List sessions) and Fork (Branch b, Hand off H, Compress z, the new hermes-chat-compress). A new Aside group exposes the formerly slash-only side question (y), background task (G) and Stop processes (!). Root b remains Switch chat buffer. Stop processes stops the terminal processes registered with the whole instance, across all chats; it does not cancel the model turn or delegated workers.
  • While a turn runs with no backend event for 30 seconds (hermes-chat-status-rule-quiet-after), the composer rule appends quiet 0:45 in the warning face; it clears as soon as an event arrives.
  • When output arrives while no window shows the end of the transcript, the mode line shows ↓ new. mouse-1 on it or C-c C-j jumps to the latest output; scrolling the end into view also clears it.
  • The header's context usage turns warning-coloured from 80% and error-coloured from 95%. Its tooltip gives exact tokens and percentage, and mouse-1 opens the Context budget view.

… …