Add debug log panel, centered modals, and fix multiple UX issues
Features:
- Add /logs command to toggle debug log panel (20% width on right)
- Debug panel shows API calls, streaming events, tool calls, state changes
- Add /help submenus with detailed command explanations
- Center all modal dialogs in the terminal window
Bug Fixes:
- Fix streaming content not displaying (add fallback when streaming fails)
- Fix permission modal shortcut key mismatch ('a' → 'l' for local scope)
- Fix agent prompt accumulation when switching agents multiple times
- Fix permission modal using brittle index for "No" option
Improvements:
- Restrict git commands (add, commit, push, etc.) unless user explicitly requests
- Unify permission options across all UI components
- Add Ollama model selection when switching to Ollama provider
- Store base system prompt to prevent agent prompt stacking
New files:
- src/tui-solid/components/debug-log-panel.tsx
- src/tui-solid/components/centered-modal.tsx
- src/tui-solid/components/help-menu.tsx
- src/tui-solid/components/help-detail.tsx
- src/constants/help-content.ts
This commit is contained in:
@@ -4,11 +4,7 @@
|
||||
|
||||
import { saveSession as saveSessionSession } from "@services/session";
|
||||
import { appStore } from "@tui/index";
|
||||
import {
|
||||
CHAT_MESSAGES,
|
||||
HELP_TEXT,
|
||||
type CommandName,
|
||||
} from "@constants/chat-service";
|
||||
import { CHAT_MESSAGES, type CommandName } from "@constants/chat-service";
|
||||
import { handleLogin, handleLogout, showWhoami } from "@services/chat-tui/auth";
|
||||
import {
|
||||
handleRememberCommand,
|
||||
@@ -31,8 +27,8 @@ type CommandHandler = (
|
||||
callbacks: ChatServiceCallbacks,
|
||||
) => Promise<void> | void;
|
||||
|
||||
const showHelp: CommandHandler = (_, callbacks) => {
|
||||
callbacks.onLog("system", HELP_TEXT);
|
||||
const showHelp: CommandHandler = () => {
|
||||
appStore.setMode("help_menu");
|
||||
};
|
||||
|
||||
const clearConversation: CommandHandler = (state, callbacks) => {
|
||||
@@ -112,6 +108,15 @@ const selectMode: CommandHandler = () => {
|
||||
appStore.setMode("mode_select");
|
||||
};
|
||||
|
||||
const toggleDebugLogs: CommandHandler = (_, callbacks) => {
|
||||
appStore.toggleDebugLog();
|
||||
const { debugLogVisible } = appStore.getState();
|
||||
callbacks.onLog(
|
||||
"system",
|
||||
`Debug logs panel ${debugLogVisible ? "enabled" : "disabled"}`,
|
||||
);
|
||||
};
|
||||
|
||||
const COMMAND_HANDLERS: Record<CommandName, CommandHandler> = {
|
||||
help: showHelp,
|
||||
h: showHelp,
|
||||
@@ -137,6 +142,7 @@ const COMMAND_HANDLERS: Record<CommandName, CommandHandler> = {
|
||||
status: showStatus,
|
||||
remember: handleRememberCommand,
|
||||
learnings: (_, callbacks) => handleLearningsCommand(callbacks),
|
||||
logs: toggleDebugLogs,
|
||||
};
|
||||
|
||||
export const executeCommand = async (
|
||||
|
||||
@@ -54,6 +54,7 @@ import type {
|
||||
ChatServiceCallbacks,
|
||||
ToolCallInfo,
|
||||
} from "@/types/chat-service";
|
||||
import { addDebugLog } from "@tui-solid/components/debug-log-panel";
|
||||
|
||||
// Track last response for feedback learning
|
||||
let lastResponseContext: {
|
||||
@@ -116,47 +117,58 @@ const createToolResultHandler =
|
||||
/**
|
||||
* Create streaming callbacks for TUI integration
|
||||
*/
|
||||
const createStreamCallbacks = (): StreamCallbacks => ({
|
||||
onContentChunk: (content: string) => {
|
||||
appStore.appendStreamContent(content);
|
||||
},
|
||||
const createStreamCallbacks = (): StreamCallbacks => {
|
||||
let chunkCount = 0;
|
||||
|
||||
onToolCallStart: (toolCall) => {
|
||||
appStore.setCurrentToolCall({
|
||||
id: toolCall.id,
|
||||
name: toolCall.name,
|
||||
description: `Calling ${toolCall.name}...`,
|
||||
status: "pending",
|
||||
});
|
||||
},
|
||||
return {
|
||||
onContentChunk: (content: string) => {
|
||||
chunkCount++;
|
||||
addDebugLog("stream", `Chunk #${chunkCount}: "${content.substring(0, 30)}${content.length > 30 ? "..." : ""}"`);
|
||||
appStore.appendStreamContent(content);
|
||||
},
|
||||
|
||||
onToolCallComplete: (toolCall) => {
|
||||
appStore.updateToolCall({
|
||||
id: toolCall.id,
|
||||
name: toolCall.name,
|
||||
status: "running",
|
||||
});
|
||||
},
|
||||
onToolCallStart: (toolCall) => {
|
||||
addDebugLog("tool", `Tool start: ${toolCall.name} (${toolCall.id})`);
|
||||
appStore.setCurrentToolCall({
|
||||
id: toolCall.id,
|
||||
name: toolCall.name,
|
||||
description: `Calling ${toolCall.name}...`,
|
||||
status: "pending",
|
||||
});
|
||||
},
|
||||
|
||||
onModelSwitch: (info) => {
|
||||
appStore.addLog({
|
||||
type: "system",
|
||||
content: `Model switched: ${info.from} → ${info.to} (${info.reason})`,
|
||||
});
|
||||
},
|
||||
onToolCallComplete: (toolCall) => {
|
||||
addDebugLog("tool", `Tool complete: ${toolCall.name}`);
|
||||
appStore.updateToolCall({
|
||||
id: toolCall.id,
|
||||
name: toolCall.name,
|
||||
status: "running",
|
||||
});
|
||||
},
|
||||
|
||||
onComplete: () => {
|
||||
appStore.completeStreaming();
|
||||
},
|
||||
onModelSwitch: (info) => {
|
||||
addDebugLog("api", `Model switch: ${info.from} → ${info.to}`);
|
||||
appStore.addLog({
|
||||
type: "system",
|
||||
content: `Model switched: ${info.from} → ${info.to} (${info.reason})`,
|
||||
});
|
||||
},
|
||||
|
||||
onError: (error: string) => {
|
||||
appStore.cancelStreaming();
|
||||
appStore.addLog({
|
||||
type: "error",
|
||||
content: error,
|
||||
});
|
||||
},
|
||||
});
|
||||
onComplete: () => {
|
||||
addDebugLog("stream", `Stream complete (${chunkCount} chunks)`);
|
||||
appStore.completeStreaming();
|
||||
},
|
||||
|
||||
onError: (error: string) => {
|
||||
addDebugLog("error", `Stream error: ${error}`);
|
||||
appStore.cancelStreaming();
|
||||
appStore.addLog({
|
||||
type: "error",
|
||||
content: error,
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Run audit with Copilot on Ollama's response
|
||||
@@ -386,9 +398,12 @@ export const handleMessage = async (
|
||||
}
|
||||
|
||||
// Start streaming UI
|
||||
addDebugLog("state", `Starting request: provider=${effectiveProvider}, model=${state.model}`);
|
||||
addDebugLog("state", `Mode: ${appStore.getState().interactionMode}, Cascade: ${cascadeEnabled}`);
|
||||
appStore.setMode("thinking");
|
||||
appStore.startThinking();
|
||||
appStore.startStreaming();
|
||||
addDebugLog("state", "Streaming started");
|
||||
|
||||
const streamCallbacks = createStreamCallbacks();
|
||||
const agent = createStreamingAgent(
|
||||
@@ -412,12 +427,15 @@ export const handleMessage = async (
|
||||
);
|
||||
|
||||
try {
|
||||
addDebugLog("api", `Agent.run() started with ${state.messages.length} messages`);
|
||||
const result = await agent.run(state.messages);
|
||||
addDebugLog("api", `Agent.run() completed: success=${result.success}, iterations=${result.iterations}`);
|
||||
|
||||
// Stop thinking timer
|
||||
appStore.stopThinking();
|
||||
|
||||
if (result.finalResponse) {
|
||||
addDebugLog("info", `Final response length: ${result.finalResponse.length} chars`);
|
||||
let finalResponse = result.finalResponse;
|
||||
|
||||
// Run audit if cascade mode with Ollama
|
||||
@@ -450,8 +468,18 @@ export const handleMessage = async (
|
||||
role: "assistant",
|
||||
content: finalResponse,
|
||||
});
|
||||
// Note: Don't call callbacks.onLog here - streaming already added the log entry
|
||||
// via appendStreamContent/completeStreaming
|
||||
|
||||
// Check if streaming content was received - if not, add the response as a log
|
||||
// This handles cases where streaming didn't work or content was all in final response
|
||||
const streamingState = appStore.getState().streamingLog;
|
||||
if (!streamingState.content && finalResponse) {
|
||||
// Streaming didn't receive content, manually add the response
|
||||
appStore.cancelStreaming(); // Remove empty streaming log
|
||||
appStore.addLog({
|
||||
type: "assistant",
|
||||
content: finalResponse,
|
||||
});
|
||||
}
|
||||
|
||||
addMessage("user", message);
|
||||
addMessage("assistant", finalResponse);
|
||||
|
||||
Reference in New Issue
Block a user