All plugins

hermes-windows-eventlog

Answer "why did this process die" on Windows: three read-only tools over the Windows Event Log. win_crash_events reads Application Error / Application Hang / Windows Error Reporting / .NET Runtime records for a time window, decodes the exception code (0xC0000005 -> ACCESS_VIOLATION and the other codes a debugger would name), reports the faulting module, its path and the fault offset, and groups repeated crashes by app and signature. Records are classified by provider as well as by event id, so unrelated providers that reuse ids 1000 and 1001 for informational events are not reported as crashes. win_shutdown_events reads the System log for Kernel-Power 41 unexpected restarts, bugchecks decoded to names (0x9F -> DRIVER_POWER_STATE_FAILURE, accepting the decimal form some providers emit), EventLog 6008 dirty shutdowns, 1074 operator-initiated restarts with the process and comment, and the 6005/6006/12/13 start and stop markers. win_eventlog_query is the general case: any channel, filtered by event ids, levels, an exact provider name or a payload substring, returning structured records instead of raw XML. Queries read newest-first and stop at the window start or at a 5000-record scan cap; a call returns at most 200 records and marks truncated payload text. Channel names, provider names and numeric filters are validated before anything is interpolated into the query. Disclosure - this plugin reads the local Windows event logs and hands what it finds to the model: application and faulting-module paths, exception codes and offsets, report ids, service names and provider message text, which routinely includes user and machine paths (C:\Users\<name>\...). Treat tool output like any other local diagnostic dump when sharing it. It reads only the channels the current account may read - Security and some operational logs answer with an access-denied error rather than any attempt to obtain elevation - and it never writes to a log, clears one or configures a channel. Stdlib-only (ctypes wevtapi plus xml.etree): no Python dependencies, no child processes, no network calls, no file writes, no self-update. Windows only. Disclosure — reads the local Windows event logs and hands what it finds to the model: application and faulting-module paths, exception codes and offsets, report ids, service names and provider message text, which routinely include user and machine paths (C:\Users\<name>\...); win_eventlog_query reads any channel the account running Hermes may read, so an unelevated account gets access-denied on Security and some operational logs while an elevated Hermes can return Security-log records (logon events with account names and source addresses). It never requests elevation, never writes to, clears or configures a log, and has no Python dependencies, child processes, network calls, file writes or self-update. Windows only.

Yicheng-MarkToolsv0.1.0Updated 7 days ago

About

Answer "why did this process die" on Windows: three read-only tools over the Windows Event Log. win_crash_events reads Application Error / Application Hang / Windows Error Reporting / .NET Runtime records for a time window, decodes the exception code (0xC0000005 -> ACCESS_VIOLATION and the other codes a debugger would name), reports the faulting module, its path and the fault offset, and groups repeated crashes by app and signature. Records are classified by provider as well as by event id, so unrelated providers that reuse ids 1000 and 1001 for informational events are not reported as crashes. win_shutdown_events reads the System log for Kernel-Power 41 unexpected restarts, bugchecks decoded to names (0x9F -> DRIVER_POWER_STATE_FAILURE, accepting the decimal form some providers emit), EventLog 6008 dirty shutdowns, 1074 operator-initiated restarts with the process and comment, and the 6005/6006/12/13 start and stop markers. win_eventlog_query is the general case: any channel, filtered by event ids, levels, an exact provider name or a payload substring, returning structured records instead of raw XML. Queries read newest-first and stop at the window start or at a 5000-record scan cap; a call returns at most 200 records and marks truncated payload text. Channel names, provider names and numeric filters are validated before anything is interpolated into the query. Disclosure - this plugin reads the local Windows event logs and hands what it finds to the model: application and faulting-module paths, exception codes and offsets, report ids, service names and provider message text, which routinely includes user and machine paths (C:\Users\<name>\...). Treat tool output like any other local diagnostic dump when sharing it. It reads only the channels the current account may read - Security and some operational logs answer with an access-denied error rather than any attempt to obtain elevation - and it never writes to a log, clears one or configures a channel. Stdlib-only (ctypes wevtapi plus xml.etree): no Python dependencies, no child processes, no network calls, no file writes, no self-update. Windows only. Disclosure — reads the local Windows event logs and hands what it finds to the model: application and faulting-module paths, exception codes and offsets, report ids, service names and provider message text, which routinely include user and machine paths (C:\Users\<name>\...); win_eventlog_query reads any channel the account running Hermes may read, so an unelevated account gets access-denied on Security and some operational logs while an elevated Hermes can return Security-log records (logon events with account names and source addresses). It never requests elevation, never writes to, clears or configures a log, and has no Python dependencies, child processes, network calls, file writes or self-update. Windows only.

