Frontend: TaskCreate/TaskUpdate als aggregierte Checkliste rendern

Die CLI nutzt inzwischen TaskCreate/TaskUpdate statt TodoWrite fuers
Todo-Tracking. Diese fielen in den generischen Tool-Box-Pfad und wurden
als rohe Tool-Calls mit JSON-Input angezeigt statt als Checkliste.

Da die neuen Tools inkrementell sind (ein Call pro Anlage/Statuswechsel),
werden die Einzel-Events zu EINER Checkliste aggregiert:

- tool-ui.js: Task-Tracker (applyTaskEvent/ingestTaskEvent), Box-Aufbau
  mit wiederverwendeten todo-list-Styles + Fortschritts-Badge n/m;
  sealTaskBox/sealLiveTaskList zum Segmentieren
- chat.js: Live-Pfad (SSE) haengt Task-Events in die aggregierte Box
  statt generischer Tool-Boxen; Reset beim Stream-Start
- history.js: Replay-Pfad mit eigenem Tracker pro Chain; zugehoerige
  tool_result-Messages werden uebersprungen (kein Doppel-Render)

Segmentierung: ein zusammenhaengender Task-Lauf bleibt EINE Box. Sobald
dazwischen echter Inhalt gerendert wird (Fliesstext oder anderes Tool),
wird die Box versiegelt und das naechste Task-Event oeffnet eine neue Box
darunter - so bleibt die chronologische Reihenfolge erhalten und
Narration zwischen den Tasks sichtbar. Der Task-Stand bleibt kumulativ,
der Fortschritt zaehlt ueber die Segmente hinweg hoch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
MelekiandClaude Opus 5 committed 2026-07-27 07:27:31 +02:00
1 parent be44235ca9
commit f38f3a42d1
3 files changed
+205 -1

No files matched your search

+18 -1
View File
@@ -42,6 +42,9 @@ function handleStreamEvent(eventType, data) {
break;
case 'text_start':
// Fließtext zwischen Task-Events → aktuelle Task-Box versiegeln, damit das nächste
// Task-Event eine neue Box UNTER dem Text öffnet (chronologische Reihenfolge).
sealLiveTaskList();
if (!_streamDiv) startStreamBubble();
break;
@@ -55,7 +58,13 @@ function handleStreamEvent(eventType, data) {
case 'tool_start':
endStreamBubble();
if (isToolVisible(data.name)) {
// Task*-Tools werden zu EINER aggregierten Checkliste gebündelt (erst bei tool_end,
// da wir dort Input+Result haben) → keine generische Lade-Box.
if (isTaskListTool(data.name)) {
_currentToolBox = null;
} else if (isToolVisible(data.name)) {
// Anderes Tool zwischen Task-Events → Task-Box versiegeln (nächstes Task-Event = neue Box)
sealLiveTaskList();
_currentToolBox = addToolBoxLoading(data.name);
} else {
_currentToolBox = null;
@@ -63,6 +72,12 @@ function handleStreamEvent(eventType, data) {
break;
case 'tool_end':
if (isTaskListTool(data.name)) {
if (isToolVisible(data.name)) handleLiveTaskEvent(data.name, data.input, data.result);
_currentToolBox = null;
_lastFinishedToolBox = null;
break;
}
if (_currentToolBox) {
finishToolBox(_currentToolBox, data);
_lastFinishedToolBox = _currentToolBox;
@@ -78,6 +93,7 @@ function handleStreamEvent(eventType, data) {
break;
case 'web_search_start':
sealLiveTaskList();
pauseStreamBubble();
_currentToolBox = addToolBoxLoading('web_search');
break;
@@ -473,6 +489,7 @@ async function _runStream(fetchPromise) {
_streamingPollInterval = null;
}
_currentToolBox = null;
resetLiveTaskList();
_chatAbandoned = false;
_stopClickCount = 0;
startTypingIndicator();