← Back to Kontasks
Warm Session Cache Design
How process caching would integrate with konsole's existing --resume
Output
Warm Session Cache Design
Integrating process caching with konsole's existing --resume mechanism
Current Architecture (2 Layers)
Layer 1: Konsole Session session.ts → ses_abc123 (persisted to disk, 24hr TTL) → stores claudeSessionId for resume → tracks kontexts, status, working history Layer 2: Claude Process claude.ts → spawnClaude() called PER PROMPT → spawns: claude --print --resume {claudeSessionId} "prompt" → process exits after each response → ~1-3 seconds startup overhead
Key Insight: --resume ≠ Process Reuse
Currently --resume passes the Claude session ID to a new process. The new process loads conversation history from Claude's internal storage (~/.claude/), but still pays the full startup cost.
Prompt 1: claude --print "Hello" → spawn, run, exit (3s) Prompt 2: claude --print --resume abc "Hi" → spawn, load, run, exit (3s) Prompt 3: claude --print --resume abc "Bye" → spawn, load, run, exit (3s) Total: 9s overhead
Warm Cache: Keep Process Alive
Instead of exiting after each response, keep the Claude process running and pipe new prompts via stdin.
Prompt 1: claude --print "Hello" → spawn (3s) + run Prompt 2: (stdin) "Hi" → immediate (0s) Prompt 3: (stdin) "Bye" → immediate (0s) [5 min idle] → kill process Prompt 4: claude --print --resume abc → spawn (3s) + run Total: 6s overhead (vs 12s)
Implementation Changes
1. claude.ts - Process Pool
// NEW: Track warm processes const warmProcesses = new Map<string, WarmClaudeProcess>(); interface WarmClaudeProcess { process: Deno.ChildProcess; stdin: WritableStreamDefaultWriter; lastUsedAt: number; claudeSessionId: string; } // Reuse or spawn function getOrSpawnClaude(sessionId, prompt, claudeSessionId) { const warm = warmProcesses.get(sessionId); if (warm && warm.process.running) { warm.lastUsedAt = Date.now(); warm.stdin.write(prompt); // Send via stdin return warm; } // Cold start - spawn new return spawnWarmClaude(sessionId, prompt, claudeSessionId); }
2. Idle Timeout (5 min)
// Run every 60 seconds setInterval(() => { const now = Date.now(); for (const [id, warm] of warmProcesses) { if (now - warm.lastUsedAt > 5 * 60 * 1000) { warm.process.kill("SIGTERM"); warmProcesses.delete(id); log.info("Expired warm process", { sessionId: id }); } } }, 60_000);
3. Fallback to --resume
// When warm process expires, fall back to --resume if (!warm && claudeSessionId) { // Still have session ID from before return spawnWarmClaude(sessionId, prompt, claudeSessionId); // ↑ Uses --resume to restore context, then stays warm }
How It Works Together
| Scenario | Behavior | Latency |
|---|---|---|
| First prompt (cold start) | Spawn Claude, save claudeSessionId, keep warm | ~3s |
| Second prompt (<5 min) | Reuse warm process, pipe via stdin | ~0s |
| Third prompt (<5 min) | Reuse warm process, pipe via stdin | ~0s |
| After 5 min idle | Process killed, session ID retained | - |
| Next prompt (after idle) | Spawn with --resume, load context, stay warm | ~3s |
| After 24hr session expiry | New session, fresh start | ~3s |
Challenges to Address
| stdin protocol | Need to delimit prompts in stdin stream. Claude may need --interactive mode. |
| Output parsing | Distinguish which response belongs to which prompt. May need request IDs in stream. |
| Memory growth | Long-running process may accumulate memory. Monitor and restart if needed. |
| Concurrent users | One warm process per user session. Multiple users = multiple warm processes. |
Summary
--resume = Restore conversation context to a new process (still pays spawn cost)
Warm cache = Keep same process alive, send prompts via stdin (zero spawn cost)
Best of both = Warm cache for active use, fallback to --resume after 5 min idle
Quick Actions
Original Request
No layout configured
Details
Type
General
Status
Completed
Created
1 Jan 2026, 11:21 pm
Updated
2 Jan 2026, 3:10 pm
Created By
claude
Raw Data
{
"id": "b9688e4e-b532-48ab-89c0-e2ac2d157eca",
"type": "general",
"status": "completed",
"title": "Warm Session Cache Design",
"description": "How process caching would integrate with konsole's existing --resume",
"context": {
"output": "<div style=\"background:#1e1e1e;color:#e0e0e0;padding:20px;border-radius:8px;font-family:sans-serif;\">\n<h2 style=\"color:#fff;margin:0 0 8px 0;\">Warm Session Cache Design</h2>\n<p style=\"color:#7f848e;margin:0 0 20px 0;\">Integrating process caching with konsole's existing --resume mechanism</p>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#61afef;margin:0 0 12px 0;font-size:1rem;\">Current Architecture (2 Layers)</h3>\n<pre style=\"background:#1a1a1a;padding:12px;border-radius:4px;font-family:monospace;font-size:0.8rem;overflow-x:auto;line-height:1.6;margin:0;\"><span style=\"color:#7f848e;\">Layer 1: Konsole Session</span>\n<span style=\"color:#98c379;\">session.ts</span> → <span style=\"color:#e5c07b;\">ses_abc123</span> (persisted to disk, 24hr TTL)\n → stores <span style=\"color:#c678dd;\">claudeSessionId</span> for resume\n → tracks kontexts, status, working history\n\n<span style=\"color:#7f848e;\">Layer 2: Claude Process</span>\n<span style=\"color:#98c379;\">claude.ts</span> → <span style=\"color:#e06c75;\">spawnClaude()</span> called PER PROMPT\n → spawns: <span style=\"color:#e5c07b;\">claude --print --resume {claudeSessionId} \"prompt\"</span>\n → process exits after each response\n → <span style=\"color:#c678dd;\">~1-3 seconds startup overhead</span></pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;border-left:4px solid #e5c07b;\">\n<h3 style=\"color:#e5c07b;margin:0 0 12px 0;font-size:1rem;\">Key Insight: --resume ≠ Process Reuse</h3>\n<p style=\"color:#e0e0e0;margin:0;font-size:0.9rem;line-height:1.6;\">\nCurrently <code style=\"background:#1a1a1a;padding:2px 6px;border-radius:3px;\">--resume</code> passes the Claude session ID to a <strong>new process</strong>. The new process loads conversation history from Claude's internal storage (~/.claude/), but still pays the full startup cost.\n</p>\n<pre style=\"background:#1a1a1a;padding:12px;border-radius:4px;font-family:monospace;font-size:0.8rem;margin:12px 0 0 0;\">\n<span style=\"color:#7f848e;\">Prompt 1:</span> claude --print \"Hello\" <span style=\"color:#e06c75;\">→ spawn, run, exit (3s)</span>\n<span style=\"color:#7f848e;\">Prompt 2:</span> claude --print --resume abc \"Hi\" <span style=\"color:#e06c75;\">→ spawn, load, run, exit (3s)</span>\n<span style=\"color:#7f848e;\">Prompt 3:</span> claude --print --resume abc \"Bye\" <span style=\"color:#e06c75;\">→ spawn, load, run, exit (3s)</span>\n <span style=\"color:#7f848e;\">Total: 9s overhead</span></pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;border-left:4px solid #22c55e;\">\n<h3 style=\"color:#22c55e;margin:0 0 12px 0;font-size:1rem;\">Warm Cache: Keep Process Alive</h3>\n<p style=\"color:#e0e0e0;margin:0 0 12px 0;font-size:0.9rem;line-height:1.6;\">\nInstead of exiting after each response, keep the Claude process running and pipe new prompts via stdin.\n</p>\n<pre style=\"background:#1a1a1a;padding:12px;border-radius:4px;font-family:monospace;font-size:0.8rem;margin:0;\">\n<span style=\"color:#7f848e;\">Prompt 1:</span> claude --print \"Hello\" <span style=\"color:#e06c75;\">→ spawn (3s)</span> + run\n<span style=\"color:#7f848e;\">Prompt 2:</span> <span style=\"color:#98c379;\">(stdin) \"Hi\"</span> <span style=\"color:#98c379;\">→ immediate (0s)</span>\n<span style=\"color:#7f848e;\">Prompt 3:</span> <span style=\"color:#98c379;\">(stdin) \"Bye\"</span> <span style=\"color:#98c379;\">→ immediate (0s)</span>\n<span style=\"color:#7f848e;\">[5 min idle]</span> → kill process\n<span style=\"color:#7f848e;\">Prompt 4:</span> claude --print --resume abc <span style=\"color:#e06c75;\">→ spawn (3s)</span> + run\n <span style=\"color:#98c379;\">Total: 6s overhead (vs 12s)</span></pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#c678dd;margin:0 0 12px 0;font-size:1rem;\">Implementation Changes</h3>\n\n<div style=\"margin-bottom:16px;\">\n<h4 style=\"color:#61afef;margin:0 0 8px 0;font-size:0.95rem;\">1. claude.ts - Process Pool</h4>\n<pre style=\"background:#1a1a1a;padding:10px;border-radius:4px;font-family:monospace;font-size:0.8rem;overflow-x:auto;margin:0;\"><span style=\"color:#7f848e;\">// NEW: Track warm processes</span>\n<span style=\"color:#c678dd;\">const</span> warmProcesses = <span style=\"color:#c678dd;\">new</span> <span style=\"color:#e5c07b;\">Map</span><<span style=\"color:#e5c07b;\">string</span>, <span style=\"color:#e5c07b;\">WarmClaudeProcess</span>>();\n\n<span style=\"color:#c678dd;\">interface</span> <span style=\"color:#e5c07b;\">WarmClaudeProcess</span> {\n process: <span style=\"color:#e5c07b;\">Deno.ChildProcess</span>;\n stdin: <span style=\"color:#e5c07b;\">WritableStreamDefaultWriter</span>;\n lastUsedAt: <span style=\"color:#e5c07b;\">number</span>;\n claudeSessionId: <span style=\"color:#e5c07b;\">string</span>;\n}\n\n<span style=\"color:#7f848e;\">// Reuse or spawn</span>\n<span style=\"color:#c678dd;\">function</span> <span style=\"color:#61afef;\">getOrSpawnClaude</span>(sessionId, prompt, claudeSessionId) {\n <span style=\"color:#c678dd;\">const</span> warm = warmProcesses.<span style=\"color:#61afef;\">get</span>(sessionId);\n <span style=\"color:#c678dd;\">if</span> (warm && warm.process.running) {\n warm.lastUsedAt = <span style=\"color:#e5c07b;\">Date</span>.<span style=\"color:#61afef;\">now</span>();\n warm.stdin.<span style=\"color:#61afef;\">write</span>(prompt); <span style=\"color:#7f848e;\">// Send via stdin</span>\n <span style=\"color:#c678dd;\">return</span> warm;\n }\n <span style=\"color:#7f848e;\">// Cold start - spawn new</span>\n <span style=\"color:#c678dd;\">return</span> <span style=\"color:#61afef;\">spawnWarmClaude</span>(sessionId, prompt, claudeSessionId);\n}</pre>\n</div>\n\n<div style=\"margin-bottom:16px;\">\n<h4 style=\"color:#61afef;margin:0 0 8px 0;font-size:0.95rem;\">2. Idle Timeout (5 min)</h4>\n<pre style=\"background:#1a1a1a;padding:10px;border-radius:4px;font-family:monospace;font-size:0.8rem;overflow-x:auto;margin:0;\"><span style=\"color:#7f848e;\">// Run every 60 seconds</span>\n<span style=\"color:#61afef;\">setInterval</span>(() => {\n <span style=\"color:#c678dd;\">const</span> now = <span style=\"color:#e5c07b;\">Date</span>.<span style=\"color:#61afef;\">now</span>();\n <span style=\"color:#c678dd;\">for</span> (<span style=\"color:#c678dd;\">const</span> [id, warm] <span style=\"color:#c678dd;\">of</span> warmProcesses) {\n <span style=\"color:#c678dd;\">if</span> (now - warm.lastUsedAt > <span style=\"color:#d19a66;\">5 * 60 * 1000</span>) {\n warm.process.<span style=\"color:#61afef;\">kill</span>(<span style=\"color:#98c379;\">\"SIGTERM\"</span>);\n warmProcesses.<span style=\"color:#61afef;\">delete</span>(id);\n log.<span style=\"color:#61afef;\">info</span>(<span style=\"color:#98c379;\">\"Expired warm process\"</span>, { sessionId: id });\n }\n }\n}, <span style=\"color:#d19a66;\">60_000</span>);</pre>\n</div>\n\n<div>\n<h4 style=\"color:#61afef;margin:0 0 8px 0;font-size:0.95rem;\">3. Fallback to --resume</h4>\n<pre style=\"background:#1a1a1a;padding:10px;border-radius:4px;font-family:monospace;font-size:0.8rem;overflow-x:auto;margin:0;\"><span style=\"color:#7f848e;\">// When warm process expires, fall back to --resume</span>\n<span style=\"color:#c678dd;\">if</span> (!warm && claudeSessionId) {\n <span style=\"color:#7f848e;\">// Still have session ID from before</span>\n <span style=\"color:#c678dd;\">return</span> <span style=\"color:#61afef;\">spawnWarmClaude</span>(sessionId, prompt, claudeSessionId);\n <span style=\"color:#7f848e;\">// ↑ Uses --resume to restore context, then stays warm</span>\n}</pre>\n</div>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#56b6c2;margin:0 0 12px 0;font-size:1rem;\">How It Works Together</h3>\n<table style=\"width:100%;border-collapse:collapse;font-size:0.85rem;\">\n<tr style=\"border-bottom:2px solid #444;background:#1a1a1a;\">\n<th style=\"text-align:left;padding:10px;\">Scenario</th>\n<th style=\"text-align:left;padding:10px;\">Behavior</th>\n<th style=\"text-align:right;padding:10px;\">Latency</th>\n</tr>\n<tr style=\"border-bottom:1px solid #333;\">\n<td style=\"padding:10px;\">First prompt (cold start)</td>\n<td style=\"padding:10px;color:#e0e0e0;\">Spawn Claude, save claudeSessionId, keep warm</td>\n<td style=\"padding:10px;text-align:right;color:#e06c75;\">~3s</td>\n</tr>\n<tr style=\"border-bottom:1px solid #333;\">\n<td style=\"padding:10px;\">Second prompt (<5 min)</td>\n<td style=\"padding:10px;color:#e0e0e0;\">Reuse warm process, pipe via stdin</td>\n<td style=\"padding:10px;text-align:right;color:#98c379;\">~0s</td>\n</tr>\n<tr style=\"border-bottom:1px solid #333;\">\n<td style=\"padding:10px;\">Third prompt (<5 min)</td>\n<td style=\"padding:10px;color:#e0e0e0;\">Reuse warm process, pipe via stdin</td>\n<td style=\"padding:10px;text-align:right;color:#98c379;\">~0s</td>\n</tr>\n<tr style=\"border-bottom:1px solid #333;\">\n<td style=\"padding:10px;\">After 5 min idle</td>\n<td style=\"padding:10px;color:#e0e0e0;\">Process killed, session ID retained</td>\n<td style=\"padding:10px;text-align:right;color:#7f848e;\">-</td>\n</tr>\n<tr style=\"border-bottom:1px solid #333;\">\n<td style=\"padding:10px;\">Next prompt (after idle)</td>\n<td style=\"padding:10px;color:#e0e0e0;\">Spawn with --resume, load context, stay warm</td>\n<td style=\"padding:10px;text-align:right;color:#e5c07b;\">~3s</td>\n</tr>\n<tr>\n<td style=\"padding:10px;\">After 24hr session expiry</td>\n<td style=\"padding:10px;color:#e0e0e0;\">New session, fresh start</td>\n<td style=\"padding:10px;text-align:right;color:#e06c75;\">~3s</td>\n</tr>\n</table>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;border-left:4px solid #f97316;\">\n<h3 style=\"color:#f97316;margin:0 0 12px 0;font-size:1rem;\">Challenges to Address</h3>\n<table style=\"width:100%;border-collapse:collapse;font-size:0.85rem;\">\n<tr style=\"border-bottom:1px solid #444;\">\n<td style=\"padding:8px 0;color:#e5c07b;width:150px;\">stdin protocol</td>\n<td style=\"color:#e0e0e0;\">Need to delimit prompts in stdin stream. Claude may need <code style=\"background:#1a1a1a;padding:2px 4px;border-radius:3px;\">--interactive</code> mode.</td>\n</tr>\n<tr style=\"border-bottom:1px solid #444;\">\n<td style=\"padding:8px 0;color:#e5c07b;\">Output parsing</td>\n<td style=\"color:#e0e0e0;\">Distinguish which response belongs to which prompt. May need request IDs in stream.</td>\n</tr>\n<tr style=\"border-bottom:1px solid #444;\">\n<td style=\"padding:8px 0;color:#e5c07b;\">Memory growth</td>\n<td style=\"color:#e0e0e0;\">Long-running process may accumulate memory. Monitor and restart if needed.</td>\n</tr>\n<tr>\n<td style=\"padding:8px 0;color:#e5c07b;\">Concurrent users</td>\n<td style=\"color:#e0e0e0;\">One warm process per user session. Multiple users = multiple warm processes.</td>\n</tr>\n</table>\n</div>\n\n<div style=\"background:#22c55e20;padding:16px;border-radius:6px;border:1px solid #22c55e;\">\n<h3 style=\"color:#22c55e;margin:0 0 12px 0;font-size:1rem;\">Summary</h3>\n<p style=\"margin:0;color:#e0e0e0;line-height:1.6;\">\n<strong>--resume</strong> = Restore conversation context to a <em>new</em> process (still pays spawn cost)<br>\n<strong>Warm cache</strong> = Keep <em>same</em> process alive, send prompts via stdin (zero spawn cost)<br>\n<strong>Best of both</strong> = Warm cache for active use, fallback to --resume after 5 min idle\n</p>\n</div>\n\n</div>",
"requestedAt": "2026-01-01T13:15:00.000Z",
"requestId": "518ab0e4-4216-4096-82cb-edbf5c9ad415"
},
"createdBy": "claude",
"createdAt": "2026-01-01T13:21:25.333Z",
"updatedAt": "2026-01-02T05:10:54.071Z",
"requestId": "518ab0e4-4216-4096-82cb-edbf5c9ad415"
}