feat(zwischenruf): Korrektur mitten in den laufenden Turn schieben

Neben Senden ein 'Zwischenruf' (App: oranger Button, nur wenn ARIA im
aktiven Kontext arbeitet; Diagnostic: Buttons neben beiden Senden). Geht
NICHT in die Queue und bricht NICHT ab — die Nachricht wird in den
laufenden claude-Subprozess geschoben; er greift sie an der naechsten
Tool-Grenze auf.

Technik:
- proxy-patches/manager.js: claude laeuft jetzt im --input-format
  stream-json-Modus, initialer Prompt als stream-json User-Message,
  stdin bleibt OFFEN; sendMessage() schiebt weitere User-Messages nach;
  bei 'result' wird stdin geschlossen (Turn endet sauber). Ersetzt die
  bisherigen sed-Patches (jetzt volle Datei via cp, docker-compose.yml).
- proxy-patches/routes.js: Side-Channel POST /interject {projectId,text}
  → subprocess.sendMessage der Kontext-Subprozesse.
- rvs: 'interject' erlaubt. bridge: RVS interject → Proxy /interject.
- App/Diagnostic: Zwischenruf-Buttons + lokale '📣 Zwischenruf'-Bubble.

Empirisch verifiziert: mid-turn injizierte Message wird an der naechsten
Tool-Grenze aufgegriffen (nicht mitten in einem blockierenden Befehl).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 05:05:13 +02:00
co-authored by Claude Opus 4.8
parent 2ad8c2f245
commit 648e3b04fd
8 changed files with 395 additions and 9 deletions
+43 -3
View File
@@ -2444,6 +2444,24 @@ const ChatScreen: React.FC = () => {
}
}, [inputText, pendingAttachments, sendPendingAttachments, getCtxState, setCtxState, setCtxQueue, actuallySend]);
// Zwischenruf: waehrend ARIA arbeitet eine Korrektur MITTEN in den laufenden
// Turn schieben — NICHT in die Queue, KEIN Abbruch. Geht als 'interject' ueber
// RVS an die Bridge → Proxy → laufender Subprozess (greift es an der naechsten
// Tool-Grenze auf). Sichtbar nur, wenn der aktive Kontext gerade arbeitet.
const sendInterject = useCallback(() => {
const text = inputText.trim();
if (!text) return;
const activePid = focusedProjectIdRef.current;
rvs.send('interject' as any, { projectId: activePid, text });
// Lokale Bubble zur Rueckmeldung (laeuft NICHT durch Send/Queue).
setMessages(prev => capMessages([...prev, {
id: nextId(), sender: 'user', text: `📣 Zwischenruf: ${text}`,
timestamp: Date.now(), projectId: activePid,
}]));
projectDraftsRef.current = { ...projectDraftsRef.current, [activePid]: '' };
setInputText('');
}, [inputText]);
// --- Rendering ---
const renderMessage = ({ item }: { item: ChatMessage }) => {
@@ -3218,9 +3236,19 @@ const ChatScreen: React.FC = () => {
{/* Senden oder Sprache */}
{inputText.trim() || pendingAttachments.length > 0 ? (
<TouchableOpacity style={styles.sendButton} onPress={sendTextMessage}>
<Text style={styles.sendIcon}>{'\u2B06\uFE0F'}</Text>
</TouchableOpacity>
<>
{/* Zwischenruf: nur wenn ARIA im aktiven Kontext gerade arbeitet und
Text da ist. Schiebt die Korrektur in den laufenden Turn statt
sie anzustellen. */}
{inputText.trim() && (agentActivityByCtx[focusedProjectId]?.activity || 'idle') !== 'idle' ? (
<TouchableOpacity style={styles.interjectButton} onPress={sendInterject} accessibilityLabel="Zwischenruf">
<Text style={styles.interjectIcon}>{'\uD83D\uDCE3'}</Text>
</TouchableOpacity>
) : null}
<TouchableOpacity style={styles.sendButton} onPress={sendTextMessage}>
<Text style={styles.sendIcon}>{'\u2B06\uFE0F'}</Text>
</TouchableOpacity>
</>
) : (
<>
<VoiceButton
@@ -3738,6 +3766,18 @@ const styles = StyleSheet.create({
sendIcon: {
fontSize: 18,
},
interjectButton: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: '#FF9500', // orange — Zwischenruf, klar vom blauen Senden getrennt
alignItems: 'center',
justifyContent: 'center',
marginRight: 6,
},
interjectIcon: {
fontSize: 18,
},
wakeWordBtn: {
width: 32,
height: 32,