Commit Graph

51420 Commits

Author SHA1 Message Date
Teknium
7bc05085f0 fix(ci): docs drift handles repeated placeholders, scopes its warning filter
Review follow-ups: `providers.<id>.models.<model>.timeout_seconds` now
matches (a second <...> segment no longer ends the pattern), the YAML
warning filter is scoped with catch_warnings, and the slash-command list
drops a `hidden` filter CommandDef never had.
2026-10-11 05:09:46 -07:00
Teknium
4e8fe569e4 feat(ci): new config keys and slash commands must ship with docs
DEFAULT_CONFIG changed 183 times in 30 days and nothing checked that a new
key reached the docs; /pause (the global emergency stop) went undocumented
for two months.

scripts/ci/check_docs_drift.py lists every DEFAULT_CONFIG leaf and every
registered slash command and finds the ones the docs never mention. A key is
documented by its dotted path, a placeholder path in inline code
(`auxiliary.<task>.timeout`, `display.pet.*`), or a YAML example nesting it
under its parents; a slash command by a `/name` row in
reference/slash-commands.md.

The 132 keys undocumented today are baselined in docs_drift_baseline.json;
tests/ci/test_docs_drift.py fails only on NEW gaps, so it never blocks an
unrelated PR, and documenting a baselined key never fails. Deterministic,
stdlib + repo imports, ~1s.

