Max Nardit
← Artikel
6 mindev

Claude Code Code Mode: Der Auto Mode prüft jetzt das Skript, nicht jeden Tool-Aufruf

In einer Code-Mode-Session liest der Classifier des Auto Mode das Skript einmal und nie die Aufrufe darin. Ein Pfad oder Host, der erst zur Laufzeit entsteht, bekommt von ihm keine Prüfung. Ein PreToolUse-Hook, der nicht startet oder in einen Timeout läuft, lehnt den Aufruf jetzt ab, einer mit Exit Code 1 lässt ihn aber weiter durch. Echte Werte sehen nur deine Regeln, deine Hooks und die Sandbox.

Inhalt

Claude Code 2.1.323, erschienen am 12. November, bringt Code Mode, ein experimentelles Opt-in. Normalerweise fragt Claude einen Tool-Aufruf nach dem anderen an, liest das Ergebnis und entscheidet dann, was als Nächstes kommt. Im Code Mode schreibt es ein Stück JavaScript, das die Aufrufe selbst macht, und ein Tool namens REPL führt diesen Code in deiner Session aus. Das ist das Beispiel aus der Dokumentation, für ein höheres Retry-Limit:

javascript
const files = await $`rg -l "function retryPolicy" src`.text();
const target = files.trim().split("\n")[0];
await edit(target, "maxAttempts: 3", "maxAttempts: 5");
({ edited: target, tests: $`npm test -- retryPolicy`.nothrow() });

Die Dokumentation empfiehlt es für Arbeit mit vielen zusammenhängenden Tool-Aufrufen: eine Änderung über viele Dateien, eine Suche, die gefiltert wird, bevor Claude sie liest, Befehle, die parallel laufen können. Im Gespräch siehst du die Tool-Aufrufe, nicht das Skript; der Code liegt hinter Ctrl+O in eingeklappten Code-Zeilen. Lies den Abschnitt zu Berechtigungen, bevor du es einschaltest, denn der Classifier des Auto Mode prüft jetzt weniger, und ein kaputter Hook verhält sich anders. Die Fakten unten stammen aus der Dokumentation und den Release Notes. Ich habe Code Mode nicht laufen lassen, nichts hier ist also eine Messung.

Was ändert Claude Code Code Mode an den Berechtigungsprüfungen?

Im Auto Mode prüft der Classifier jedes Stück Code einmal, bevor es läuft, und nicht die Tool-Aufrufe darin. Die Dokumentation benennt den Preis: Er „doesn’t see values computed at run time, such as a path taken from search output, so keep rules or hooks for calls you must control.“ Jeder Aufruf aus dem Code läuft weiter durch deine deny- und ask-Regeln und deine PreToolUse-Hooks.

Die Seite zu den Permission Modes zeigt, wie sehr sich der Classifier auf wörtliche Werte stützt. Standardmäßig blockiert er rm -rf "$VAR", wenn die Variable nirgends zugewiesen ist, wo er sie sehen kann, weil er „can’t tell what the command would delete“, und die Sperre fällt weg, sobald Claude das Löschen mit dem echten Pfad im Befehl erneut ausführt. Im Code Mode gibt es keinen späteren Aufruf, in dem dieser Pfad zur Prüfung auftaucht: Der Classifier hat das Skript gelesen, und im Skript stand eine Variable. (Das edit aus dem Beispiel oben wäre ohnehin nicht beim Classifier gelandet. Änderungen innerhalb deines Arbeitsverzeichnisses genehmigt der Auto Mode automatisch, mit oder ohne Code Mode.)

In den anderen Berechtigungsmodi startet der Code ohne Prüfung, und du bekommst für jeden Aufruf die übliche Abfrage, wenn dieser Modus eine stellen würde. Unter bypassPermissions gibt es keinen Classifier und kaum eine Abfrage, dann stehen nur deine Regeln und Hooks im Weg. Um das Skript selbst zu prüfen, hängst du einen PreToolUse-Hook mit dem Matcher REPL ein und liest das JavaScript aus tool_input.code. Dieser Hook liest denselben Text wie der Classifier, mit demselben blinden Fleck. Ein Hook auf Bash oder Edit ist etwas anderes: Er feuert bei jedem Aufruf aus dem Code, mit dem aufgelösten Argument im Input. Das schließe ich aus der Aussage der Dokumentation, dass jeder Aufruf durch die Hooks geht; getestet habe ich es nicht.

