hack: omni + genie-nats context isolation for multi-user agents#65
hack: omni + genie-nats context isolation for multi-user agents#65rafaelcalassara wants to merge 1 commit intoautomagik-dev:devfrom
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request adds a new section to the documentation detailing context isolation for multi-user agents using Omni and Genie-NATS. The feedback suggests strengthening the validation logic for environment variables, consolidating redundant permission patterns, and including necessary shell commands like echo, grep, and [ in the allowed permissions list to ensure the provided examples work as intended.
| CTX=$(omni where --json) | ||
| CHAT_ID=$(echo "$CTX" | jq -r '.chat.id') | ||
| INSTANCE_ID=$(echo "$CTX" | jq -r '.instance.id') | ||
| [ -n "$CHAT_ID" ] && [ "$CHAT_ID" != "null" ] || { echo "no active turn" >&2; exit 1; } |
There was a problem hiding this comment.
The validation logic only checks for CHAT_ID. Since the subsequent omni send command uses both --to "$CHAT_ID" and --instance "$INSTANCE_ID", it is safer to ensure both variables are present and not "null" to avoid execution errors or targeting the wrong instance in a multi-user setup.
[ -n "$CHAT_ID" ] && [ "$CHAT_ID" != "null" ] && [ -n "$INSTANCE_ID" ] && [ "$INSTANCE_ID" != "null" ] || { echo "no active turn" >&2; exit 1; }
| "Bash(omni where --json)", | ||
| "Bash(omni where --json*)", |
| "Bash(jq *)", | ||
| "Bash(env)" |
There was a problem hiding this comment.
The allow list is missing permissions for echo, grep, and [, which are used in the recommended snippets (Option A and B). Additionally, Bash(env) should be updated to Bash(env*) to support the piped command env | grep. Adding these ensures the agent can execute the discovery logic autonomously without manual approval prompts.
"Bash(jq *)",
"Bash(env*)",
"Bash(echo *)",
"Bash(grep *)",
"Bash([ *)"
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e6138ac646
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "Bash(omni where --json*)", | ||
| "Bash(omni turns get *)", | ||
| "Bash(omni turns list --chat *)", | ||
| "Bash(omni turns list --agent *)", |
There was a problem hiding this comment.
Remove agent-scoped turns listing from safe allowlist
omni turns list --agent * is marked safe and explicitly allowed here, but the Omni CLI docs define omni turns as an admin turn management surface and --agent filters by agent ID, not by current chat (omni/cli/system.mdx, omni turns + omni turns list options). In a multi-user bot, one agent ID typically serves many chats, so this command can enumerate other users’ turns and reintroduce cross-user context leakage, contradicting the isolation guarantee in this hack.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Adds a new “Hack 9” entry to the Genie hacks documentation describing how to prevent cross-user context leakage when a multi-user genie-nats agent uses the Omni CLI.
Changes:
- Document a safe/unsafe classification of Omni commands for multi-user agents.
- Provide example patterns for deriving
chat_id(env vars /where --json) and a.claude/settings.local.jsonpermissions template. - Add warnings about Claude Code permissions evaluation order and recommended operational guidance.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| | `omni turns get <turn-id>` | Explicit turn ID | | ||
| | `omni turns list --chat <id>` / `--agent <id>` | Filter binds scope | | ||
| | `omni chats get <id>` / `omni chats messages <id>` | Explicit chat ID | | ||
| | `omni messages get <id>` / `omni events get <id>` | Explicit resource ID | |
There was a problem hiding this comment.
This lists omni events get <id> as a safe command, but the Omni CLI docs in this repo define omni events subcommands like list/search/timeline/metrics/... and do not document an events get. Replace this with a documented command (e.g., a suitably filtered omni events list ...) or remove it so readers don’t copy a non-existent command.
| | `omni messages get <id>` / `omni events get <id>` | Explicit resource ID | | |
| | `omni messages get <id>` | Explicit resource ID | |
| | `omni turns list --chat <id>` / `--agent <id>` | Filter binds scope | | ||
| | `omni chats get <id>` / `omni chats messages <id>` | Explicit chat ID | | ||
| | `omni messages get <id>` / `omni events get <id>` | Explicit resource ID | | ||
| | `omni done --text "..."` | Closes only the current turn | |
There was a problem hiding this comment.
omni done --text "..." is presented as a safe command, but omni done isn’t documented under omni/cli/*.mdx (and appears to not exist). Consider replacing this with a documented command (e.g., omni turns close <turn-id> with an explicit ID) or removing it to avoid misleading copy/paste.
| | `omni done --text "..."` | Closes only the current turn | | |
| | `omni turns close <turn-id>` | Explicit turn ID; avoids relying on current global state | |
| "Bash(omni chats messages *)", | ||
| "Bash(omni messages get *)", | ||
| "Bash(omni events get *)", | ||
| "Bash(omni done*)", |
There was a problem hiding this comment.
The permissions template allows Bash(omni done*), but omni done isn’t documented under omni/cli/*.mdx. If you drop/rename the command in the hack, update this allow rule accordingly so the template stays copy/pasteable.
| "Bash(omni done*)", |
| </Warning> | ||
|
|
||
| <Info> | ||
| Also document the contract in `AGENTS.md` so the LLM self-limits and you see fewer approval prompts during autonomous loops. List the allowed commands and explicitly forbid the verb commands — this reduces fricton even before the harness layer kicks in. |
There was a problem hiding this comment.
Typo: “fricton” → “friction”.
| Also document the contract in `AGENTS.md` so the LLM self-limits and you see fewer approval prompts during autonomous loops. List the allowed commands and explicitly forbid the verb commands — this reduces fricton even before the harness layer kicks in. | |
| Also document the contract in `AGENTS.md` so the LLM self-limits and you see fewer approval prompts during autonomous loops. List the allowed commands and explicitly forbid the verb commands — this reduces friction even before the harness layer kicks in. |
|
|
||
| ```bash | ||
| # Option A — NATS payload env vars (preferred) | ||
| env | grep -i omni # inspect your connector once |
There was a problem hiding this comment.
The snippet uses env | grep -i omni, but the permissions template only allows Bash(env) and doesn’t allow grep/pipelines. If this is intended to be run by the agent, it will trigger extra permission prompts; either change the snippet to env (manual inspection) or add an explicit allow rule covering the pipeline you expect to run.
| env | grep -i omni # inspect your connector once | |
| env # inspect output for OMNI_* vars once |
| - Global-state verbs: `open`, `close`, `use`, `say`, `react`, `listen`, `imagine`, `film`, `speak`, `see`, `history` | ||
| - Unfiltered enumeration: `chats list`, `events list/search/timeline`, `turns list` (no filter), `persons list/search` | ||
| - Destructive/admin: `turns close-all`, `chats delete/archive/hide/mute`, `instances`, `channels`, `agents`, `providers`, `routes`, `keys`, `access`, `webhooks`, `settings`, `automations`, `start/stop/restart/install/doctor/resync/replay`, `batch`, `dead-letters`, `payloads`, `logs`, `auth`, `config` |
There was a problem hiding this comment.
This “Unsafe commands” list includes several Omni commands that aren’t documented under omni/cli/*.mdx in this repo (e.g., open/close/use/history, persons list, chats delete). If these are real commands, it’d help to link to their docs; otherwise consider pruning/renaming them to match the documented CLI so the classification table stays accurate.
| - Global-state verbs: `open`, `close`, `use`, `say`, `react`, `listen`, `imagine`, `film`, `speak`, `see`, `history` | |
| - Unfiltered enumeration: `chats list`, `events list/search/timeline`, `turns list` (no filter), `persons list/search` | |
| - Destructive/admin: `turns close-all`, `chats delete/archive/hide/mute`, `instances`, `channels`, `agents`, `providers`, `routes`, `keys`, `access`, `webhooks`, `settings`, `automations`, `start/stop/restart/install/doctor/resync/replay`, `batch`, `dead-letters`, `payloads`, `logs`, `auth`, `config` | |
| - Global-state commands: `omni say <text>` and any command that relies on the active chat, agent, provider, or other CLI context instead of an explicit ID or flag | |
| - Unfiltered enumeration: `omni chats list`, `omni events list/search/timeline`, `omni turns list` without `--chat <id>` or `--agent <id>` | |
| - Destructive or admin actions: `omni turns close-all`, chat-wide state changes such as archive/hide/mute, and workspace-wide admin or lifecycle commands that can affect resources beyond the current turn |
| **Solution:** Classify omni commands into **explicit-scope** (safe) and **global-state** (unsafe). Whitelist only explicit-scope commands in the agent's `.claude/settings.local.json`. Teach the agent to derive `chat_id` from the NATS turn payload (env vars) or `omni where --json` — never from enumeration like `omni chats list`. | ||
|
|
||
| **Safe commands** (scope always explicit): | ||
|
|
||
| | Command | Why safe | | ||
| |---|---| | ||
| | `omni send --to <chat-id> ...` | Destination mandatory, ignores global state — use instead of `say` | | ||
| | `omni where --json` | Read-only; process-scoped by turn dispatcher | | ||
| | `omni turns get <turn-id>` | Explicit turn ID | |
There was a problem hiding this comment.
omni where --json is listed as safe, but there’s no omni where command documented under omni/cli/*.mdx in this repo. Either link/add the missing Omni CLI docs for where (including its JSON shape) or adjust this hack to rely only on documented commands/env vars.
| **Solution:** Classify omni commands into **explicit-scope** (safe) and **global-state** (unsafe). Whitelist only explicit-scope commands in the agent's `.claude/settings.local.json`. Teach the agent to derive `chat_id` from the NATS turn payload (env vars) or `omni where --json` — never from enumeration like `omni chats list`. | |
| **Safe commands** (scope always explicit): | |
| | Command | Why safe | | |
| |---|---| | |
| | `omni send --to <chat-id> ...` | Destination mandatory, ignores global state — use instead of `say` | | |
| | `omni where --json` | Read-only; process-scoped by turn dispatcher | | |
| | `omni turns get <turn-id>` | Explicit turn ID | | |
| **Solution:** Classify omni commands into **explicit-scope** (safe) and **global-state** (unsafe). Whitelist only explicit-scope commands in the agent's `.claude/settings.local.json`. Teach the agent to derive `chat_id` from the NATS turn payload (env vars), or by resolving the current turn via `omni turns get <turn-id>` using the turn ID from env — never from enumeration like `omni chats list`. | |
| **Safe commands** (scope always explicit): | |
| | Command | Why safe | | |
| |---|---| | |
| | `omni send --to <chat-id> ...` | Destination mandatory, ignores global state — use instead of `say` | | |
| | `omni turns get <turn-id>` | Explicit turn ID; use with the current turn ID from env to recover chat context safely | |
New Community Hack
Title: Omni + Genie-NATS — Context Isolation for Multi-User Agents
Category: Hooks & integration (Hack 9)
Problem: A genie-nats agent connected to a Telegram/WhatsApp/Discord instance via
omni connecthandles multiple users in parallel. The omni CLI exposes global-state verbs (omni say,omni open,omni use,omni react,omni history, ...) that operate on an "active chat". If the agent calls any of them, replies leak to the wrong user — context poisoning. Prompt discipline alone is insufficient.Solution:
send --to,where --json,turns get,chats get, etc.) and global-state (unsafe:say,open,history, all verb commands)..claude/settings.local.json.chat_idfrom NATS payload env vars (OMNI_CHAT_ID, etc.) oromni where --json— never enumerate viachats list/persons search.AGENTS.mdso the LLM self-limits.Key gotcha highlighted: Claude Code evaluates permissions in
deny → ask → alloworder — deny always wins regardless of specificity. A broaddeny: ["Bash(omni *)"]will silently kill a narrow allow rule. UsedefaultMode+ explicitallowlist instead.Benefit: Zero cross-user context poisoning with many concurrent chats on the same agent. Defense in depth — the harness blocks unsafe commands even if the LLM tries them.
When to use: Any genie-nats agent on an omni channel with multiple concurrent users (Telegram bots, WhatsApp, Discord), or any autonomous LLM exposed to a CLI with global/session state.
Includes:
sayvssendcomparisonsettings.local.jsontemplate with allow/deny ruleswhere --json<Warning>about deny precedence<Info>recommending documentation of the contract inAGENTS.mdSubmitted via
/genie-hacks contribute