Skip to content

Retarget Agents surface from opencode to Hermes (enterpriseaiframework-8a9) - #39

Draft
baron-3dl wants to merge 10 commits into
mainfrom
worktree-retarget-agents-to-hermes
Draft

Retarget Agents surface from opencode to Hermes (enterpriseaiframework-8a9)#39
baron-3dl wants to merge 10 commits into
mainfrom
worktree-retarget-agents-to-hermes

Conversation

@baron-3dl

Copy link
Copy Markdown
Contributor

Retargets the resident Agents surface from opencode serve to Hermes Agent (NousResearch), fixing the conflation of the Agents UX with the coding UX. Deployed live and verified; branch is what the running control-plane (c7b81c0) was built from.

What changed

  • Runtime: agent pods run hermes gateway run (image nousresearch/hermes-agent:v2026.8.3, HERMES_HOME=/opt/data PVC, config.yaml seeded to our gateway on deepseek-v4-flash@deepinfra), empty securityContext (s6 boots root → drops to uid 10000), config-seed init container. No inbound port.
  • Console: Open console is now an in-browser xterm terminal that execs hermes --tui via the Kubernetes pods/exec subresource (owner-scoped), replacing the opencode web-console proxy. xterm vendored; RBAC gains pods/exec create.
  • Both renderers (portal agents.py + operator provision-agent.sh) retargeted with matching CFGSUM; opencode connector shell scripts + entrypoint.sh deleted; NetworkPolicy drops the 4096 ingress.
  • Source of truth: docs/design/records/agents-surface-hermes-retarget.md + design.md §12.

Tests

  • control-plane suite: 179 passed (create renders Hermes; console exec-bridge owner-scoping; connectors via envFrom).
  • operator (tests/) agent suite green after retarget.

Verified live

Portal-created agent scout reached Running and answered inference through the gateway; console page serves owner-200 / non-owner-404; control-plane SA can exec. Camp workspaces untouched by the deploy.

Follow-up

enterpriseaiframework-ff6 — connector env-var name translation (Slack/Discord/email don't auto-enable until AGENT_* keys map to Hermes's native names). Additive; core create/console/multi-tenant unaffected.

🤖 Generated with Claude Code

alice and others added 7 commits August 10, 2026 20:11
…truth (enterpriseaiframework-8a9)

