fix(agent): send/ask teach the verbatim body form first, and take it on stdin (DIVE-4421) - #933
Merged
Merged
Conversation
…on stdin (DIVE-4421) The corruption a customer reported happens in the CALLER'S shell, before this CLI runs: a backtick-quoted verb runs as command substitution and its words are deleted, `US$4,500` becomes `US,500`, and the send still prints OK. The safe form (--message-file, DIVE-2627) already existed and was documented last in the usage line and in a parser comment — a warning that is reachable, not effective. - usage + `agent send|ask --help` lead with --message-file and say what the inline forms do to your text (--help used to die on `unknown flag`) - --message-file=- reads the body from stdin, via _read_prose_file, so a quoted heredoc needs no temp file and every *-file prose flag gains it at once - agent ask gets --message-file (it had --message= and no file form at all) - a send-side nudge naming --message-file when an inline body still carries a backtick or $; it is a reminder, NOT a detector, and says so - the requested receive-side artifact warning is declined, reason on the row tests/agent_send_verbatim_unit.sh (25 arms) grades the RENDERED help, a byte-identical stdin round trip, a mutation arm on the shipping reader, and the DIVE-2627 refusals unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Two of a customer's agents independently lost text out of a2a messages within two hours — a backtick-quoted function name vanished to command substitution, and
US$4,500arrived asUS,500($4= empty positional). Neither sender saw an error; both deliveries printed OK.The premise "there is no safe form" was wrong:
--message-file=<path>has existed since DIVE-2627 and reads the body verbatim. The defect is discoverability — the usage line taught the two shell-interpolated forms first and the safe one last, and nothing at the point of typing said the shell expands--messagebefore 5dive ever sees it. DIVE-2627's warning lived in a parser comment; a warning that is reachable is not a warning that is effective.What shipped (one commit, 4fb717d)
main.shandagent send|ask --helpnow list--message-file=<path>first, with--message=<text>annotated as short plain text only and naming$, backticks and$( )explicitly.agent send --helpandagent ask --helpused to die onunknown flag: --help; they render now.--message-file=-reads stdin, via_read_prose_file, so every*-fileprose flag in the CLI gains it and the stdin read is the SAMEread -r -d ''as the file read — no second code path to drift.--messagebody whose literal argv still carries a backtick or$prints a one-line warn naming--message-file. Those survive only when the caller quoted them, so it has no false positives on the dangerous cases — and it is silent on the already-corrupted ones. That is stated in the comment: it is a reminder, not a detector.Request 2 declined as the row specified (receive-side warn on "empty expansion artifacts" like
US,500, a lone backtick, an unbalanced quote). It is a by-name artifact list — the same incomplete-list trap the report itself names for "just escape it" — andUS,500is a legal string. The reason sits in the code comment beside the nudge so the next reader cannot re-add it by accident.Scope added beyond the row, and why
agent askwas checked as the row asked, and it does not share the body flags — it had--message=and no file form at all. Teaching "the file form is the safe one" in its usage while that form did not exist would have been a lie on the verb whose bodies are longer (a question with code in it). Soaskgot--message-fileplus the dupe and positional guards that come with it, rather than a usage line pointing at nothing.Correction to acceptance clause (c)
The row asks that a real path "still refuses a symlink / a file another seat owns (DIVE-2627 arms unchanged)". No such check exists on main and none was added.
_read_prose_filegates on-e,-f|-p|-cand-ronly, and-fFOLLOWS a symlink, so a symlink to a readable regular file is accepted today exactly as before;tests/prose_file_flags_unit.shhas no symlink or ownership arm either. Arm (c) therefore grades the refusals that DO exist and are unchanged — missing path, non-regular path, empty file,--message-filevs--message,--message-filevs positional — and the harness records the discrepancy in place rather than asserting a guard that never shipped. If seat-ownership on a prose file is actually wanted, it is a new row against_read_prose_file, not a clause of this one.Evidence worth keeping
The first draft of the help text wrote
and `-` reads it from stdinand the RENDERED help said "and reads it from stdin" —usage()is an unquotedcat <<USAGEheredoc, so the text teaching people to avoid command substitution was itself eaten by command substitution. A grep of the source would have passed. That is why arm (a) builds the bundle and grades the rendered bytes, and it is the customer's own defect class one layer in.Tests
tests/agent_send_verbatim_unit.sh, 25 arms, all green — including a control body carrying every hazard at once (US$4,500, backticks,$HOME,$(date)) delivered byte-identical over stdin.tests/prose_file_flags_unit.sh21/21 unchanged; 10 sibling send/ask/envelope harnesses green; shellcheck-S error --shell=bashclean on all three changed sources. Pre-push rail green (3/3 changed harnesses).Residual I am not signing
No live delivery into a real agent's pane. The bundle smoke proves stdin is consumed and the body survives parsing — it stops at the agent lookup. What is unproven is the tmux
send-keysleg for a multi-line stdin body, which is unchanged by this diff: the body reaches the existing wrap+inject path identically to a--message-file=<path>body, which DIVE-2627 already shipped.Not done, deliberately:
README.md:363/README.zh-CN.md:339still show5dive agent send <name> <text>in the quick-start. That is the legitimate use of the inline form (short plain prose), and the row asks for the fix on the tool, not in a doc.Row: DIVE-4421. Related: DIVE-2627, DIVE-4412, DIVE-4413, DIVE-4415, DIVE-4416, DIVE-4419.
🤖 Generated with Claude Code