docs(agents): surface capability belongs to the session, not the process env
The rule the preview-tool bug broke, written down so the next GUI-adjacent tool does not rediscover it: the client and the backend are separate machines, so "was this process spawned by Electron?" cannot answer "is a GUI watching?". Names the working pattern (toolset gates the surface, check_fn answers only reachability or user opt-in), the process-wide check_fn TTL cache that makes it the wrong home for a per-session answer, and the test that would have caught it — assert the GUI session gets the tool with the env var absent.
This commit is contained in:
parent
7ad9ace2cc
commit
ac745a0b07
39
AGENTS.md
39
AGENTS.md
|
|
@ -210,6 +210,45 @@ backends, providers, notifiers), don't merge them one at a time — design an
|
|||
ABC + orchestrator, wrap the existing built-in as the first provider, and turn
|
||||
the competing PRs into plugins against that interface.
|
||||
|
||||
### Surface capability is a property of the SESSION, never of the process env
|
||||
|
||||
A tool that only works because of *who is on the other end of the connection* —
|
||||
the desktop app's panes, the in-app browser, message reactions, Projects — must
|
||||
resolve its availability from the **session's own source**, not from an env var
|
||||
on the backend process.
|
||||
|
||||
The client and the backend are separate machines on separate clocks. The
|
||||
desktop app can be driving a backend Electron spawned locally, one over SSH,
|
||||
one behind a plain URL + token, or Hermes Cloud. Only the first two are spawned
|
||||
by us and carry `HERMES_DESKTOP=1`. Every env-keyed GUI gate is therefore a
|
||||
silent no-op on the other half of the topologies, and the failure is invisible:
|
||||
the tool is stripped from the schema before the model ever sees it, on the same
|
||||
backend whose platform hint is telling the model it's *"chatting inside the
|
||||
Hermes desktop app."*
|
||||
|
||||
The pattern that works:
|
||||
|
||||
- **The toolset is the surface gate.** Keep the tools off `_HERMES_CORE_TOOLS`
|
||||
(nobody else should pay their schema) and put them in a named toolset —
|
||||
`desktop_ui`, `project`. The GUI gateway's `_load_enabled_toolsets(platform)`
|
||||
folds that toolset in when the session's platform says GUI. One resolver,
|
||||
every topology.
|
||||
- **`check_fn` answers reachability or user opt-in, not surface.** "Is the
|
||||
renderer bridge wired?", "did the user enable reactions?" — fine. "Was I
|
||||
spawned by Electron?" — not fine. `check_fn` results are also TTL-cached
|
||||
process-wide (`tools/registry.py`), so a per-session answer does not belong
|
||||
there at all: one process serves many sessions.
|
||||
- **Ask which identity you actually mean.** `HERMES_DESKTOP=1` legitimately
|
||||
marks *"this backend process was spawned by the app"* — it gates the cron
|
||||
ticker and web-dist handling correctly. It does NOT mean "a GUI is watching",
|
||||
and the embedded terminal pane (`hermes --tui` against that same backend) is
|
||||
the standing counterexample.
|
||||
|
||||
Same test both ways: if the capability would still make sense with the client
|
||||
on another machine, it is session-scoped. Cover it with a test that asserts the
|
||||
GUI session gets the tool **with the env var absent** — that's the assertion
|
||||
the original gate could never have passed.
|
||||
|
||||
## Development Environment
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -153,6 +153,14 @@ consumer. Design a shared contract only once more than one real consumer proves
|
|||
its shape. "Plugin" means several unrelated things across Hermes — do not assume
|
||||
one surface's extension model runs in another.
|
||||
|
||||
When the new capability is an **agent-callable** one — a tool that acts on this
|
||||
renderer (open a pane, read the in-app browser, react to a message) — it is a
|
||||
property of the SESSION's client, not of the backend host. Wire its
|
||||
availability off the session source the app already sends on `session.create`
|
||||
(`source: 'desktop'`), never off an env var on the backend process: that
|
||||
process might be a remote or cloud gateway this app merely connected to. See
|
||||
the root AGENTS.md, "Surface capability is a property of the SESSION."
|
||||
|
||||
## Respect the person using it
|
||||
|
||||
Design and engineering meet at intent. The user's attention and context are
|
||||
|
|
|
|||
Loading…
Reference in New Issue