Tell the model when a message was a steer

A message typed during a turn reaches the model at the next model call if
the turn has one left, and otherwise as the opening line of the next turn --
Claude's read out of the fifo after the turn ended, Codex's requeued when
turn/steer is refused. Read there it is indistinguishable from a reply, so
the model treats the answer it just gave as seen.

Both drivers now compose the text the CLI receives through
driver::message_body, which prefixes a note saying the message was written
without having seen the rest of that turn. The transcript still holds the
words that were typed; only the CLI's copy carries the note.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
iris-aiandClaude Opus 5 committed 2026-09-13 22:30:32 -04:00
1 parent 59ebd75b46
commit 3af2502982
4 files changed
+107 -18

No files matched your search

+10 -13
View File
@@ -503,7 +503,7 @@ impl Driver for ClaudeDriver {
// An image goes into the message itself; the model looks at it. Any
// other file stays where the upload put it and the message says where,
// because the CLI can read a file by path and a model cannot be handed
// a trace any other way. Named after the text, so the words come first.
// a trace any other way.
let mut files = Vec::new();
for id in &attachments {
let sent = if crate::media::media_type_for(id).is_some() {
@@ -517,18 +517,6 @@ impl Driver for ClaudeDriver {
});
}
}
let mut body = text.clone();
for path in files {
if !body.is_empty() {
body.push_str("\n\n");
}
body.push_str(&format!("Attached file: {}", path.display()));
}
if !body.is_empty() {
content.push(json!({"type": "text", "text": body}));
}
let line =
json!({"type": "user", "message": {"role": "user", "content": content}}).to_string();
let mut queue = self.queue.lock().unwrap();
// Saying so beats writing into a fifo that nothing is reading, which is
// what this used to do -- the message went nowhere and looked exactly
@@ -541,6 +529,15 @@ impl Driver for ClaudeDriver {
});
return;
}
// Built under the lock because whether this is a steer decides what the
// CLI is told, and the answer must be the same one the branch below acts
// on -- see `super::driver::message_body`.
let body = super::driver::message_body(&text, &files, queue.running);
if !body.is_empty() {
content.push(json!({"type": "text", "text": body}));
}
let line =
json!({"type": "user", "message": {"role": "user", "content": content}}).to_string();
if queue.running {
// Into the running turn, now. Announced when the CLI shows it has
// been round the model again -- see `Queue`.