feat: interactive AskUserQuestion + WebSocket stability

- Add WebSocket heartbeat (30s ping/pong) to prevent proxy timeouts
- Add auto-reconnect with exponential backoff (1s-30s, max 10 attempts)
- Add interactive AskUserQuestion rendering with clickable options
- Add custom input field for free-text answers
- Add smooth animations (hover, selection glow, checkbox scale)
- Make interactive tool cards wider (max-w-2xl) without scrolling
- Hide error badge and result section for interactive tools
- Use TextareaAutosize for lag-free custom input
- Add Skill, SlashCommand tool renderings
- Add ThinkingBlock component for collapsible <thinking> tags

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-18 06:50:57 +01:00
parent 1186cb1b5e
commit eb45891d6f
4 changed files with 327 additions and 22 deletions

View File

@@ -129,6 +129,9 @@ function generateRequestId() {
const server = createServer(app);
const wss = new WebSocketServer({ server });
// WebSocket heartbeat interval (30 seconds)
const WS_HEARTBEAT_INTERVAL = 30000;
// Scan directory for projects
function scanProjects(basePath, depth = 0, maxDepth = 1) {
const projects = [];
@@ -560,6 +563,20 @@ wss.on('connection', async (ws, req) => {
const sessionId = uuidv4();
console.log(`[${sessionId}] New WebSocket connection`);
// Track connection health
ws.isAlive = true;
// Heartbeat to keep connection alive through proxies
const heartbeatInterval = setInterval(() => {
if (ws.readyState === ws.OPEN) {
ws.ping();
}
}, WS_HEARTBEAT_INTERVAL);
ws.on('pong', () => {
ws.isAlive = true;
});
// Authenticate WebSocket connection
let wsUser = null;
if (authConfig.app.authEnabled && sessionStore) {
@@ -1030,6 +1047,8 @@ wss.on('connection', async (ws, req) => {
ws.on('close', () => {
console.log(`[${sessionId}] WebSocket closed`);
clearInterval(heartbeatInterval);
clearInterval(cleanupInterval);
if (claudeProcess) {
claudeProcess.kill();
sessions.delete(sessionId);