Wie verhalten sich Claude Code PreToolUse-Hooks im Code Mode?

Sie lehnen in mehr Fällen ab als bisher, aber nicht in allen. Außerhalb des Code Mode ist ein command- oder http-Hook, der nicht startet, abstürzt oder in einen Timeout läuft, ein nicht blockierender Fehler, und der Aufruf geht durch die normalen Berechtigungsprüfungen weiter, solange du nicht "onFailure": "block" setzt. Diesen Standard habe ich für die Hooks-Anleitung getestet: Ein Hook, der auf ein fehlendes Skript zeigte, ließ den Aufruf laufen.

Im Code Mode lehnt ein PreToolUse-Hook einen Aufruf aus Claudes Code ab, wenn er nicht starten konnte, abgebrochen wurde, mit 126, 127 oder einem Code über 128 endete, einen HTTP-Fehler oder ungültige Ausgabe lieferte, auch ohne gesetztes onFailure. Die Fehlermeldung lautet the hook failed. Auch ein Timeout lehnt ab, mit the hook did not finish, und diese Regel reicht über den Code hinaus: Sobald Claude in der Session Code ausgeführt hat, lehnt ein Hook, der in einen Timeout läuft, jeden Tool-Aufruf ab.

Zwei Fälle gehen weiter durch. Ein Hook, der mit einem gewöhnlichen Code wie 1 endet, und genau das tut eine nicht abgefangene Python-Exception, lässt den Aufruf weiter durch, solange du nicht "onFailure": "block" setzt. Ein Guard, der intern kaputtgeht und trotzdem mit 0 endet, wie der jq-Guard aus der Anleitung, lässt den Aufruf wie bisher durch. Für command- und http-Hooks stellt "onFailure": "continue" das alte Verhalten wieder her.

Auch Prompt-Filter ändern sich. In einer Session, in der Code im Hintergrund laufen kann, laufen command-, http- und mcp_tool-Hooks auf UserPromptSubmit auch auf der Ausgabe des Codes, mit source gleich poll_event. Ein Filter, der für deine Eingaben geschrieben wurde, kann jetzt zurückhalten, was dein Build ausgegeben hat.

Was passiert mit einer unbeaufsichtigten Claude-Code-Session im Code Mode?

Code wartet im Vordergrund bis zu zehn Minuten, Abfragen eingeschlossen, dann stoppt er. Code im Hintergrund kann bis zu zwei Stunden laufen, aber nur in einer Session, die im Auto Mode oder in bypassPermissions gestartet wurde.

Startet Claude im Auto Mode neuen Code, während älterer Code noch läuft, gilt der ältere Code nicht mehr als geprüft, und Claude Code fragt dich zu seinen verbleibenden Aufrufen, die eine bräuchten. Ohne verbundene IDE zählt die Abfrage herunter; das Beispiel in der Dokumentation lautet „Claude Code will automatically block this request in 1:59, to avoid holding up an unattended session“. Verpasst du sie, wird dieser Aufruf abgelehnt. Ebenso wird jeder spätere Aufruf abgelehnt, der einen Countdown zeigen würde, und zwar ohne Abfrage, bis jemand Claude eine Nachricht schickt.

Ablehnen ist besser, als ungeprüfte Aufrufe laufen zu lassen. Für eine Session, die niemand beobachtet, halte ich das deshalb für die richtige Art zu scheitern. Was die Dokumentation nicht sagt: wie eine Ablehnung gemeldet wird, abgesehen von dem Fehler, den Claude zurückbekommt. Bis das jemand testet, würde ich annehmen, was ich bei denyInsteadOfPrompting gefunden habe: Die interaktive Session ging zurück in den Leerlauf, nichts meldete einen Fehler, und die Ablehnung stand nur in der Session selbst, solange kein Hook sie anderswo festhielt.

