wGrow
menu
Sensitive Prompts Belong Outside The Model Transcript
Infra & Security 27 August 2026 · 5 min

Sensitive Prompts Belong Outside The Model Transcript

By wGrow Project Team ·

Terminal Secrets Require PTY Interception, Not Better Prompts

VS Code’s terminal inline chat carries a small, unglamorous piece of plumbing: interception that keeps a masked password local and never lets it reach GitHub Copilot. A coding-agent terminal should treat masked input as local-only: the runtime detects the masked prompt, passes a placeholder to the model, and keeps the literal keystrokes out of the transcript entirely.

That’s not a UX nicety. It’s a baseline. Any team building a custom coding-agent runtime today — and at wGrow we build several — has to treat local secret interception as a mandatory architectural component, not a hardening pass you bolt on after something goes wrong. The logic is simple: masking secrets is an infrastructure job. It doesn’t belong in the system prompt, no matter how well-worded that prompt is.

PTY Interception Over Prompt Engineering

Over-the-shoulder view of an engineer typing at a dual-monitor workstation.

The first instinct, when this problem shows up, is to fix it with instructions: “never repeat passwords back to the user,” “redact anything that looks like a credential.” We tried variants of this on an internal agent runner. We killed the approach within a week.

The flaw is sequencing. By the time an instruction like that has any chance of working, the secret has already been tokenized into the model’s context window and shipped off to a provider’s inference endpoint. The transcript is compromised the moment the credential crosses the wire — what the model does with it afterward is beside the point. A well-behaved model that “chooses” not to echo the password back has still ingested it. That’s not a mitigated risk. That’s a breach with better manners.

So the fix has to sit below the model, at the terminal itself. When an agent runtime drives a pseudo-terminal (PTY), it can inspect terminal attributes through termios or the equivalent OS API. Calling tcgetattr on the PTY tells you whether the ECHO flag is currently disabled — and a disabled ECHO flag is the standard tell that the running process wants masked input. It’s the same mechanism getpass, sudo, ssh, and most legacy CLI password prompts rely on. Reliable for interactive credential prompts specifically. It won’t catch secrets passed through other channels — command-line arguments, environment variables sitting exposed in a process listing — those need their own controls.

Once the runtime sees ECHO off, it traps the following keystrokes locally, feeds them to the terminal process so the command still completes, and reports back to the model’s state manager with a <REDACTED_INPUT> token instead of the literal bytes. The terminal finishes its work. The model’s transcript stays clean. Nobody had to ask the LLM to behave itself.

Legacy Systems Demand Hard Intercepts

Interception Sequence
step 01
Detect disabled ECHO flag via termios
step 02
Trap raw keystrokes locally in PTY
step 03
Append <REDACTED> to model transcript

This isn’t a theoretical edge case for us. Two projects made it very concrete.

The first is WaterDoctor’s remote sensor field diagnostics CLI. Field technicians authenticate to the interactive shell with a supervisor PIN before running sensor calibration routines. The tool predates this agent work by years, and there’s no near-term plan to move it onto a secrets vault. Point an autonomous agent at that CLI to automate a diagnostic run, and without PTY-level interception, that PIN goes straight into whatever LLM provider backs the agent — logged and retained per that provider’s policy, out of our control the second it leaves the process.

The second is a 2017 deployment script built for a public-sector vendor portal integration (the agency isn’t named here, by policy). The script authorizes payload drops through an interactive two-factor prompt: type the OTP, hit enter, deployment proceeds. Nobody has ever rewritten it to accept a token from an environment variable. It was built for a human sitting at a keyboard, and nine years later it still expects one.

Neither system is unusual, and that’s the point. Legacy scripts of this vintage rarely externalize credentials to config or vault injection — most were never designed with automation in mind to begin with. They halt, print a prompt, and wait on stdin. Any agent tasked with maintaining or operating this kind of infrastructure will hit a masked prompt eventually — not as an exception, but as a routine step in the job.

Building the Fake CLI Regression Test

Minimalist isometric illustration of a server node filtering data packets.

Given that, this belongs in agent CI as a standard test, not something you scramble to add after an incident report. And it’s cheap to build. Write a short Python script using getpass that simulates a legacy CLI: prompt for a fake supervisor PIN, then a fake OTP, then print a success message on match.

Run the agent against it with instructions to complete the fake authentication and finish the task. Then pull the full LLM transcript and the agent’s trace logs — every payload sent to the provider. The test fails the moment the raw PIN or OTP string shows up anywhere in that payload. It passes only when the terminal session completes successfully and the transcript shows the redaction placeholder standing in for the credential. Five minutes of work, and it catches a class of failure that manual review reliably misses — because the leak happens silently, inside a request body nobody bothers to inspect by default.

Graceful Handoff at the Redaction Layer

test_legacy_cli.py
1 import getpass
2 import sys
3
4 def main():
5 print('WaterDoctor Diagnostic Shell v2.4')
6 pin = getpass.getpass('Enter Supervisor PIN: ') ← ①
7 if not pin:
8 sys.exit(1)
9 print('Authentication successful.')
10
  1. Disables terminal ECHO, which the runtime must detect

Interception is only half the job. An agent can’t guess a supervisor PIN, and it definitely can’t generate a valid OTP — so detecting a masked prompt has to trigger a defined handoff, not a stall or a crash. The runtime needs to suspend the agent thread cleanly and route to a human-in-the-loop step that can supply the credential without it ever touching the model’s context. That handoff carries its own overhead — someone has to be reachable to supply the credential — but it’s a bounded, predictable cost. Compare that to an unbounded credential leak, and it’s not really a close call.

As agent runtimes get pointed at more legacy infrastructure — and let’s be honest, that’s most of the infrastructure that actually needs automating — interactive authentication gates like these will keep showing up. Build the termios-level check once, test it with a fake CLI in CI, and that guarantee holds no matter which model ends up sitting behind the agent. Which is really the whole point: this control shouldn’t depend on the LLM at all.