Adds a 'web_extract shows a stale page' section to the bundled
hermes-agent skill's troubleshooting reference: the 20-minute TTL
symptom, the automatic never-cache carveouts (localhost/private/LAN,
website_blocklist, rescue/failed responses), the web.cache_exempt_hosts
recipe for staging/tunnel sites, and the TTL/disable fallbacks.
`useTheme`, the accent override, the retint helper and the OKLCH math reached
the SDK without reaching this page, which still documented `THEMES_AREA` alone.
Registering a theme only lists it in the picker, so the natural reading was that
plugins cannot switch themes at all — and the one person who tried concluded
exactly that and patched the app instead.
Documents the selection half: the hook for components, `requestTheme` for
callbacks with no component around them, and a Theming row in the export table.
The agent-facing reference gets the same note, since it never covered themes.
The routing table listed 18 topics and had nothing to say about the rest of
the product, so an agent asked how to get bots to talk to each other answered
that it could not — while user-guide/bot-mode documented four ways to do it.
Point the catch-all at the published index, which is generated from the docs
tree on every build and so cannot fall behind the feature set. website/ is
never packaged, so the URL is the only complete self-knowledge a running
Hermes has; curl covers sessions where the web tools are disabled.
The Bots roster highlight and the Routines (Cronjobs) tile were keyed off
host.state.profile — the gateway socket's home. Tab/tile focus moves without
swapping the socket, so opening one bot's chat while the socket was homed on
another highlighted the wrong bot and showed the wrong bot's cronjobs
(community report: Newsanalyst chat open, Hermes highlighted).
- sdk: new host.state.focusedSessionProfile — owner profile of the focused
chat, resolved from the focused stored session's row stamp via
rememberedSessionProfile() (same ladder as remembered navigation and the
HUD), with the gateway profile as the draft/uncached fallback.
- hermes-bots: $focusedBotProfile = focusedSessionProfile || profile
(feature-detected; older desktops keep prior behavior). BotRow highlight,
RoutinesPane scope, and the $selectedBot tracker use it. Turn-busy 'work'
mood stays keyed to the socket-home profile (only it can be mid-turn).
- tests: SDK atom behavior (vitest) + plugin source-shape suite; prewarm
harness stubs gain the new atom.
- docs: SDK page + hermes-agent skill reference list the new atom.
Extends ctx.os.notify (the curated plugin OS door from #78685) with icon,
action buttons, and a serializable `activate` target. Body/action clicks
focus the window and navigate to the plugin's screen; activation paths
share one resolver (hermes-open-target.ts) with hermes:// OS deep links,
so `hermes://index-network/intent/1`, `/index-network/intent/1`, and
{ path, params } all land on the same hash-router route. Approval
notifications keep their existing session-scoped channel.
Salvaged from PR #84192 by @serefyarar (net diff of the PR branch applied
onto current main; branch carried merge commits so a single authored
commit preserves attribution).
Follow-up to #89382: the operator runbook, bundled-skill docs page, and the
bundled SKILL.md now cover the organizer-scoped lookup flag and note that
/meet/ short URLs require it while webhook jobs derive the organizer
automatically.
The transcript becomes a contribution area (transcript.directives). A plugin
registers a named directive and the model addresses it by emitting
::name{key="value"} as its own paragraph; that leaf renders as the plugin's
component, wrapped in the contribution error boundary. Unclaimed or malformed
directives stay plain prose, so nothing changes for text that merely looks
like a directive (std::vector) or for users with the plugin disabled.
Core ships ::preview{file="..."} as the reference consumer (the existing
preview-attachment card), the desktop platform hint teaches the model the
syntax, and the SDK exports the area + types so runtime plugin.js files get
the surface through the normal plugins API.
Independent second-pass review found three gaps:
- focusedUsage is null | UsageStats, not Partial — ClientSessionState.usage
is the full type (app/types.ts) and its only write site seeds the four
required fields before merging (gateway-event.ts). The earlier Partial
annotation traced the wrong type (SessionRuntimeInfo, an RPC payload).
Comment now names the genuinely optional fields instead.
- The tile-focus contract test never seeded $sessionTiles/$sessionStates,
so it proved focusedStoredSessionId follows a tile but could not
distinguish focusedSessionId/focusedUsage working from broken. Seed a
bound runtime with distinct usage and assert both readout atoms move.
- The two public host.state references (website docs + bundled skill
reference) enumerated the old six atoms; plugin authors would never
discover the new ones. Both lists updated.
tsc/eslint/vitest green (4/4).
Gate composer submit and plugin host busy on the target session slice, not a leftover foreground busyRef. Staff can keep typing while a worker session is running.
Includes the follow-up test that submit uses the target session busy flag.
Plugins can now read host.state.busy and host.state.awaitingResponse
for the focused chat. These follow the same session slice the chat pane
uses, so a draft falls back to the global flags and a background turn
does not leak.
Live complex-action QA on a real KDE desktop (kcalc + kate multi-app
flows) found two dispatch gaps:
1. Wrong-window input reported as success. Input actions deliver to the
backend's sticky target (last capture/focus_app); the app= argument
models routinely pass on the input call itself was silently dropped.
Proven live: with kcalc sticky, type(text='777', app='kate') returned
ok:true and typed 777 INTO KCALC. New guard: provable mismatch
(both names known, neither substring of the other — list_windows
names are localized/variant) refuses with input_target_mismatch and
a one-call fix instruction. Unknown current target fails open so
legacy no-app flows are untouched.
2. Near-miss unknown actions were dead ends. A model emitting 'hotkey'
got a bare unknown-action error. Suggestion map now names the real
action ('did you mean key?') without aliasing — we never repair bad
model output, we just point at the schema.
Also documents the verified-lost-keystroke rung in the computer-use
skill: KTextEditor (Kate/KWrite) discards synthetic X keystrokes at the
toolkit level — foreground type reports ok but AX shows nothing arrived,
and a raw XTest control fails identically outside our stack. Guidance:
after one verified-lost round trip, switch to file/DBus I/O instead of
looping the ladder.
Live proof on the fixed build: mismatch refused, kcalc display clean,
same call after capture(app=kate) succeeds, 'hotkey' suggests 'key'.
11 new tests; 158 sibling tests green.
Live-tested against the real cua-driver 0.19.3 binary (Linux x86_64):
- bounded serve flags corrected: the daemon accepts
--session-policy/--approve-session-policy, not the docs'
--capability-manifest names (which it rejects). Verified end-to-end:
a bounded daemon with a real policy file starts and reports running.
- browser-approve verified real but interactive-only (refuses without a
TTY) and its token is a legacy compatibility path disabled by default
on current drivers (per the live browser_prepare schema). Kept as a
passthrough; no longer presented as the primary route.
- NEW primary standard-mode route, verified live: launch the runtime
with cua-driver's trusted-launcher grant. config opt-in
computer_use.grant_existing_profile: true appends
--grant existing-profile to the standard-mode MCP spawn (MCP
initialize verified accepting the flag). Default false = attachment
keeps failing closed. Never applied to bounded/unrestricted daemons.
- Skill, system prompt, tool schema, and docs updated to the verified
ladder: config grant > bounded manifest > YOLO; token = legacy.
Completes the typed cua_browser_* route (PR #74166 lineage) with the
authorization surface that makes existing-profile attachment and
repeatable bounded automation reachable by real users:
- hermes computer-use browser-approve: CLI passthrough that mints
cua-driver's five-minute single-use attachment token for one exact
(pid, window_id). The user, never the model, is the token source.
- approval_token passthrough on cua_browser_prepare (schema + dispatch +
browser_route), forwarded only for existing_profile and only as a
non-empty string.
- computer_use.permission_mode: bounded + capability_manifest config:
private per-session embedded daemon launched with
--capability-manifest/--approve-capability-manifest; missing manifest
fails loudly. 'unrestricted' is deliberately NOT a config value —
it stays bound to the explicit per-session YOLO toggle.
- Skill + system-prompt + docs guidance for the three authorization
rungs and the isolated-profile-first default.
E2E-verified against a temp HERMES_HOME: real config resolution to
bounded, loud failure without a manifest, real argparse path driving a
fake cua-driver binary, standard default preserved.
Box cloud content management via the official @box/cli through the
terminal tool: files, folders, sharing, search, metadata, Box AI,
Hubs, bulk operations, webhooks, and a REST fallback via box request.
OAuth-only auth; SKILL.md routes to ten scoped reference files.
Salvaged from PR #52107 by @iskysun96.
Adds a bundled productivity skill that lets Hermes organize the user's own
session library conversationally: find sessions by topic via session_search,
summarize goals/decisions from bookends, rename them meaningfully, propose
archives/prunes with a mandatory plan-first + dry-run discipline, and split
requests into parallel workstreams via delegate_task.
Inspired by Perplexity Computer's session management by prompt (changelog
07/27/26): find/summarize past sessions, fork focused follow-ups, rename,
pin/archive with plan-first confirmation, and fan one request out into
parallel per-task sessions.
omo's ultimate-browsing engine added a 'surrogate retrieval tier' (PR #6662):
when a page fetch is blocked by a WAF/paywall/rate-limit, it falls back to
third-party copies (Wayback, archive.today, Jina Reader) with strict
provenance labeling and validators that reject fake successes (dead Google
Cache interstitials, AMP redirect stubs, rate-limit bodies).
Hermes adaptation: a bundled research skill + stdlib-only script instead of
a Python sub-engine — zero core-tool footprint, per the footprint ladder.
Clean-room implementation (their repo is Sustainable Use License; nothing
copied), keeping the good ideas: provenance contract (snapshot vs live),
body validation over status codes, domain rotation for archive.today,
API-first pivot guidance, and explicit skip of proxy relays (MITM).
E2E tested: recovered a real 486KB Wayback snapshot with timestamp;
validators reject redirect stubs, interstitial titles, and sub-floor bodies.
On keyring-less headless Linux (VPS, containers, no dbus session),
'gh auth login --with-token' can block indefinitely waiting on a
secret-service keyring -- even with --insecure-storage, with no output.
Hit live on a headless x86_64 VPS (gh 2.97.0): the documented device
flow succeeded up to the token, then --with-token hung twice.
- Add a timeout guard to the device-flow polling loop so the hang is
detected instead of silently stalling the login.
- Document the proven fallback: write ~/.config/gh/hosts.yml directly
(chmod 600) and run 'gh auth setup-git' -- both read the file store
without touching the keyring.
Two Windows agent-loop friction fixes:
1. tools/mcp_tool.py (#56536): shutil.which(cmd, path=env_path) reads
executable extensions from the PARENT process PATHEXT, not the MCP
subprocess env — a stdio MCP config supplying both PATH and PATHEXT
could fail to resolve a command its own env can locate, and startup
then got a bare command name. On Windows, when the first which() call
misses and the config env carries PATHEXT (any key casing), retry the
resolution with the config's PATHEXT temporarily applied.
2. skills/ + optional-skills/ (#50606): 42 SKILL.md files that declare
platforms: [.., windows] used python3 in their command examples.
python3 does not exist on native Windows (the toolchain probe in the
system prompt reports python3=missing), so every copy-pasted example
burned a failed agent turn before self-correction. Replaced the
command word python3 -> python (python3-config / python3.x version
strings untouched). python is the spelling that exists in every
Hermes-managed environment (Windows native, uv-managed venvs on all
three OSes); agents on POSIX hosts additionally see the probed
toolchain line and adapt either way.
* fix: warn agents off driving interactive console TUIs via pty on Windows
Driving 'gh auth login' (and other survey-style console TUIs) through a
pty background process on Windows silently hangs: these programs read
Win32 console key events via ReadConsoleInput, not the stdin byte
stream, so Enter keypresses submitted over process stdin never register.
The agent-visible symptom is a prompt frozen at 'Press Enter to open
browser...' while the user sees nothing, and a turn interrupt then kills
the process, invalidating any device code the user already entered on
github.com.
Two guidance fixes, both proven in a live session on Windows 10:
- agent/prompt_builder.py: extend _WINDOWS_BASH_SHELL_HINT to steer
agents toward non-interactive paths (flags, --with-token, config
files, curl-polled OAuth device flow) instead of answering console
prompts programmatically.
- skills/github/github-auth: document the pitfall and add the manual
OAuth device-flow procedure (curl against gh's public client_id,
poll for the token, finish with 'gh auth login --with-token'), which
succeeded first try after two interactive attempts hung.
* fix: send CRLF for Enter on Windows PTY submit; correct root cause in guidance
Review feedback (helix4u) was right on both counts:
1. Root cause correction. gh's 'Press Enter to open browser' prompt is
waitForEnter -> bufio.Scanner reading stdin, not a survey/console-API
prompt. The real bug is ours: submit_stdin appended a bare \n, and
through pywinpty/ConPTY a lone \n is not delivered as a line
terminator, so the child's blocking line read never returns. Verified
empirically against pywinpty 2.0.15 with a readline() child:
\n -> hang, \r -> line delivered, \r\n -> line delivered.
Fix: submit_stdin now appends \r\n for Windows PTY sessions (POSIX
PTYs and Popen pipes keep \n). Windows-only regression tests cover
the PTY and pipe branches.
2. Prompt hint rewritten: instead of claiming Windows console TUIs
cannot be driven, it now says to use process(submit) rather than raw
writes with bare \n, and to prefer non-interactive paths when a CLI
offers one.
3. Skill device flow rewritten as an executable script: parses the
device-code response, polls per the returned interval, handles
authorization_pending / slow_down (+5s per GitHub docs) /
expired_token / access_denied / unexpected responses, pipes the token
straight into gh without echoing it, and drops the undocumented
workflow scope (repo,read:org,gist is the documented minimum for
gh auth login --with-token). The pitfall note is narrowed to the
reproduced condition.
Adds the comment-based hotspot convention (no new primitives) across three
guidance surfaces:
- KANBAN_GUIDANCE worker lifecycle: new step 7 — when a file keeps colliding
with siblings or appears in other cards' recent comments, leave a
'hotspot: <path> — <reason>' kanban_comment and repeat it in completion
metadata so the orchestrator can decompose the file first.
- kanban.md (en + zh-Hans): 'Collision hotspots in parallel campaigns'
subsection — the convention, the orchestrator response (2+ flags on one
path => dedicated decomposition card before queuing more work touching
it), and the cross-link to merge-reconciler for conflicts that already
happened.
- merge-reconciler SKILL.md Pitfalls: repeated conflicts on the same file
across rounds are a hotspot signal — flag for decomposition rather than
serially reconciling.
Live-verified: guidance renders once via real import (6152 chars); hotspot
comment round-trips through add_comment -> list_comments -> worker context
on an isolated HERMES_KANBAN_DB; kanban tools, review-surfaces, and
merge-reconciler skill tests green (45 passed).
Teach the kanban reviewer to vary its inspection lens per review round
instead of repeating the same framing: round 1 reads the artifact cold
before the implementer narrative, round 2 checks out and empirically
executes the work, round 3+ audits strictly against the original
acceptance criteria and every prior request_changes item. The round is
derived from the changes_requested entries already visible in the
reviewer's worker context (live-verified against build_worker_context
across two real request_review/request_changes rounds on an isolated
board). Also adds a lens-variation note for parallel delegate_task
review fan-outs. Contract test updated with section order and lens
assertions.
Adds skills/autonomous-ai-agents/merge-reconciler — a bundled skill teaching
a neutral third-party agent to resolve git merge conflicts between two
agents' branches: gather both diffs + intents, classify each hunk
(disjoint-intent / same-question-different-answer / superseded), resolve
under an impartiality contract, verify, and hand back a per-hunk summary.
Procedure was live-tested end-to-end against a real conflict fixture.
Includes contract tests (tests/skills/test_merge_reconciler_skill.py) and a
kanban docs cross-reference (en + zh-Hans): assign a third neutral profile a
reconciliation card with both conflicted cards as parents.
New tests/skills/test_authoring_standards.py parametrizes every bundled +
optional SKILL.md (1148 checks) against the mechanically-verifiable subset
of the hardline standards:
- required frontmatter fields (name/description/version/author/license/
platforms) + tags
- frontmatter name == directory name
- description <= 60 chars, ends with period, no marketing words
- related_skills resolve in-repo
- no machine-local paths
- <= 100k chars
Grandfather dict for legacy debt ships EMPTY — all pre-existing violations
fixed in this PR:
- 13 frontmatter names canonicalized to their directory names (the install
identifier); all related_skills references updated (comfyui -> stable-
diffusion). Fixes the class behind PR #42788's report; also fixes
here.now's invalid dot-name.
- optional-skills/devops/cli -> inference-sh-cli (dir was the generic
'cli'; fm name was right) incl. docs pages (en + zh-Hans), catalog row,
sidebar entry.
- pytorch-fsdp: 157k generated 'Quick Reference' dump moved to
references/common-patterns.md; SKILL.md 159k -> 2.5k with a pointer.
- research-paper-writing: 31.7k Phase 5 drafting section moved to
references/phase5-paper-drafting.md; SKILL.md 103k -> 71k.
Docs regenerated with scope discipline.
- description 210 -> 57 chars; author credits Ben Barclay (benbarclay) first
- optional-skills/creative/ (marketing vertical, narrowest audience of
the batch)
- dropped phantom 'image-generation-workflow' ref; visuals via the
image_generate tool
- honest handoff language: platforms without connectors end at approved
drafts marked handed-off, never claimed as published
- tests (10) incl. phantom-ref and honest-handoff guards
- docs regen scoped: per-skill page + one catalog row + one sidebar line
Skill polish:
- description 208 -> 57 chars; author credits Ben Barclay (benbarclay) first
- connector framing (google-workspace, obsidian, notion, email-inbox-triage)
- modern section order; boilerplate folded into step-local rules
Blueprint wiring (completes the batch's recipe integration):
- weekly-review blueprint loads weekly-review-planning; prompt follows the
skill's seven-section shape, drafts-only
- morning-brief blueprint loads google-workspace; prompt points at
references/daily-brief.md when connected
- important-mail blueprint loads email-inbox-triage
- blueprints index regenerated
Tests: 13 skill tests + two catalog invariants (every blueprint skills=
entry resolves to a real bundled skill; the four task blueprints are wired
to their procedure skills). 32 green across both files.
The salvaged fix covered run_workflow.py and hardware_check.py. The same
locale-default read of user-authored workflow JSON exists in five sibling
scripts (auto_fix_deps, check_deps, extract_schema, health_check,
run_batch) — same bug class, same utf-8-sig fix. Invariant test extended
to pin all nine read sites.
The pdf half of the original PR is superseded: those scripts were
replaced wholesale by the clean-room rewrite (#81890), which ships
UTF-8-explicit I/O enforced by its own invariant test.
The bundled comfyui and pdf skills read and write text files with the
locale-default codec. Both declare platforms: [linux, macos, windows], so
these paths run on hosts where that codec is not UTF-8 (cp1252 on US
Windows, cp936 on Chinese Windows, ASCII under LC_ALL=C).
Readers (the live bugs):
- run_workflow.py load_schema() and the main() workflow read parse
user-authored JSON. A non-ASCII label crashes json.load with
UnicodeDecodeError under a non-UTF-8 locale, and a file saved from a
Windows GUI editor carries a UTF-8 BOM that json.load rejects with
JSONDecodeError. Both are read as utf-8-sig, which is BOM-tolerant and
identical to utf-8 on BOM-less input. This differs from adecb0d1a,
which used plain utf-8 for the pdf form JSON; those payloads are
agent-authored and BOM-free by construction, these are not.
- hardware_check.py reads /proc/version and /proc/meminfo. Both are
Linux-gated so Windows never reaches them, but the C locale defaults to
ASCII, so they pin plain utf-8. No BOM is possible on /proc.
Writers (not currently broken):
- extract_form_structure.py and extract_form_field_info.py write their
JSON with json.dump, whose default ensure_ascii=True keeps the bytes
pure ASCII. Pinned anyway because the codec is the writer's contract,
not a property of what the caller happens to dump.
wf_path.open() is a Path.open() site that check-windows-footguns.py
deliberately does not flag (per the rule comment: "Path.open() is ALSO
affected ... and can be audited separately"). It is fixed here because it
is the same bug 156 lines from a site the checker does flag, and line 623
of the same file already uses read_text(encoding="utf-8").
Adds tests/skills/test_comfyui_skill.py with contract assertions plus two
live regressions that run load_schema in a child interpreter under
LC_ALL=C with PYTHONUTF8=0, and extends the office skill tests with writer
contract assertions. All 8 new tests fail without this change.
Note that pyproject.toml exempts skills/** from ruff PLW1514
(unspecified-encoding) because skill scripts are partly user-authored.
This change does not touch that exemption; the sites are fixed by hand,
the same way adecb0d1a did.
The bundled docx, xlsx, powerpoint, and pdf skills were adapted from
Anthropic's document skills and carried their proprietary LICENSE.txt
(no derivatives, no redistribution). Flagged as critical license
findings by the SkillEvaluator Tier 1 scan of our skill tree.
This replaces all four with clean-room rewrites:
- Authored from scratch against library knowledge only (python-docx,
openpyxl, python-pptx, pypdf/reportlab/pdfplumber — all MIT/BSD) by
isolated subagents given functional specs, with an explicit
prohibition on reading the prior skill content or anthropics/skills;
session transcripts retained as provenance evidence.
- MIT licensed (LICENSE file per skill), author: Nous Research.
- Each skill: SKILL.md to house standards + argparse helper scripts
with UTF-8-explicit I/O + its own e2e pytest suite (fixtures built
on the fly, non-ASCII round-trips run under LC_ALL=C).
- All four pass SkillEvaluator Tier 1 pii+unicode+lint 3/3.
tests/skills/test_office_document_skills.py rewritten against the new
contracts: MIT/no-Anthropic-text invariants, scripts documented in
SKILL.md, argparse CLI shape, and a no-locale-default-open() check
(which caught and fixed a real gap: pdfplumber text reads are fine,
but the invariant scan now guards every future script).
Docs pages regenerated for the four skills (scoped; unrelated
generator drift excluded).
Honest capability deltas vs the old versions are documented per
SKILL.md (e.g. tracked-changes accept/reject and OOXML XSD validation
are not reimplemented; form flattening limits stated).
Findings from scanning skills/ + optional-skills/ with NVIDIA
SkillEvaluator's deterministic Tier 1 checks (PII/secrets, unicode
smuggling, script lint):
- pixel-art, pokemon-player: remove hardcoded /home/teknium/ personal
paths (use ~ / portable phrasing); pokemon-player no longer claims
machine-specific state as fact
- kanban-video-orchestrator: replace <path> angle-bracket token in
frontmatter credits (flagged as XML-in-frontmatter prompt injection)
- comfyui, hermes-agent, unsloth, 1password, actual-setup: rephrase
placeholder secrets so they no longer pattern-match real credentials
(your-* placeholder convention, comment markers, {env:...} form)
- docker-management, pytorch-lightning: drop user:pass@ from example
connection strings (env/secret-manager guidance instead)
- evm: break up Keccak round constant that Luhn-validates as a credit
card number (digit-group underscores, value unchanged)
All targeted skills now pass pii+unicode+lint 3/3 except unsloth, which
retains scanner false positives only (Colab notebook IDs read as Bitcoin
addresses; an email inside a quoted upstream system prompt).
The brief is single-connector (every command comes from google-workspace),
so it ships as references/daily-brief.md with a pointer + load trigger in
SKILL.md — progressive disclosure instead of a new skill-index entry.
Contributor's procedure preserved (half-open day windows, mail-to-meeting
linking with fuzzy-match discipline, 7-section brief, bounded actions);
credit noted in the reference header. Tests (8) guard the wiring and
disciplines. Version 1.1.0 -> 1.2.0.
Rewrote from a sibling-skill routing table into a skill that carries its
own procedure, and folded in generalized rules from maintainer practice:
- full-thread reads (gh issue view --comments; newest comment = live state)
- duplicate-PR sweep (issue number + keyword variants) before any code
- design-intent check via git log -p -S alongside premise reproduction
- fix the class: sweep sibling call sites into the same PR
- sabotage run: prove the regression test fails without the fix
- open the PR immediately (PR dispatches CI; CI latency is the long pole)
- close the loop: comment the issue with the PR link
Also: description 205 -> 59 chars, author credits Ben Barclay first,
modern section order, boilerplate trimmed, tests (10) incl. a
router-pattern guard, scoped docs regen.