Es kommen ohnehin nur wenige Sessions dafür in Frage. Code Mode läuft nur in einer interaktiven claude-Session im Terminal, mit direkter Verbindung zu Anthropic und einem unterstützten Modell: kein -p, kein Agent SDK, keine Hintergrund-Sessions oder Remote Control, kein Gateway oder Cloud-Anbieter, keine Zero Data Retention und nicht zusammen mit einem Mod, der Tool-Aufrufe prüft. Den Rest listet die Dokumentation auf.

Wer entscheidet, ob Claude Code Code Mode an ist?

Du, mit CLAUDE_CODE_EXPERIMENTAL_CODE_MODE=1 in deiner Shell oder in ~/.claude/settings.json. Claude Code ignoriert die Variable in den Settings eines Projekts, ein geklontes Repository kann es also nicht einschalten. Eine Organisation kann Code Mode mit 0 in den Managed Settings sperren, wirksam für jeden Nutzer nach dem nächsten Neustart, aber sie kann es nicht zwangsweise einschalten.

Kann man dem Claude Code Auto Mode mit eingeschaltetem Code Mode trauen?

In einer Code-Mode-Session prüft der Classifier weniger als außerhalb. Er beurteilt, was ein Skript vorhat, nie die Werte, die das Skript erzeugen wird, und bei einem Löschen oder einem Upload entscheidet das Ziel. Als ich Egress-Kontrollen danach geordnet habe, was das Modell nicht wegdiskutieren kann, kam in dieser Reihenfolge kein Classifier vor: zuerst die Allowlist der Sandbox, dann mein eigener Hook, dann der eingebaute Mod, dann eine Zeile in CLAUDE.md. Einen Classifier würde ich unter den Hook setzen. Wie ich im Hooks-Text geschrieben habe: Ein Modell an der Schranke wägt ab, es setzt nichts durch, und genau diesen Fall behandelt der Essay über die Freigabe-Schranke ausführlich. Code Mode ändert diese Reihenfolge nicht. Er macht ihre Spitze wichtiger, weil die Schicht in der Mitte jetzt weniger liest.

Dasselbe Release hat geändert, was passiert, wenn ein Hook nicht antworten kann. Ich lese die beiden Änderungen zusammen, auch wenn die Dokumentation die zweite unter Einschränkungen führt und nicht als Sicherheitsfunktion. Code Mode ist außerdem zwei Tage alt: Release 2.1.325 hat Skripte repariert, die einen per EnterWorktree betretenen Worktree ignorierten, Worte, die zusammen mit einem Yes in einem Berechtigungsdialog getippt wurden und Claude nicht erreichten, und Claude, das nach dem Fortsetzen einer mitten im Skript beendeten Session von selbst losarbeitete.

Wenn du diese Woche CLAUDE_CODE_EXPERIMENTAL_CODE_MODE=1 setzt, erledige vorher zwei Dinge. Schreib die Aufrufe auf, die nie passieren dürfen, und notiere, was jeden davon stoppt. Bei einem Shell-Befehl sollte das die Sandbox sein, denn eine Bash-deny-Regel liest Befehlstext und lässt sich umgehen, mit oder ohne Code Mode. Bei einem Datei-Tool eine deny-Regel oder ein Hook auf dieses Tool. Der Auto Mode sollte in keiner dieser Zeilen stehen. Dann geh deine PreToolUse-Hooks durch: "onFailure": "block" auf jeden Guard, damit auch ein Absturz mit Exit Code 1 ablehnt, und "onFailure": "continue" auf alles, was nur loggt oder benachrichtigt, damit ein Timeout dort keine Arbeit ablehnen kann. Erhöhe timeout nur dort, wo du selbst einen kurzen gesetzt hast.

01 / Diskussion

Hier gibt es keine Kommentarspalte. Diskussionen laufen auf X.

Max Nardit@mnardit

02 / Weitere Artikel