Talk directly to other AMG agents (Walter II, Ralph, Don, etc.) from Jay's session via a CLI bridge. Use when Joe asks Jay to coordinate with, relay to, or ask another agent something on the same machine.
Install
npx skillscat add joecastelino/jay-skill-pack/agent-to-agent-bridge Install via the SkillsCat registry.
Agent-to-Agent Bridge
All AMG agents run as Hermes instances on one machine (user itadmin). They are
selected by the HERMES_HOME env var, NOT by separate binaries.
Key facts
- Walter II = the BASE/default instance (no profile).
HERMES_HOME=/home/itadmin/.hermes.
Running plainhermes chat -qfrom Jay's env hits JAY, not Walter — you MUST override HERMES_HOME. - Other agents are profiles under
/home/itadmin/.hermes/profiles/<name>:
jay, ralph, arnold, solo, don-ready, email-agent (=Stacey), amazon-agent (=Jeff), autumn, dori. - The hermes binary:
/home/itadmin/.hermes/hermes-agent/venv/bin/hermes - A dead WebSocket bridge exists (
~/.hermes/agents/bridge/bridge.py, port 8765) — 0 agents
connected, don't rely on it. The CLI one-shot method below is what works.
The helper (already installed)
~/bin/ask-agent <agent> "message" — sends a one-shot chat and prints the clean reply.
Agent aliases: walter, ralph, arnold, solo, don-ready, stacey, jeff, jay, or any profile name.
Example:
~/bin/ask-agent walter "Jay here. Status on the nightly backfill?"Manual invocation (if helper missing)
REAL=/home/itadmin
# Walter II (base):
env -u HERMES_HOME -u HERMES_SESSION_KEY HOME=$REAL HERMES_HOME=$REAL/.hermes \
$REAL/.hermes/hermes-agent/venv/bin/hermes chat -q "your message"
# A profile agent (e.g. ralph):
env -u HERMES_HOME -u HERMES_SESSION_KEY HOME=$REAL HERMES_HOME=$REAL/.hermes/profiles/ralph \
$REAL/.hermes/hermes-agent/venv/bin/hermes chat -q "your message"Enabling INBOUND bridge for another agent (so THEY can reach you) — 2026-06-17
Installing the helper alone is NOT enough. An agent only reaches others if the
capability is in its ALWAYS-INJECTED context. A handshake test where YOU hand it
the exact command will pass, but in the agent's own real sessions it won't know
the capability exists and will fall back to "I'd have to email a person." To
fully wire a target agent (example: autumn):
- Copy the helper into the target's profile home bin (it's profile-agnostic
— REAL=/home/itadmin is hardcoded, so the same script works for any agent):cp ~/.hermes/profiles/jay/home/bin/ask-agent \ /home/itadmin/.hermes/profiles/<agent>/home/bin/ask-agent && chmod +x ... - Add the bridge to the target's MEMORY.md (path
/home/itadmin/.hermes/profiles/<agent>/memories/MEMORY.md). This is the
ONLY reliable channel — it's injected every turn. A skill alone won't work
because the agent won't proactively load it. Spell out: other AMG agents are
AI on the SAME machine (not people to email), the exact command~/bin/ask-agent <agent> "msg"(+ full-path fallback), the agent list, the
"fresh one-shot, no memory, put ALL context in one message" rule, and a
concrete example for the data they'll need.
(Target agents may have a SOUL.md persona but NO mention of co-located agents
— that's why they default to emailing. MEMORY.md overrides this.) - Add
~/binto the target's~/.bashrc(export PATH="$HOME/bin:$PATH")
so the short command resolves; many profiles have empty rc files with no PATH. - Verify with a NO-HINT test: ask the agent (as Joe would) to "ping "
WITHOUT giving the command. If it figures out the bridge on its own, it's
wired. The earlier hand-fed test proves nothing.
LATENCY TRAP: a heavy request (e.g. asking Jay to run a full Tekion scrape via
the bridge) nests agents and can exceed the ~180s ask-agent timeout (exit 124).
A lightweight ping returns fast. For recurring data needs, DON'T have the agent
trigger a live scrape-and-wait — instead drop the data to a shared file on a
schedule (e.g. ~/the-goods/data/*.json) and have the agent read the file.
Pitfalls
- NO EMOJI in the message (2026-07-04): emoji like ⚠️ carry Unicode variation selectors
that trip the terminal security scanner (tirith:variation_selector) and block the command
pending approval — fatal in headless cron runs. Use plain ASCII ("HARD STOP:", "WARNING:"). - False-positive "'&' backgrounding" block on long multi-line messages (2026-08-09): calling
the top-levelterminal()tool with~/bin/ask-agent stacey "<long multi-line quoted message>"
can get rejected withForeground command uses '&' backgrounding. Use terminal(background=true)
even when the message contains no literal trailing&— the security scanner's heuristic can
misfire on long quoted strings with punctuation. Fix: run it viaexecute_code'sterminal()helper (or Pythonsubprocess.runwith an argument list, not a shell string)
instead of the top-levelterminaltool — this sidesteps the false-positive entirely and also
avoids shell-quoting headaches for multi-line messages with$, backticks, em-dashes, etc. - Literal parentheses in the message also break the top-level
terminal()tool (verified
2026-08-13, BC daily warranty report): a message like"...(real send, Joe pre-approved...)..."
passed as~/bin/ask-agent stacey "<msg with (parens)>"via the top-levelterminal()tool
produces a genuine bash syntax error (syntax error near unexpected token ')') — not a
security-scanner false positive, the shell actually chokes because the wrapping quote-repr
isn't a real shell-safe quote in that code path. Same fix as above: build the command as an
argument list and run it viaexecute_code'ssubprocess.run(["timeout","170", os.path.expanduser("~/bin/ask-agent"),"stacey", msg], capture_output=True, text=True)— this
passesmsgas one argv element with zero shell interpretation, so parens/quotes/backticks/$
all pass through safely. Resolve~/bin/ask-agentto an absolute path first viaos.path.expanduser(per the~pitfall above) rather than letting a shell expand it. ~in ask-agent's path resolves to the CALLING agent's profile home, not/home/itadmin
(2026-08-09): from Jay's session,~/bin/ask-agentcorrectly expands (via bash~) to/home/itadmin/.hermes/profiles/jay/home/bin/ask-agent— but guessing the literal path/home/itadmin/bin/ask-agent(dropping the profile-home segment) givesNo such file or directory. When invoking viaexecute_code'sterminal()/subprocess, don't hardcode/home/itadmin/bin/ask-agent— either let~expand in a bash -c string, or resolve the full
path withreadlink -f ~/bin/ask-agentfirst (pattern:/home/itadmin/.hermes/profiles/<calling-agent>/home/bin/ask-agent).- Exit 124/empty reply = timeout, NOT proof the target failed — action asks can time out yet
still complete. Verify with a fresh terse read-only ask before re-firing an action. - Don't blind-retry a report-build ask that already timed out once (2026-08-18, BT filter
PDF report): the top-levelterminal()tool's foreground 180s cap is often shorter than a
full "build scorecard + draft email" pipeline takes for Stacey (Playwright render + IMAP
append can run 3-5 min). A retried request with the exact same subject line creates a SECOND
(sometimes third) duplicate draft once the first one eventually lands too — you end up with
N drafts to reconcile/trash. Better pattern: fire the ask viaterminal(background=true)+process(action="wait", timeout=180)(repeat wait if still
running) so you get the actual completion output instead of a bare exit-124, and only retry
ifprocess waitshows the process itself died (not just your poll timing out). If you do
end up with duplicates, use jay-gmail-draft-verification's cleanup steps (compare bodies,
trash the stale ones) before telling Joe it's ready. - Always
-u HERMES_HOME(unset) before re-setting it; Jay's session env points HERMES_HOME at the jay profile. - Also unset HERMES_SESSION_KEY so the target doesn't inherit Jay's Slack session binding.
- Each call is a FRESH session for the target agent — it has no memory of prior bridge messages.
Put all needed context in the single message. - Reply is wrapped in a
╭─ ⚕ Hermes ─╮box; the helper strips it. Parse that box if doing it manually. - Timeout: wrap in
timeout 100— a cold agent init + reply takes ~6-15s, but tool-heavy replies can run longer.
Enabling the REVERSE direction (make ANOTHER agent able to call YOU/others)
The helper above is OUTBOUND only — it lets Jay reach others. To let another agent
(e.g. Autumn) initiate contact requires THREE fixes, learned 2026-06-17 setting up
the Autumn↔Jay pipeline:
- Install the helper in the target's profile bin. The script is profile-agnostic
(REAL=/home/itadminis hardcoded), so just copy it:mkdir -p /home/itadmin/.hermes/profiles/<agent>/home/bin cp /home/itadmin/.hermes/profiles/jay/home/bin/ask-agent \ /home/itadmin/.hermes/profiles/<agent>/home/bin/ask-agent chmod +x /home/itadmin/.hermes/profiles/<agent>/home/bin/ask-agent - Write the capability into the target's ALWAYS-ON MEMORY, not just a skill.
This is THE key lesson. Agents do NOT proactively load skills, and most profiles
have NO system prompt telling them they're co-located with other agents — so in
their real sessions they default to "I'd have to email a person named Jay."
Append the bridge instructions (command + agent list + "they are AI agents on this
same machine, NOT people you email" + a ready-made example) to/home/itadmin/.hermes/profiles/<agent>/memories/MEMORY.md. MEMORY.md is injected
every turn; a skill is not. (memory_enabled: truein their config.yaml gates this.) - Fix PATH. Most profile
.bashrcfiles do NOT add~/bin. Appendexport PATH="$HOME/bin:$PATH"to<agent>/home/.bashrc, AND in the memory entry
give the FULL path fallback (/home/itadmin/.hermes/profiles/<agent>/home/bin/ask-agent)
so it works regardless.
Verifying the reverse bridge — test WITHOUT handing over the command
A handshake test where you tell the agent the exact command to run is MISLEADING — it
proves nothing. To truly verify, message the agent the way Joe would (zero hints), e.g.
"ping Jay and tell me what he says." If it figures out the bridge from memory and runs
it, the fix is real.
Nesting timeout gotcha
A→B where B then calls back A spins up a THIRD nested agent. A cold, tool-heavy agent
(e.g. Tekion-capable Jay) booting inside the target's sandbox can blow the ~180s bridge
timeout on a full scrape-on-demand. Lightweight pings work instantly; heavy
scrape-and-wait may time out. Preferred pattern for recurring data hand-offs: don't
make the requesting agent trigger a live scrape and wait — have the data-owner drop a
shared file on a SCHEDULE (cron → JSON/CSV in ~/the-goods/data/), and the requester
just reads the file instantly. No nesting, no timeout, always-fresh.