Place orders via interactive confirmation (no bypass/passphrase) - #117
Merged
Conversation
Option A: placing a live order no longer requires bypass mode + the arm-bypass token + the dangerous-action passphrase. In confirm mode the agent now shows the previewed order and asks the operator; it places only on an explicit yes. Bypass mode is UNCHANGED and remains the headless path for future autonomous use.⚠️ THE RAILS ARE UNTOUCHED. Every order still runs the 15 hard rails FIRST; the confirmation is an ADDITIONAL human gate after they pass, never a replacement. A test pins that a vetoed order never reaches the prompt (rails run first, confirmation second). The executor was already built for this (`execute` takes `confirm_fn(preview) -> bool`); the agent just hardcoded it to None. This threads a real `confirm_fn` through `run_once`/`loop`/`_handle_exits` (all default None, so every existing caller and test is unchanged and still fails closed), and `keel agent` passes `_interactive_confirm` in confirm mode / None in bypass. `_interactive_confirm` renders the broker preview and prompts; it FAILS CLOSED on a non-TTY (a script/cron never trades unattended), same posture as everything else. Net: placing an order goes from "set-passphrase -> arm-bypass -> --bypass --passphrase" to "run keel agent, answer y" -- and is arguably safer for supervised use, since a human sees the actual order every time instead of pre-authorising a window. Tests: approved->places the BUY (+ its OCO bracket); declined->nothing; no confirm_fn->still nothing (backward compat); confirm_fn sees the preview; a rail veto never reaches the prompt; _interactive_confirm yes/no/non-TTY; the agent command wires _interactive_confirm in confirm and None in bypass. NOTE: the go-live runbook (PR #116, unmerged) still describes the old bypass/passphrase dance and must be rewritten against this once both land. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
eaitbrahim
force-pushed
the
feat/interactive-confirm
branch
from
July 21, 2026 22:43
40e0668 to
f67c553
Compare
This was referenced Jul 21, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Option A, as agreed: placing a live order no longer needs bypass mode, the arm-bypass token, or the dangerous-action passphrase. In confirm mode the agent shows the previewed order and asks; it places only on an explicit yes. Bypass mode is untouched and stays the headless path for future autonomous use.
The rails are untouched — this is important
Every order still runs the 15 hard rails first. The confirmation is an additional human gate after they pass, never a replacement. There's a test pinning that a rail-vetoed order never reaches the prompt — the rails run first, the human second. So removing the bypass ceremony did not weaken any hard limit.
Why this was small
The executor was already built for it —
executetakesconfirm_fn(preview) -> bool; the agent just hardcoded it toNone. This threads a realconfirm_fnthroughrun_once/loop/_handle_exits(all defaulting toNone, so every existing caller and test is unchanged and still fails closed), andkeel agentpasses an interactive prompt in confirm mode._interactive_confirmrenders the broker preview and asks; it fails closed on a non-TTY, so a script or cron never trades unattended.Net effect
Placing an order goes from:
to:
Arguably safer for supervised use — a human looks at the real order every single time instead of pre-authorizing a window.
Tests
approved → places the BUY (+ its OCO bracket); declined → nothing; no
confirm_fn→ still nothing (backward compat);confirm_fnsees the preview; a rail veto never reaches the prompt;_interactive_confirmyes/no/non-TTY; and theagentcommand wires the interactive prompt in confirm mode,Nonein bypass.Follow-ups (not in this PR)
config.yamlin the release + a seed-the-db command — coming as a separate PR.1266 tests pass (up from 1258), ruff clean.
🤖 Generated with Claude Code