hermes-windows-eventlog

Ask Windows why the process died, from inside Hermes.

Three read-only tools over the Windows Event Log:

| Tool | Answers |
|---|---|
| win_crash_events | Application Error / Application Hang / Windows Error Reporting / .NET Runtime records in a window, with the faulting module and a decoded exception code, plus groups that collapse repeated crashes of the same app and signature |
| win_shutdown_events | Kernel-Power 41 unexpected restarts, bugchecks decoded to names, EventLog 6008 dirty shutdowns, 1074 operator restarts with the process and comment, and the 6005/6006/12/13 start/stop markers |
| win_eventlog_query | Any channel with filters (event ids, levels, exact provider, substring) returning structured records instead of raw XML |

win_crash_events({"hours": 24, "process": "hermes"})
win_crash_events({"hours": 168, "kinds": ["application_error"]})
win_shutdown_events({"hours": 720})
win_eventlog_query({"channel": "System", "event_ids": [7031, 7034], "contains": "gateway"})

Why this exists

Hermes runs on Windows as a desktop app, a gateway and a pile of child processes. When one
of them disappears, the exit is silent: the session is gone, the log line never arrived,
and hermes came back with a new process and no explanation. Windows itself keeps the
record — the Application log has the faulting module, offset and exception code for every
crash and hang, and the System log has the machine-level story (unexpected restart,
bugcheck, operator shutdown) — but it is XML, it is spread over several providers, and the
interesting parts are codes like 0xC0000005 and 0x0000009F.

This plugin turns that into three tool calls. The agent gets the same evidence a debugger
would start from, without shelling out to PowerShell and without a human reading event
viewer.

Two facts drove the design:

  • Crash evidence is not discoverable by file. The Windows Error Reporting folders under
    ProgramData are ACL'd to the machine's administrators: on a normal user's machine the
    report directories can be listed but Report.wer inside them cannot be read. The event
    log record of the same crash is readable, and it carries the signature (app, version,
    bucket, event name). So the plugin reads the log, not the report files.
  • Not every EventID 1000 is a crash. Application-specific providers reuse the ids
    1000 and 1001 for informational events. Records are classified by provider as well as id;
    anything that is not the real crash family is dropped instead of being reported as a
    crash with empty fields.

Requirements

  • Windows 10 or 11.
  • Hermes 0.21.4 or newer.
  • Nothing else: stdlib only (ctypes + xml.etree), no Python dependencies, no child
    processes, no network calls.

Install

git clone --depth 1 https://github.com/Yicheng-Mark/hermes-windows-plugins /tmp/hermes-windows-plugins
cp -r /tmp/hermes-windows-plugins/windows-eventlog ~/.hermes/plugins/hermes-windows-eventlog
hermes plugins enable hermes-windows-eventlog

Or from the catalog: hermes plugins install hermes-windows-eventlog.

What the output looks like

A real record from a machine that had a leak-detector event and a chain of crashpad
reports, win_crash_events({"hours": 72}):

{
  "window_hours": 72.0,
  "matched": 5,
  "scanned": 54,
  "events": [
    {
      "kind": "wer",
      "time": "2026-10-20T03:00:08.013002Z",
      "app": "ego.exe",
      "app_version": "0.6.0.12",
      "event_name": "RADAR_PRE_LEAK_64",
      "bucket": "2219081324101382324",
      "report_id": "51a7d1d4-a23d-4599-93b5-ea257210ac15",
      "event_id": 1001,
      "provider": "Windows Error Reporting",
      "level": "information"
    }
  ],
  "groups": [
    {"app": "MicrosoftEdgeUpdate.exe", "signature": "crashpad_log", "count": 4,
     "first": "2026-10-19T14:11:34.004523Z", "last": "2026-10-20T00:15:30.905145Z",
     "modules": []}
  ],
  "notes": ["scanned 54 Application events in the last 72h"]
}

An Application Error record carries module, module_path, offset and a decoded
exception such as 0xC0000005 (ACCESS_VIOLATION); a Data Protection-style crash shows
0xE0434352 (CLR_EXCEPTION) for a managed throw. win_shutdown_events decodes bugcheck
codes (159 from the provider or 0x0000009f from WER both land on
DRIVER_POWER_STATE_FAILURE).

Behaviour and limits

  • Newest first. Queries stop as soon as the window start is crossed or the scan cap
    (5000 records) is hit, so a 30-day window over a busy log stays bounded.
  • limit caps returned records at 200; long payload text is truncated and marked.
  • Times are UTC, ISO 8601 with microseconds.
  • Channel names are validated against a plain-name pattern, provider names must be exact
    and quote-free, and numeric filters are parsed as integers. Nothing from the model is
    interpolated into the query without those checks.
  • Channels that need elevation (Security on a normal account) return
    {"ok": false, "code": 5, "error": "..."} instead of an exception.
  • Provider data field names differ in casing between Windows builds (Faulting Module Name vs Faulting module name), so lookup is case-insensitive.
  • Provider-supplied text is whatever the machine's locale produced (a reason string may be
    重启). The plugin does not translate or parse those strings.

What this plugin does not do

  • It is not a debugger and not a minidump reader. It reports what Windows recorded.
  • It does not read Windows Error Reporting report files (they are not readable by a normal
    account) and does not elevate to get them.
  • It does not write to a log, clear a log, configure a channel or start/stop a service.

Disclosure

  • Read-only. The plugin calls EvtQuery / EvtNext / EvtRender / EvtClose through
    ctypes. No writes, no log clearing, no channel configuration.
  • No child processes, no network, no files written. The only file system work is the
    Python import system loading the plugin itself.
  • What reaches the model. Whatever the queried events contain: application names and
    paths, faulting module paths, exception codes and offsets, report ids and buckets,
    service names, and provider-supplied message text. Event payloads routinely include user
    or machine paths (C:\Users\<name>\...). Treat tool output like any other local
    diagnostic output when sharing it.
  • Scope. A process can read the logs its account is allowed to read. With a standard
    account that is Application, System and the per-user operational channels; elevation is
    required for Security and some system operational logs, and the plugin reports that as
    an error rather than trying to obtain it.
  • Windows only. On other platforms the tools return a structured error.

Tests

python -m pytest tests -q

30 tests: parsing, classification, filters, limits and error paths run against a fake event
source on every platform; the live suite exercises the real Application/System logs on
Windows (skipped elsewhere). See VALIDATION.md for the run that was done
at this commit.

License

MIT.

README from the reviewed commit 5bad3fb; it updates when the author re-pins.

More by Yicheng-Mark

14 plugins by Yicheng-Mark in the catalog.