The Agents epic (-da7) built the resident surface on 'opencode serve' + an
opencode-web-console proxy, making a 'hermes agent' literally an opencode process.
That conflated the Agents surface with the Code/coding surface (Baron's ruling
2026-08-10: 'opencode is for the coding app; don't conflate the coding UX with the
agent UX'). This lands the corrected source of truth so the design no longer encodes
the conflation, and pins the exact Hermes runtime for the build:

 - new docs/design/records/agents-surface-hermes-retarget.md: the buildable spec —
   image nousresearch/hermes-agent:v2026.8.3, 'hermes gateway run' daemon, HERMES_HOME
   /opt/data PVC, config.yaml seeded with our gateway provider (http://gateway:4000/v1),
   empty securityContext (s6 needs root), console = exec-attach 'hermes --tui', per-file
   change list, live proof plan.
 - agents-surface.md: retarget banner; chassis contracts (1/3/4/5/6) still stand.
 - design.md §12: resident process is 'hermes gateway run', console exec-attaches
   'hermes --tui', HERMES_HOME=/opt/data; runtime chart jyje/hermes-agent.

Code/workspace (Contract 6 frozen set) untouched. Build tracked in -8a9.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (enterpriseaiframework-8a9)

Proved the Hermes runtime end to end on the real cluster and captured the exact
working recipe before touching the repo template:

 - agent-baron-rudi retargeted opencode->Hermes and is Running: image
   nousresearch/hermes-agent:v2026.8.3, 'hermes gateway run' under s6, HERMES_HOME
   /opt/data on its PVC, inference routed through our gateway (http://gateway:4000/v1)
   on the integrated <baron::agents/rudi> key. Live 'hermes chat -q' answered correctly.
 - deploy/agent/hermes-config.yaml.tmpl: the seeded config.yaml (gateway provider,
   model deepseek-v4-flash@deepinfra, context_length 128000, max_tokens 8000) with the
   two integration traps documented (bare model id, and missing context_length / over-cap
   max_tokens both misreport as 'context length exceeded').
 - retarget record: 'Validated live' section. Key finding flagged for Baron — the
   Hermes image REQUIRES root at boot (s6 chowns then drops to uid 10000); the opencode
   template's runAsNonRoot/drop-ALL hardening kills it. Agent process still runs non-root
   (10000); only s6 init is root.

Console today: kubectl exec -it deploy/agent-baron-rudi -c agent -- hermes --tui.
In-portal terminal (agent_console.py exec bridge) deferred — needs a control-plane
redeploy, held while the camp is live. Code surface untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t (enterpriseaiframework-8a9)

Portal create() now provisions a Hermes pod, not opencode:
 - 64-agent.template.yaml: Hermes image, 'hermes gateway run', HERMES_HOME=/opt/data PVC,
   per-agent config.yaml ConfigMap + root init-seed, empty securityContext (s6 needs root
   then drops to 10000), no inbound port. Connector envFrom + BYO/checksum placeholders kept.
 - agents.py: DEFAULT_MODEL=deepseek-v4-flash@deepinfra (+ context_length/max_tokens defaults),
   HERMES_IMAGE (agent no longer reuses the workspace image), render() seeds config.yaml,
   create() drops the opencode server password + the obsolete agent-entrypoint ConfigMap,
   console_target() resolves the owned running pod for pods/exec 'hermes --tui'.

Console bridge (agent_console.py -> pods/exec), RBAC, provision-agent.sh parity, and the
control-plane redeploy still to come. Code/workspace surface untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…aiframework-8a9)

The Agents 'Open console' is now a real browser terminal into the running Hermes pod,
replacing the opencode-SPA proxy:
 - agent_console.py rewritten: serves an xterm terminal page at /agents/<name>/ and a
   WebSocket that bridges the browser to the Kubernetes pods/exec subresource running
   'hermes --tui' in the caller's OWN pod (owner-scoped by agents.console_target, same
   guard as stop/start/delete). Reuses the SA token + cluster CA; v4.channel.k8s.io framing.
 - xterm.js vendored into portal_static (self-hosted, CSP stays strict, no CDN).
 - 39-control-plane-rbac.yaml: grant pods/exec create (create-only, fixed command
   'hermes --tui'). Documented posture change from the opencode design's 'NO pods/exec' —
   forced by the terminal-in console Baron asked for; scoped by the owner guard, not RBAC.

Multi-tenant + multiple agents per user were already the chassis (create/stop/start/delete,
owner-scoped); this makes them run Hermes and console into Hermes. Deploy (control-plane
rebuild + agent-assets ConfigMap + RBAC apply) is the next step. py_compile clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…the tool ConfigMap (enterpriseaiframework-8a9)

Hermes reads messaging connectors from the environment, so the deployment-wide
agent-entrypoint tool ConfigMap (opencode's shell tools, finding 49's force-apply clobber)
is retired. The test now asserts a created agent has no such ConfigMap and injects the
three connector Secrets via envFrom (optional). Full control-plane suite green: 179 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sh render Hermes, drop opencode connector scripts (enterpriseaiframework-8a9)

- provision-agent.sh: Hermes image default, __CONTEXT_LENGTH__/__MAX_TOKENS__ sed,
  config-inputs CFGSUM matching agents.py, -key Secret holds only OPENAI_API_KEY
  (OPENCODE_SERVER_PASSWORD retired), no agent-entrypoint ConfigMap. Connector Secret
  KEYS kept as AGENT_* (control-plane parity); Hermes env mapping documented as TODO.
- hermes-up.sh: validate chat via connector env presence + inference via config.yaml
  base_url/model, not opencode tools/OPENCODE_MODEL.
- 63-agent-common.yaml: drop the port-4096 ingress rule (console is pods/exec now).
- deploy.sh: agent-assets ships only 64-agent.template.yaml.
- delete deploy/agent/{entrypoint.sh,agent-email,agent-slack,agent-discord,agentws.py,
  EMAIL.md,SLACK.md,DISCORD.md}.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…8a9)

