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
Guard does not see commands typed into an already-running shell (Codex write_stdin, Antigravity send_command_input) #475
The guard judges a shell command when the agent starts it: Bash, or any name in guard.SHELL_TOOL_NAMES since #473. Some harnesses can also send text to a shell that is already running:
Harness
Tool
Text field
Codex
write_stdin
chars
Antigravity
send_command_input
Input
Repro: start an interactive shell (Codex exec_command {"cmd": "bash"}, or an Antigravity run_command left running), then send git commit -am x or a hard-rule command through the stdin tool. The guard sees a non-shell tool with no command. So the hard rules, the repo-work gate and the commit freshness gate never judge that text.
Found by the Flatline Roundtable review of #474. The tool names and fields are from the reviewer's memory of each harness's schema. Verify them against live payloads before relying on them.
Possible fix
Add the stdin tools to SHELL_TOOL_NAMES, and read chars / Input in guard.shell_command_text, so the text is judged like a shell command.
The text can be partial (one line of a heredoc, or a keystroke). Decide whether to judge each write on its own or deny dangerous patterns conservatively.
Test: an interactive shell plus a stdin write of a hard-rule command is denied, in each harness that has a stdin tool.
🏭 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 recommend groomed, narrowed to Antigravity. The Codex half rests on a false premise.
Codex: write_stdin never reaches the guard. OpenAI's Codex hooks documentation (https://learn.chatgpt.com/docs/hooks) says: "write_stdin is transport for an existing unified-exec session. It doesn't run PreToolUse again when it sends input or polls a command that already passed PreToolUse." Codex also reports exec_command to hooks as Bash with tool_input.command. So adding write_stdin to guard.SHELL_TOOL_NAMES (src/omind/guard.py:1332), or reading chars in guard.shell_command_text (src/omind/guard.py:514), would be dead code. No hook event ever carries those fields.
Codex can only be closed at the launch: deny exec_command when it starts a bare interactive shell (bash, sh, zsh, pwsh, or a TTY session). That is a new hard policy about what agents may run, and it binds the guard beyond this bug. The owner should decide it in a separate issue if they want it.
Antigravity: the gap is real and the fix is small. omind registers Antigravity's hook for every tool (matcher: "*", src/omind/agents.py:2537/2549), so a send_command_input call does reach omind guard adapter --harness agy. Today:
adapters._derive_command (src/omind/adapters.py:47) reads command, CommandLine and cmd through shell_command_text, then only lowercase args / input. A capital-Input argument gives an empty command.
No hard rule matches empty text, and the tool is not mapped to Bash, so neither the repo-work gate nor the commit freshness gate judges it.
I couldn't check a live payload: neither agy nor codex is installed here. The field name is still credible. Antigravity's schema already matches the Windsurf/Cascade naming omind reads today (CommandLine, Cwd, TargetFile, AbsolutePath), and Cascade's stdin tool is send_command_input with CommandId / Input. Google's forum also describes Antigravity agents sending text strings to background terminals (thread).
On partial input (the issue's open question): judge each write on its own, like any shell command. A command split across keystroke writes can still get through. That is a known limit of a text-matching guard, and building a stdin reassembly buffer is not worth it. The whole-line case, where an agent types a full command into a running shell, is the common one and is what this closes.
Factory grooming notes
Scope: Antigravity send_command_input only. Codex is out. Its docs say write_stdin never fires PreToolUse (https://learn.chatgpt.com/docs/hooks), so do NOT add write_stdin to SHELL_TOOL_NAMES and do NOT read chars. Both would be dead code. Do not add a Codex "deny interactive shell launch" rule in this PR either. That is a separate policy decision for the owner.
Change:
Add "send_command_input" (comment: Antigravity, stdin to a running command) to guard.SHELL_TOOL_NAMES (src/omind/guard.py:1332).
Make guard.shell_command_text (guard.py:514) return the Input string. This is the single place command text is read: adapters._derive_command and record_freshness_outcome both go through it. Do not add a second extraction path in adapters.py. Keep the existing key order so command/CommandLine/cmd still win. Update the docstring.
Add send_command_input to the fail-closed tool-name regex in src/omind/omi-guard.sh:51, so the shell fallback stays in step with SHELL_TOOL_NAMES.
Judge each write on its own. No buffering or reassembly across writes. Antigravity agents often end Input with a trailing newline, so make sure text like "git commit -am x\n" still matches the same rules and gates as the bare command.
Tests (tests/test_adapters.py, alongside the existing agy run_command cases around :413-491 and the #473 SHELL_TOOL_NAMES parametrization at :786 / tests/test_guard.py:2626):
agy event {"toolCall": {"name": "send_command_input", "args": {"CommandId": "abc", "Input": "gh repo delete a/b\n"}}, "conversationId": ...} is denied by a hard rule.
The same event with "git commit -am x\n" hits the commit freshness gate, the same as run_command does.
A harmless input ("y\n", "q") is allowed and does not raise.
shell_command_text unit case for Input.
Run them through omind guard adapter --harness agy with a piped JSON event as the live check. No agy binary is available, so that is the end-to-end path to exercise.
Update the BACKLOG.md #475 entry (and CHANGELOG): say Codex write_stdin is not interceptable per the Codex hooks docs, and Antigravity is fixed.
Activity
docs(backlog): add #475, move #428 to not planned (#476)
CryptoJones commentedon Oct 10, 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 recommend groomed, narrowed to Antigravity. The Codex half rests on a false premise.
Codex:
write_stdinnever reaches the guard. OpenAI's Codex hooks documentation (https://learn.chatgpt.com/docs/hooks) says: "write_stdin is transport for an existing unified-exec session. It doesn't run PreToolUse again when it sends input or polls a command that already passed PreToolUse." Codex also reportsexec_commandto hooks asBashwithtool_input.command. So addingwrite_stdintoguard.SHELL_TOOL_NAMES(src/omind/guard.py:1332), or readingcharsinguard.shell_command_text(src/omind/guard.py:514), would be dead code. No hook event ever carries those fields.Codex can only be closed at the launch: deny
exec_commandwhen it starts a bare interactive shell (bash,sh,zsh,pwsh, or a TTY session). That is a new hard policy about what agents may run, and it binds the guard beyond this bug. The owner should decide it in a separate issue if they want it.Antigravity: the gap is real and the fix is small. omind registers Antigravity's hook for every tool (
matcher: "*",src/omind/agents.py:2537/2549), so asend_command_inputcall does reachomind guard adapter --harness agy. Today:adapters._derive_command(src/omind/adapters.py:47) readscommand,CommandLineandcmdthroughshell_command_text, then only lowercaseargs/input. A capital-Inputargument gives an empty command.Bash, so neither the repo-work gate nor the commit freshness gate judges it.I couldn't check a live payload: neither
agynorcodexis installed here. The field name is still credible. Antigravity's schema already matches the Windsurf/Cascade naming omind reads today (CommandLine,Cwd,TargetFile,AbsolutePath), and Cascade's stdin tool issend_command_inputwithCommandId/Input. Google's forum also describes Antigravity agents sending text strings to background terminals (thread).On partial input (the issue's open question): judge each write on its own, like any shell command. A command split across keystroke writes can still get through. That is a known limit of a text-matching guard, and building a stdin reassembly buffer is not worth it. The whole-line case, where an agent types a full command into a running shell, is the common one and is what this closes.
Factory grooming notes
Scope: Antigravity
send_command_inputonly. Codex is out. Its docs saywrite_stdinnever fires PreToolUse (https://learn.chatgpt.com/docs/hooks), so do NOT addwrite_stdinto SHELL_TOOL_NAMES and do NOT readchars. Both would be dead code. Do not add a Codex "deny interactive shell launch" rule in this PR either. That is a separate policy decision for the owner.Change:
Inputstring. This is the single place command text is read: adapters._derive_command and record_freshness_outcome both go through it. Do not add a second extraction path in adapters.py. Keep the existing key order socommand/CommandLine/cmdstill win. Update the docstring.Inputwith a trailing newline, so make sure text like "git commit -am x\n" still matches the same rules and gates as the bare command.Tests (tests/test_adapters.py, alongside the existing agy run_command cases around :413-491 and the #473 SHELL_TOOL_NAMES parametrization at :786 / tests/test_guard.py:2626):
Input.Run them through
omind guard adapter --harness agywith a piped JSON event as the live check. No agy binary is available, so that is the end-to-end path to exercise.Update the BACKLOG.md #475 entry (and CHANGELOG): say Codex write_stdin is not interceptable per the Codex hooks docs, and Antigravity is fixed.