Yicheng-Markhermes-dpapi-secretsResolve Hermes environment variables at startup from a single secret file encrypted with the Windows Data Protection API, instead of keeping provider keys in plaintext in .env or auth.json. Config is secrets.dpapi.map, an ENV_VAR -> store-name mapping (bare names or dpapi:// names) plus an optional file path (default <HERMES_HOME>/secrets.dpapi). The file is one DPAPI blob keyed by the current Windows user profile, so it is unreadable by another Windows account and worthless off the machine; it is written by the bundled CLI (python -m hermes_dpapi_secrets set NAME, value read from a prompt or stdin, never from argv) and only read at runtime. Mapped names that are missing or empty are skipped with a warning; a file that is not ours, a blob this user cannot decrypt, or a payload that is not a flat string map is reported as an error with nothing injected. Disclosure - this source decrypts a file for every Hermes process that enables it: resolved values become environment variables of the Hermes process and its children, exactly like .env entries, so anything that can read that environment can see them. User-scope encryption only (the machine-wide flag is never used and is not exposed), and the at-rest protection is a bar for backups, cloud sync and dotfiles, not a boundary against code already running as the same user. Stdlib-only (ctypes): no Python dependencies, no subprocesses, no network calls, no self-update. Windows only. Applying fetched values stays the orchestrator's job; the plugin never writes os.environ itself. Disclosure — decrypts a DPAPI-encrypted secret file (default <HERMES_HOME>/secrets.dpapi) at startup for every Hermes process that enables it, and the resolved values become environment variables of Hermes and its child processes like `.env` entries; the encryption is per Windows user and protects backups, cloud sync and copied files, not against other code running as the same user. Local only: no network calls, no subprocesses.CommunitytoolsYicheng-Markhermes-wincredResolve Hermes environment variables from the current user's Windows Credential Manager at startup, instead of keeping provider keys in plaintext in .env or auth.json. Config is secrets.wincred.map, an ENV_VAR -> generic-credential-target mapping (bare names or wincred:// names); each value is read with CredReadW in the current user's vault and handed to the secret-source orchestrator, which applies it with the usual precedence, provenance and conflict rules. Values may be UTF-8 or UTF-16LE (cmdkey and the Credential UI write UTF-16); anything that does not decode to text, or that contains interior NULs, is skipped with a warning instead of being injected as a mangled key. Disclosure - this source reads your Windows Credential Manager: only the generic credentials named in the map, with no enumeration of the vault. Resolved values become environment variables of the Hermes process and its children, exactly like .env entries, so anything that can read that environment can see them. The package is read-only and stdlib-only (ctypes): it never creates, updates or deletes credentials, writes no files, starts no subprocesses, makes no network calls and does not self-update. Windows only. Credential Manager is user-scoped, so this raises the at-rest bar for backups, cloud sync and dotfiles; it is not a boundary against code already running as the same user. Applying fetched values stays the orchestrator's job; the plugin never writes os.environ itself. Disclosure — when enabled, reads only the Windows Credential Manager generic credentials named in secrets.wincred.map (no vault enumeration) and injects them as environment variables of the Hermes process and its children, exactly like .env entries; read-only, stdlib ctypes, no subprocesses, no network, no file writes; user-scoped at-rest protection, not a boundary against code already running as the same user.CommunitytoolsYicheng-Markhermes-windows-appsAnswer "what is installed on this Windows machine": the software inventory behind Apps & Features, swept straight from the four Uninstall registry keys (the machine's 64-bit and 32-bit views plus the user's), so a program is found even when the Settings page hides it - a per-user install that never shows on the machine page came from the user view on the writing machine. Each row carries version, publisher, size in MB, install date and which view it lives in; the same DisplayName in two views is a 32/64-bit dual install, marked instead of listed twice. Entries the publisher asked to hide (runtime packages, update rollups) are filtered out unless asked for. Details one program with every field its publisher wrote and a reading of the uninstall command (MSI product code, setup executable, appx, script) so the next step is known before anything is run; the install folder is checked against disk. Use it for "what did that installer actually register", for inventory before a cleanup, and for "which of the two installs does uninstalling remove". Read-only: registry keys are enumerated and uninstall strings are reported, never run; no network, no file writes, no elevation. The sweep is pure registry - 102 entries in seven milliseconds on the writing machine, the whole table in ten, no PowerShell, no WMI. The plugin declares no Python dependencies. Windows only. Disclosure - the tools report program names, versions, publishers, sizes, install folders and uninstall commands, and what is hidden from Apps & Features, to the model; treat the output like the machine's own Apps & Features page.CommunitytoolsYicheng-Markhermes-windows-devicesAnswer "which device on this Windows machine is unhappy, and what does it need" on Windows: win_device_table lists every present PnP device with its class (System, Net, HIDClass, Display), its status, its manufacturer, its stable device id, and its problem code in words - 28 is one with no driver installed, 10 one that cannot start, 43 one Windows stopped after it reported a failure, 22 one the person switched off on purpose and is not broken - filtering by name or id substring, by class, or to the devices that need attention; devices no longer plugged in are history, left out unless asked for. win_device_detail takes one device by id or name substring and adds the driver behind it from the signed-driver table - name, version, date, provider, signer, INF file, signed or not - or the absence of one, which for code 28 is the same fact said twice; a disabled device gets a note saying it is on purpose, not an alarm. Use it for the Device Manager yellow-mark question without opening Device Manager, for finding what an uninstalled program left behind, and for what changed after an update. Read-only: nothing is enabled, disabled, updated or reinstalled. Both answers come from CIM (Win32_PnPEntity and Win32_PnPSignedDriver, joined by device id), whose property names, enum values and ids do not change with the display language; the human-facing device names localize and are carried through as data, never parsed. The plugin declares no Python dependencies; there is no network call and no file write. Windows only. Disclosure — the tools report device names, manufacturers, device ids and driver versions to the model, and those reveal the machine's hardware inventory and what is plugged into it; treat the output like a Device Manager window opened on their machine.Communitytools