- harness: in-pod exec branches probe connector env presence + config.yaml inference.
- test_agent_model_api_config: base_url/model read from the config ConfigMap, template
  kinds ConfigMap not Service.
- test_hermes_up: chat validated by connector env presence, inference via config.yaml;
  two in-pod checks, not three.
- test_agent_{chat,email}_provisioning: -key Secret holds only OPENAI_API_KEY; drop the
  retired agent-entrypoint tool-ConfigMap tests.
- delete test_agent_{slack,discord,email}.py, chat_fakes.py, test_agent_{chat,email}_
  entrypoint.py, test_agent_assets.py (all test deleted opencode connector tooling).
- new test_agent_no_chat_server.py preserves the no-chat/mail-server + no-inbound-route
  invariants against the Hermes template.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
the socket) means a non-owner or a stopped agent gets the real 404/409 as a page, rather
than a blank terminal that fails silently on connect."""
await agents.console_target(user, name)
return HTMLResponse(_terminal_page(name))
alice and others added 3 commits August 11, 2026 00:59
…encode AGENT_* (enterpriseaiframework-ff6)

The connector wizard shipped BROKEN: it wrote Slack/Discord/email tokens under opencode's
AGENT_* keys, but 'hermes gateway run' reads native names (SLACK_BOT_TOKEN, DISCORD_BOT_TOKEN,
EMAIL_ADDRESS, ...), so a browser-wired connector connected to nothing (found live on rudi's
Discord). Retargets all three synced copies to the native names, in lockstep:
 - agents.py CONNECTORS, portal_static/app.js CONNECTOR_FIELDS, provision-agent.sh allowlists.
 - Adds *_ALLOWED_USERS (Slack/Discord) + EMAIL_ALLOW_ALL_USERS: Hermes denies unknown senders
   by default, so without them a connector connects but answers no one (the rudi symptom).
   Wizard hints say so; blank = secure default.
 - Email loses username/port/security fields — Hermes auto-detects ports and TLS.
 - test_portal_connectors retargeted; the parity + browser-form + wiring tests keep the three
   copies honest. Control-plane suite: 179 passed.

Fixes the ff6 gap that made every new agent's connectors broken until hand-patched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nterpriseaiframework-8a9)

The retarget added agent-<user>-<name>-config (the seeded Hermes config.yaml), but the
delete path never removed it, so deleting an agent left an orphaned ConfigMap behind — and
re-creating the same name would adopt a stale config. Delete now removes it; the delete
test asserts configmaps are gone alongside deployments/services/secrets/PVCs. Found by
running delete on a live portal-created agent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nv-var names (enterpriseaiframework-ff6)

Follows the control-plane connector rename (3664edd): AGENT_* -> Hermes-native
(SLACK_BOT_TOKEN, DISCORD_BOT_TOKEN, EMAIL_ADDRESS, ...), *_DEFAULT_CHANNEL ->
*_HOME_CHANNEL, sum keys <LABEL>_CONFIG_SUM.

- hermes-up.sh: chat env-presence probe checks the native SLACK_/DISCORD_ names.
- agent_provision_harness.py: connector-sum state file uses <LABEL>_CONFIG_SUM.
- test_agent_chat_provisioning.py / test_hermes_up.py: config-files + assertions native.
- test_agent_email_provisioning.py: email fixture drops the opencode-only fields Hermes
  auto-detects (username, SMTP/IMAP port, security); new
  test_a_dropped_opencode_email_field_is_now_refused asserts the shell rejects them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants