Skip to content

Guard does not see commands typed into an already-running shell (Codex write_stdin, Antigravity send_command_input) #475

Open

Description

@CryptoJones

Bug

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.

Activity

  1. added a commit that references this issue on Oct 10, 2026
  2. CryptoJones commented on Oct 10, 2026

    @CryptoJones
    OwnerAuthor

    🏭 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfactory:groomedVetted by the factory; eligible for an agent to implement

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions