You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Grok Claude-hook fast path does not count a use_tool recall as a consult #472
Grok Build on this machine runs the Claude PreToolUse hook (omi-guard.sh), not only omind guard adapter --harness grok. v10.3.0 translates camelCase Grok payloads in Python. The bash fast path still reads .tool_name only.
A memory recall arrives as use_tool with the real tool nested at tool_input.tool_name (omi__recall-note) and the arguments at tool_input.tool_input. The script does not unwrap that, so the call is not mcp__omi__* and is not recorded as a consult. The repo-work gate then denies the recall that would have cleared it.
The same gap hits a snake_case-only event (tool_name: use_tool or run_terminal_command, no toolName). payload_is_grok is false for that shape, so translate_event("claude", event) returns it unchanged and PostToolUse does not record the consult either. run_terminal_command also skips the Bash hard rules on the fast path.
Expected
use_tool / CallMcpTool whose inner tool is omi__recall-note (snake_case or camelCase) is a consult of the inner note name. list-notes still does not clear the gate. create-note / edit-note / delete-note / restore-note still are not consults.
🏭 Factory groom — groomed. An agent can pick this up as written. Edit the description or reply here and it gets reviewed again on a later tick.
I'd mark this one groomed: the bug is real, it's confined to one subsystem (the guard), and one PR can fix it. One part is worse than the issue says. I checked all of this by reading the code at 387873f (v10.3.0). I didn't run anything.
What the code shows
The shell fast path only knows the Claude field names.src/omind/omi-guard.sh reads the tool with jq -r '.tool_name // empty' and never unwraps use_tool or CallMcpTool. The Python translation from feat(grok): support the Grok Build client #471 (harness._translate_grok, harness.py:399-450) only runs when jq is missing, when the shell hands off to guard adapter, or in the PostToolUse hooks.
A snake_case use_tool recall reaches the generic guard check as tool use_tool. That is not a consult, so the gate denies it.
A camelCase event has no .tool_name at all, so tool comes out empty and gets the same treatment.
The camelCase case also lets destructive shell commands through. Take a camelCase {"toolName":"run_terminal_command","toolInput":{"command":"gh repo delete …"}}. It never reaches the [ "$tool" = "Bash" ] branch. It drops to the generic delegation with command:"", so the guard's hard blocks on dangerous commands (the seed rules, like "never delete a repo") are never checked. tests/test_grok_client.py::test_claude_hook_denies_grok_payload_as_json only covers the Python path, so it doesn't catch this. Fail-open must never skip a hard block, which makes this the most important part of the fix.
Snake_case-only Grok events miss the translation in Python too.payload_is_grok (harness.py:385) only looks for hookEventName, toolName or toolInput. So translate_event("claude", …) passes the event through unchanged. PostToolUse (hooks.py) then never lifts the inner omi__recall-note, and verify.py's consult extraction never sees it.
toolName, sessionId and toolInput as fallbacks for the snake_case fields;
the use_tool / CallMcpTool wrapper, unwrapped to the inner tool and its arguments;
omi__x, omi_x and bare tool names mapped to mcp__omi__x;
run_terminal_command treated as Bash.
Make translate_event handle snake_case-only use_tool, CallMcpTool and run_terminal_command events.
Add regression tests for each case in the issue's "Expected" list.
There's nothing for a human to decide here: no note format, MCP schema or CLI surface changes.
Factory grooming notes
Scope: guard subsystem only — src/omind/omi-guard.sh, src/omind/harness.py (payload_is_grok / _translate_grok), and src/omind/adapters.py only if you delegate (see below). No new subcommand, hook, or harness.
omi-guard.sh fast path (the main bug):
Read the tool as .tool_name // .toolName, the input as .tool_input // .toolInput, the session as .session_id // .sessionId, the transcript as .transcript_path // .transcriptPath.
If the tool is use_tool or CallMcpTool, lift the inner name (tool_name // toolName // name) and the inner args (tool_input // arguments // toolInput). Then map names the way _translate_grok does: omi__x -> mcp__omi__x, omi_x -> mcp__omi__x, bare recall-note/search-vault/... -> mcp__omi__x.
Map run_terminal_command -> Bash BEFORE the Bash branch, so the hard rules run and fail closed exactly as for Claude's Bash. Today a camelCase run_terminal_command reaches the generic guard check with command:"" and skips the hard rules. Add a test where gh repo delete sent this way is blocked (exit 2).
Keep the existing exclusions: list-notes/list-tags/graph-*/backlinks are allowed but do not clear the gate; create/edit/delete/restore are ordinary gated actions. Consult targets must come from the INNER args.
harness.py: make a snake_case-only event with tool_name in _GROK_DISPATCHERS or == "run_terminal_command" go through _translate_grok. Do NOT widen detection to the whole _GROK_TOOL_ALIASES table: read_file (and other lowercase names) overlap Gemini's tool names, and gemini events also pass through payload_is_grok. Keep test_claude_event_is_not_treated_as_grok green and add a plain-Claude-unchanged case.
Tests (CI runs on real Windows):
Translation tests in tests/test_grok_client.py run everywhere.
Shell-hook tests use the rendered-hook helper in tests/test_guard.py (~line 976) and carry the existing _HOOK_TESTABLE skip.
Cover: snake_case and camelCase use_tool -> omi__recall-note clears the gate (a following Edit is allowed); list-notes via use_tool does not; create-note via use_tool is not a consult; run_terminal_command destructive command blocked; plain Claude event unchanged.
PostToolUse: a snake_case use_tool recall is recorded as a consult by verify.
Live check: pipe each payload through a rendered omi-guard.sh with a temp state dir and confirm exit codes and the gate sentinel.
Activity
CryptoJones commentedon Oct 9, 2026
🏭 Factory groom — groomed. An agent can pick this up as written. Edit the description or reply here and it gets reviewed again on a later tick.
I'd mark this one groomed: the bug is real, it's confined to one subsystem (the guard), and one PR can fix it. One part is worse than the issue says. I checked all of this by reading the code at
387873f(v10.3.0). I didn't run anything.What the code shows
src/omind/omi-guard.shreads the tool withjq -r '.tool_name // empty'and never unwrapsuse_toolorCallMcpTool. The Python translation from feat(grok): support the Grok Build client #471 (harness._translate_grok,harness.py:399-450) only runs when jq is missing, when the shell hands off toguard adapter, or in the PostToolUse hooks.use_toolrecall reaches the genericguard checkas tooluse_tool. That is not a consult, so the gate denies it..tool_nameat all, sotoolcomes out empty and gets the same treatment.{"toolName":"run_terminal_command","toolInput":{"command":"gh repo delete …"}}. It never reaches the[ "$tool" = "Bash" ]branch. It drops to the generic delegation withcommand:"", so the guard's hard blocks on dangerous commands (the seed rules, like "never delete a repo") are never checked.tests/test_grok_client.py::test_claude_hook_denies_grok_payload_as_jsononly covers the Python path, so it doesn't catch this. Fail-open must never skip a hard block, which makes this the most important part of the fix.payload_is_grok(harness.py:385) only looks forhookEventName,toolNameortoolInput. Sotranslate_event("claude", …)passes the event through unchanged. PostToolUse (hooks.py) then never lifts the inneromi__recall-note, andverify.py's consult extraction never sees it.adapters.normalize_action(adapters.py:105) treats everymcp__omi__*call as a consult, includinglist-notesandcreate-note.omi-guard.shandverify.py:370-388both refuse those (verifier: credit guard-demanded note reads; stop scoring vault writes as consults; log judgement context on off-topic denials #148, and the anti-dodge rule from Consult-gate dodge: reading the vault index (index.md) clears the gate without a real consult #109). So simply routing Grok payloads from the shell toomind guard adapterwould bring back the gate-dodge the issue says must not come back.What one PR should do
omi-guard.shread the Grok shapes:toolName,sessionIdandtoolInputas fallbacks for the snake_case fields;use_tool/CallMcpToolwrapper, unwrapped to the inner tool and its arguments;omi__x,omi_xand bare tool names mapped tomcp__omi__x;run_terminal_commandtreated asBash.translate_eventhandle snake_case-onlyuse_tool,CallMcpToolandrun_terminal_commandevents.There's nothing for a human to decide here: no note format, MCP schema or CLI surface changes.
Factory grooming notes
Scope: guard subsystem only — src/omind/omi-guard.sh, src/omind/harness.py (payload_is_grok / _translate_grok), and src/omind/adapters.py only if you delegate (see below). No new subcommand, hook, or harness.
omi-guard.sh fast path (the main bug):
.tool_name // .toolName, the input as.tool_input // .toolInput, the session as.session_id // .sessionId, the transcript as.transcript_path // .transcriptPath.use_toolorCallMcpTool, lift the inner name (tool_name // toolName // name) and the inner args (tool_input // arguments // toolInput). Then map names the way_translate_grokdoes:omi__x->mcp__omi__x,omi_x->mcp__omi__x, barerecall-note/search-vault/... ->mcp__omi__x.run_terminal_command->BashBEFORE the Bash branch, so the hard rules run and fail closed exactly as for Claude's Bash. Today a camelCaserun_terminal_commandreaches the genericguard checkwithcommand:""and skips the hard rules. Add a test wheregh repo deletesent this way is blocked (exit 2).omind guard adapter --harness claude, which already translates and renders FMT_GROK. If you do, first makeadapters.normalize_actionstop marking list-notes/list-tags/graph-*/backlinks and create/edit/delete/restore-note as consults (today it marks everymcp__omi__*, adapters.py:105). Otherwise that path re-opens verifier: credit guard-demanded note reads; stop scoring vault writes as consults; log judgement context on off-topic denials #148/Consult-gate dodge: reading the vault index (index.md) clears the gate without a real consult #109. Keep the shell's fail-closed handling for any rc other than 0/2 on shell commands.harness.py: make a snake_case-only event with
tool_namein_GROK_DISPATCHERSor== "run_terminal_command"go through_translate_grok. Do NOT widen detection to the whole_GROK_TOOL_ALIASEStable:read_file(and other lowercase names) overlap Gemini's tool names, and gemini events also pass throughpayload_is_grok. Keeptest_claude_event_is_not_treated_as_grokgreen and add a plain-Claude-unchanged case.Tests (CI runs on real Windows):
_HOOK_TESTABLEskip.use_tool->omi__recall-noteclears the gate (a following Edit is allowed);list-notesviause_tooldoes not;create-noteviause_toolis not a consult;run_terminal_commanddestructive command blocked; plain Claude event unchanged.use_toolrecall is recorded as a consult by verify.fix(guard): count Grok use_tool recalls as consults; run the freshnes…