/pause is documented here, so the slash-command baseline starts empty.
contributing.md says what to do when the test fails.
2026-10-11 05:09:46 -07:00
hermes-seaeye[bot]
e388385e94 fmt(js): npm run fix on merge (#136589)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-11 12:01:18 +00:00
Siddharth Balyan
c77a5be979 fix(desktop): a Rename link refreshes the account list before calling the account gone
The cached list can predate an account connected since the page loaded.
2026-10-11 17:21:08 +05:30
Siddharth Balyan
93a8a7856b test(desktop): the named-account notice links by account id; key list includes renameGone 2026-10-11 17:21:08 +05:30
Siddharth Balyan
f38da142bc fix(desktop): Rename links go by account id and open once, a started add refreshes the list
The chat notice's Rename link carries the account id; a link whose account is
gone opens nothing and says so. A docked Capabilities page takes each new
link's scope, and of two mounted pages only one acts on a link. A started add
or reconnect refreshes the account list at once.
2026-10-11 17:21:08 +05:30
Siddharth Balyan
e22e4517ad fix(desktop): Try again is disabled while its start is in flight
Two quick clicks could reach the backend's reissue before either mint
finished and start two replacement sign-ins. The button now follows the
app's pending connect.
2026-10-11 17:21:08 +05:30
Siddharth Balyan
df728fc7b4 fix(desktop): reconnect goes by account id, failed sign-ins retry, add errors show
- Reconnect addresses the account by its connection_id, never by name. An
  optimistic rename changes the row's name before the server answers, and
  another session may hold that name, so a name could repair a different
  account. The name stays display-only.
- An open operation whose targets all failed or expired lets its own
  account retry: that row's Reconnect stays enabled and the sign-in
  summary shows Try again, which resends the operation's own request.
  Every other account stays blocked until the operation ends.
- The name editor keeps the value the gateway refused with its error and
  shows the error while the field still holds that value, so a refused
  add explains why instead of hiding the reason once the user has typed.
2026-10-11 17:21:08 +05:30
Siddharth Balyan
96da586664 chore(desktop): overlay-gaps.json lists the connector account keys on top of main's file 2026-10-11 17:21:08 +05:30
Siddharth Balyan
ca4aca13e5 fix(desktop): reconnect on an unnamed account repairs that account by its id
A row's Reconnect on an account with no name sent no address, so the gateway
had nothing to repair. It now sends the account's connection_id, which
connectors.connect accepts with reconnect for exactly this case. Named rows
still go by name; an app-level reconnect is unchanged.
2026-10-11 17:21:08 +05:30
Siddharth Balyan
66f4ab7932 fix(desktop): a sign-in stays visible when the same profile is picked again
Review round 3: on a multi-connection desktop the scope selector pins
`{connectionId, profile}` while an operation started from the ambient
scope holds the bare profile name, so re-picking the same profile hid
the open sign-in. Scopes now compare by the backend they resolve to
(connection, else the active one, plus profile).
2026-10-11 17:21:08 +05:30
Siddharth Balyan
49c396d20a fix(desktop): a sign-in in one profile no longer locks another profile's accounts
Review round 2: the open-sign-in lookup matched the app only, so a
Gmail sign-in left open in one profile disabled Add another account and
Reconnect for Gmail in every other profile, and the stop-waiting path
could act on the other profile's operation. The lookup now matches the
profile scope the operation was started in.
2026-10-11 17:21:08 +05:30
Siddharth Balyan
4a4e1f765e fix(desktop): account actions act on the account the user picked
Review round 1:
- An older rename reply or rollback no longer overwrites a newer
  rename of the same account, and a refused rename reopens its editor
  only when the user has not started another edit.
- Add another account and per-row Reconnect wait while a sign-in for
  that app is open, so the sign-in controls and the browser always
  speak for the same account.
- Reconnect on an unnamed row stays unnamed instead of borrowing the
  summary account's name.
- The chat's Rename link carries the chat's profile and connection, and
  a pending rename belongs to its app and clears when the dialog closes.
- The account list scrolls inside the dialog instead of clipping.
2026-10-11 17:21:08 +05:30
Siddharth Balyan
fcd90decf3 fix(desktop): keep the locale catalogs under their size cap
The account strings grew en/fr/de/es/zh/types past the per-file line
cap the health ratchet holds them to. The connector card and account
strings now live in their own topic files (`<locale>_connector_card.ts`,
`<locale>_connector_accounts.ts`, `types_connectors.ts`), the same split
the other large topics already use, which leaves each catalog shorter
than on main. Also regenerates `locales/_keys.desktop.json` for the new
keys.
2026-10-11 17:21:08 +05:30
Siddharth Balyan
9a83171db5 feat(desktop): list, name and rename several accounts per connector
A hosted connector can now hold several accounts (gmail "home" and
"work"). The connector dialog lists every live account by its name, or
the vendor label when it has none, with Rename, Remove (confirmed, that
one account only) and Reconnect on the rows that need it. Reconnect
names the account it repairs, so nothing is deleted first. "Add another
account" asks for a name and connects under it. Accounts a reconnect
replaced fold under "N retired". The card shows the name, or "N
accounts". Rename paints at once and rolls back with the gateway's
reason when it is refused.

When the agent connects an account under a name it picked, the chat
card says "Hermes named this Gmail account work." once the account is
connected. Rename opens that account's name editor on the Connectors
tab; nothing opens by itself.
2026-10-11 17:21:08 +05:30
Siddharth Balyan
4265ddb8e1 refactor(tui): /connectors row shortcuts dispatch through a table
The letter shortcuts that act on the selected row map to handlers, and the
reconnect refusal and hint choice live beside the other account rules, so
the panel stays under the complexity target.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
cb9f6ac736 fix(tui,cli): reconnect is offered only as a repair, sign-in failures keep their reason
The /connectors panel offers c only on an account that needs repair; on a
healthy row the hint drops it and the key answers that the account is
connected. connect --reconnect on an active account says so and starts
nothing unless --force. A failed or expired sign-in shows the gateway's
detail. The docs say account is present only for a named account.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
ffe20f24ad fix(tui): Ctrl+C in /connectors closes the panel without reconnecting
Ink reports Ctrl+C as 'c' with key.ctrl set, so the panel's c shortcut
fired Reconnect on the same press the global handler used to close the
panel. Letter shortcuts now go through panelShortcut, which ignores
Ctrl and Meta chords.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
d216e425a4 fix(cli): connectors registers on its own, retired accounts can be removed
Review on ee2d14de98:
- `hermes connectors` registers in _build_cli_parser next to `mcp`, not
  from inside the mcp builder. The sessions lazy handler moves into its
  own subcommands module to keep main.py under its size cap.
- `disconnect --alias` can pick a retired account (shown by
  `accounts --all`); reconnect still never targets one.
- The connect link and its instruction flush at once, so a piped run
  shows the link before the wait.
- The two reference links in the tool-gateway guide go up two levels.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
6b7f5f8e88 feat(tui,cli): reconnect repairs the chosen account by id, retired accounts fold
The agent core now takes `connection_id` on a reconnect and marks retired
accounts `disabled`. Both surfaces use them:
- /connectors `c` and `hermes connectors connect <app> --reconnect --alias
  <name-or-label>` repair that exact account, named or unnamed. The old
  account ends retired and the replacement takes its place; nothing is
  deleted. The "name it first" refusal for unnamed rows is gone.
- Retired accounts fold behind Tab in /connectors and behind --all in the
  CLI, and cannot be picked for a reconnect.
- The accounts panel draws nothing while a prompt is open: the shared
  remove confirm renders under the floating panel and was hidden by it.
- The remove confirm title drops its own "y/N"; the prompt shows the keys.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
1705a3f47e fix(tui): /connectors calls the gateway directly and a lapsed wait claims no failure
Review round 2: drop the pass-through request helper, and when the link
deadline passes, say only that waiting ended. A late reply may still
have connected the account, and the reloaded list shows its real state.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
91d2f95117 fix(tui): /connectors yields keys to prompts and reconnects only named accounts
Review round 1:
- Remove now asks through the shared confirm prompt, and the overlay stops
  reading keys while any agent question or confirm is open, so a `y` meant
  for another prompt can no longer remove an account.
- Reconnect needs the account's name: an unnamed row would send an app-only
  request that reports success for any healthy account of that app. The row
  asks the user to name it first.
- The link wait ends at the operation's deadline and on any coded refusal
  (access lost, signed out), showing the error instead of waiting forever.
- Esc on a link reloads the list, so the new pending account shows.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
18f68923c3 refactor: keep main.py and the overlay under the code-health caps
The connectors parser registers from the mcp builder, next to the command
it sits beside in --help, so main.py does not grow. $isBlocked reads a key
list instead of a 21-term boolean, and the accounts overlay's app picker
and link view are their own components.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
0daa6d7731 feat(tui): /connectors manages named connector accounts
A /connectors overlay lists every account as app, name and status, with
unnamed accounts shown by their app label and retired ones folded behind
Tab. r renames inline, a adds another named account and waits on its link,
c reconnects one account, x removes one after a confirmation.

When the agent connects an account under a name it chose, the setup card
title carries that name and the settled transcript line tells the user
"Hermes named this <app> account <name>. Rename it anytime."

Enter on a fully typed TUI-local command now runs it: the gateway does
not know those commands, so its fuzzy completion could rank another one
first (typing /connectors ran /login).
2026-10-11 17:21:07 +05:30
Siddharth Balyan
1fad8eb3dd docs: multiple named connector accounts
Covers account names, no default account, the agent telling the user the
name it chose, connector_alias on tools and account on results, rename,
reconnect keeping the old account retired, and where to manage accounts
(desktop, TUI /connectors, hermes connectors, Nous Portal).
2026-10-11 17:21:07 +05:30
Siddharth Balyan
af530e86d6 feat(cli): hermes connectors lists, connects, renames and removes accounts
One app can now hold several named accounts. The CLI lists them (retired
ones folded behind --all), connects a new named account and waits for the
browser step, renames by name or app label, and removes one account after
a confirmation. Ctrl-C settles the open operation so the next connect of
that app is not blocked.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
08c049a0c0 chore(ci): record the manage_connections schema in the prompt surface
manage_connections gains account names, rename and a repair target by
account id; the tool schema grows by 149 tokens on every surface.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
a1e78983d6 fix(connectors): a session retry with the open account's id is no longer refused
An id-based retry omits alias by contract, but the guard compared the
open target's alias with the request's, so retrying a named account by
its id answered UNKNOWN_TARGET. Aliases are compared only when the
request carries one.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
21a4ab05ec test(connectors): repairs by id never land on another account
Three invariants, each red before the fix: a healthy unnamed account
reconnected by label mints nothing unless forced (then by id); an open
repair of one account is not reused for another; a session retry with
another account's id is refused. The RPC helper now reads only the
current call's reply from a shared transport.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
d0ef95ec50 fix(connectors): a repair by account id targets exactly that account
Review findings on the id-based repair:
- an open repair of one unnamed account was reused for another, because
  the operation lookup matched only connector and alias; it now matches
  the repaired account id too
- a session retry carrying another account's id re-minted the open
  target; it is refused like a retry naming another alias
- reconnect by label restarted a healthy unnamed account; only a repair
  a surface asked for by id skips the healthy check
- reconnect by label with force sent the label as an alias; the label
  now resolves to the account id first
2026-10-11 17:21:07 +05:30
Siddharth Balyan
ce38bf281b test(connectors): an unnamed account repairs by id; a pending link keeps its account id
Both red before the fix.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
b20d7904f8 fix(connectors): reconnecting an unnamed account repairs it instead of adding a second one
manage_connections reconnect could only address an account by alias, so
an unnamed account (addressed by its label, as rename already allows)
failed the alias format check and the only way out was a forced
reconnect that minted a second account beside the broken one. A label
now resolves to that account and the repair sends its id. status marks
retired accounts. A CONNECTION_REQUIRED result keeps the account id of
a link the user already holds, so the model can tell a waiting sign-in
from a new one.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
cd764c5ce0 feat(connectors): account rows say when an account is retired, and a repair can name one by id
The gateway marks the account a reconnect replaced as disabled; Hermes
dropped the field, so every surface showed a retired account as an
ordinary inactive one. Account rows now carry it. connectors.connect
takes connection_id with reconnect, the only way a surface can repair
an unnamed account; operation targets carry it as repair_id.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
6569c1bdde fix(connectors): Try again on a named account repairs the account it minted
A retry from the connection card re-minted with the target's alias, not
the account id the first repair recorded. If that account was renamed and
its old name given to another login while the card stayed open, Try again
restarted the other login. A repair with no explicit id now reuses the
named target's recorded connection id.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
a36fe1b926 fix(connectors): reconnecting a named account repairs that exact account
A reconnect naming an account sent only its alias, so the gateway had to
look the account up by name again. The account list the repair check
already reads carries the account's connection id; the connect request
now sends that id (and no alias) so the gateway restarts exactly that
account. A name no account has yet still goes out as an alias.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
991548a0b9 test(connectors): cover a successful rename and both review fixes
A rename through the real portal client sends PATCH with the new name
and returns the renamed row. A failed account read mints nothing on
reconnect, and a session retry for another account is refused. Both fix
tests are red with the fixes reverted.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
7b0f731c5d fix(connectors): a session retry naming another account no longer re-mints the open one
connectors.connect with an alias reissued the session's open target even
when that target is a different account of the same connector, handing
back the wrong account's login link. A retry with an alias must match
the open target's alias; one without an alias keeps today's behaviour.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
33457bc682 fix(connectors): a failed account read no longer restarts a named account
reconnect with an alias read the account list from Nous Portal and
treated a failed read as 'not connected', so a portal outage sent the
user through a new login for a healthy account. The read error now
reaches the tool result and nothing is minted.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
d2590382a1 test(connectors): named accounts across dispatch, managed actions and RPC
Invariants, each red on main: the account a call ran under survives the
single and batch dispatch paths; connect with an alias asks for that
account; reconnect with an alias repairs it while another account is
healthy; rename of an unknown name lists the valid ones; an open
operation is not reused for another account of the same connector; the
connect and rename RPCs carry the alias and map a taken name.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
e5ebe5d8d7 fix(connectors): catch only gateway errors around the portal account read
The portal client wraps transport and parse failures in ToolGatewayError,
so the soft account reads in status and reconnect catch that, not every
exception.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
43e054890c feat(cli): the connection card says which name Hermes gave an account
When a connected target carries an alias, the classic CLI card adds
'Hermes named this <app> account <alias>. Rename it anytime.'
2026-10-11 17:21:07 +05:30
Siddharth Balyan
f22d98aec6 test(connectors): the managed gateway fake accepts an account alias 2026-10-11 17:21:07 +05:30
Siddharth Balyan
4834cf0d97 feat(connectors): gateway contracts for named connector accounts
connectors.connect takes an alias (one connector per call), operation
targets carry their alias, and connectors.accounts.rename names or
renames one account, answering ALIAS_TAKEN when the name is in use.
Generated TypeScript contracts regenerated.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
0ca69688b0 feat(connectors): connect, repair, rename and list named accounts
connect sends the alias so the gateway adds or reuses that account.
reconnect with an alias checks that account's own row instead of the
connector-wide connected flag, which reports any account. rename resolves
the alias (or the label of an unnamed account) to its id and renames it
through Nous Portal; an unknown name lists the valid ones. status lists
each connector's accounts and stays usable when that read fails.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
6301bf4bc3 feat(connectors): a connection operation can target one named account
Target carries the account alias and shows it in its snapshot. An open
operation is reused only for the same connector AND alias, so connecting
gmail 'work' never joins an open operation for the default gmail account.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
ec8a971c08 feat(connectors): manage_connections targets can name an account
A hosted target object takes 'alias' (the account's name) and 'to' (a
rename's new name), and the tool gains a 'rename' action. Targets parse
into HostedTarget; a call with an alias carries that one target, and MCP
targets refuse both fields.
2026-10-11 17:21:07 +05:30
Siddharth Balyan
8c86e112f7 feat(connectors): connector results name the account they ran under
The gateway now reports the alias of the account each call used. Keep it
through the wire model, the result dict, both dispatch paths and the
model-facing entry, and show the chosen alias in the tool label.
2026-10-11 17:21:07 +05:30
Teknium
7adb97f22a chore(hooks): web lint hook no longer grabs website/ files
lefthook's `root: web/` is a string-prefix filter, so `website/sidebars.ts`
matched it, was rewritten to `.site/sidebars.ts`, and eslint aborted the
commit ("No files matching the pattern"). Every docs PR that registers a
page in the sidebar hit it. Anchoring the glob to `web/**` keeps web/
files linted and leaves website/ alone.
2026-10-11 04:41:20 -07:00
Teknium
36e0c68ecc feat(optional-skills): anatomy, ported for Hermes (MIT, wheresryan22/anatomy)
Replaces the catalog stub with a vendored, Hermes-specific port of
wheresryan22/anatomy pinned at dbf697a (v1.0.0). The skill explains a
technical idea by building it as an interactive isometric machine
figure (SVG, optional WebGL layers, beta 3D mode) and proves the
drawing with a geometry audit and a line-end checker.

Vendored verbatim: kit/, references/, scripts/, the example sources,
LICENSE. Hermes changes:

- SKILL.md gains a "Running it in Hermes" section: ${HERMES_SKILL_DIR},
  build in a copied workspace (build.mjs writes next to itself, so it
  must never run inside the installed skill), vision_analyze for the
  PNG reviews, background terminal for the long audits, clarify for
  the Epic scope call, how to deliver results on CLI and messaging.
- The upstream body is kept, with "read every PNG" mapped to
  vision_analyze and the background runs mapped to the terminal tool.
- Example sources only (528 KB): the 9 MB of built HTML pages are
  dropped and the skill says how to rebuild them in seconds.
- capture/drive/turn-check now find the Playwright headless shell that
  Hermes' browser tool installs (Linux and macOS cache dirs) before a
  system Chrome, and pass --disable-background-networking. Under
  strace the headless shell makes zero outbound connections; a desktop
  Chrome reaches Google services even with that flag.
- The desk-computer example's CRT shows an H mark and types
  "hello, hermes" instead of another vendor's mark and name.
- Two CC-22 functions in the vendored spot-plate example carry a
  reviewed health allow rather than a refactor, so the port stays
  diffable against upstream.

.gitignore: the bare `examples/` pattern (meant for dataset dumps)
swallowed skill example dirs, which other skills had to force-add.
Skill trees are now allowlisted.
2026-10-11 04:40:50 -07:00
Teknium
283b3f39c9 test(scanner): caution-fixtures use shapes the FP fixes keep at caution
test_caution_plugin_accepted_via_callback parametrized two desktop/plugin.js
fixtures (dig lookup, authorized_keys help string) that this same PR correctly
demotes to safe, so the caution-accepted path no longer triggered and the test
failed. Replace them with a /proc/self/fd read (proc_access, still high): a
genuine caution shape the sweep confirms stays flagged (hermes-trove). The
eval('1+1') case is unchanged.
2026-10-11 04:37:57 -07:00