diff --git a/appcast.xml b/appcast.xml
index a076cd946..ed3682187 100644
--- a/appcast.xml
+++ b/appcast.xml
@@ -4,9 +4,9 @@
Clawdis
-
2.0.0-beta5
- Sat, 03 Jan 2026 06:11:43 +0100
+ Sat, 03 Jan 2026 07:15:16 +0100
https://raw.githubusercontent.com/steipete/clawdis/main/appcast.xml
- 2.0.0-beta5
+ 2765
2.0.0-beta5
15.0
Clawdis 2.0.0-beta5
@@ -62,7 +62,8 @@
CLI: add
configure, doctor, and update wizards for ongoing setup, health checks, and modernization.
CLI: add Signal CLI auto-install from GitHub releases in the wizard and persist wizard run metadata in config.
CLI: add remote gateway client config (gateway.remote.*) with Bonjour-assisted discovery.
-CLI: add clawdis tui gateway-connected terminal UI (local or remote).
+CLI: enhance clawdis tui with model/session pickers, tool cards, and slash commands (local or remote).
+Gateway: allow sessions.patch to set per-session model overrides (used by the TUI /model flow).
Skills: allow bun as a node manager for skill installs.
Skills: add things-mac (Things 3 CLI) for read/search plus add/update via URL scheme.
Skills: add Apple Notes + Reminders skills via memo CLI (thanks @tylerwince).
@@ -205,7 +206,7 @@
View full changelog
]]>
-
+
\ No newline at end of file
diff --git a/src/tui/components/chat-log.ts b/src/tui/components/chat-log.ts
index 8129707f6..dae49be4e 100644
--- a/src/tui/components/chat-log.ts
+++ b/src/tui/components/chat-log.ts
@@ -8,6 +8,7 @@ export class ChatLog extends Container {
private toolById = new Map();
private streamingAssistant: AssistantMessageComponent | null = null;
private streamingRunId: string | null = null;
+ private streamingText: string | null = null;
private toolsExpanded = false;
clearAll() {
@@ -15,6 +16,7 @@ export class ChatLog extends Container {
this.toolById.clear();
this.streamingAssistant = null;
this.streamingRunId = null;
+ this.streamingText = null;
}
addSystem(text: string) {
@@ -30,6 +32,7 @@ export class ChatLog extends Container {
const component = new AssistantMessageComponent(text);
this.streamingAssistant = component;
this.streamingRunId = runId ?? null;
+ this.streamingText = text;
this.addChild(component);
return component;
}
@@ -42,17 +45,23 @@ export class ChatLog extends Container {
this.startAssistant(text, runId);
return;
}
+ this.streamingText = text;
this.streamingAssistant.setText(text);
}
finalizeAssistant(text: string, runId?: string) {
- if (this.streamingAssistant && (!runId || runId === this.streamingRunId)) {
+ if (
+ this.streamingAssistant &&
+ (!runId || runId === this.streamingRunId || text === this.streamingText)
+ ) {
+ this.streamingText = text;
this.streamingAssistant.setText(text);
} else {
this.startAssistant(text, runId);
}
this.streamingAssistant = null;
this.streamingRunId = null;
+ this.streamingText = null;
}
startTool(toolCallId: string, toolName: string, args: unknown) {
diff --git a/src/tui/tui.ts b/src/tui/tui.ts
index 9e3218045..b785f6847 100644
--- a/src/tui/tui.ts
+++ b/src/tui/tui.ts
@@ -110,6 +110,7 @@ export async function runTui(opts: TuiOptions) {
let currentSessionKey = defaultSession;
let currentSessionId: string | null = null;
let activeChatRunId: string | null = null;
+ const finalizedRuns = new Map();
let historyLoaded = false;
let isConnected = false;
let toolsExpanded = false;
@@ -296,10 +297,30 @@ export async function runTui(opts: TuiOptions) {
tui.requestRender();
};
+ const noteFinalizedRun = (runId: string) => {
+ finalizedRuns.set(runId, Date.now());
+ if (finalizedRuns.size <= 200) return;
+ const keepUntil = Date.now() - 10 * 60 * 1000;
+ for (const [key, ts] of finalizedRuns) {
+ if (finalizedRuns.size <= 150) break;
+ if (ts < keepUntil) finalizedRuns.delete(key);
+ }
+ if (finalizedRuns.size > 200) {
+ for (const key of finalizedRuns.keys()) {
+ finalizedRuns.delete(key);
+ if (finalizedRuns.size <= 150) break;
+ }
+ }
+ };
+
const handleChatEvent = (payload: unknown) => {
if (!payload || typeof payload !== "object") return;
const evt = payload as ChatEvent;
if (evt.sessionKey !== currentSessionKey) return;
+ if (finalizedRuns.has(evt.runId)) {
+ if (evt.state === "delta") return;
+ if (evt.state === "final") return;
+ }
if (evt.state === "delta") {
const text = extractTextFromMessage(evt.message, {
includeThinking: showThinking,
@@ -313,6 +334,7 @@ export async function runTui(opts: TuiOptions) {
includeThinking: showThinking,
});
chatLog.finalizeAssistant(text || "(no output)", evt.runId);
+ noteFinalizedRun(evt.runId);
activeChatRunId = null;
setStatus("idle");
}