perf: fix memory leaks — strip task outputs from SSE broadcasts, use light state everywhere

- TaskTracker.getTaskTreeLight(): strips large `output` strings from tasks
  in SSE broadcasts (was serializing 5-10MB every 500ms with many subagents)
- session:created broadcasts now use toLightDetailedState() (consistent with
  session:updated which already did)
- GET /api/sessions/:id returns light state (no 2-3MB terminal+text buffers)
- Ralph wizard polls /terminal?tail=2048 instead of full session endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-02-20 06:50:59 +01:00
co-authored by Claude Opus 4.6
parent 51e45ed317
commit 868d4f1196
8 changed files with 47 additions and 28 deletions
+10 -4
View File
@@ -7716,7 +7716,8 @@ class ClaudemanApp {
while (attempts < maxAttempts) {
await new Promise(r => setTimeout(r, 500));
try {
const statusRes = await fetch(`/api/sessions/${sessionId}`);
// Use /terminal?tail=2048 instead of /sessions/:id to avoid fetching 2-3MB full state
const statusRes = await fetch(`/api/sessions/${sessionId}/terminal?tail=2048`);
const statusData = await statusRes.json();
// Session is ready ONLY when Claude CLI shows its UI:
// Must see prompt character '❯' OR 'tokens' status line
@@ -7781,9 +7782,14 @@ class ClaudemanApp {
for (let attempt = 0; attempt < initMaxAttempts; attempt++) {
await new Promise(r => setTimeout(r, 1000));
try {
const statusRes = await fetch(`/api/sessions/${sessionId}`);
const statusData = await statusRes.json();
const termBuf = statusData?.terminalBuffer || '';
// Fetch terminal tail (lightweight) + session state separately to avoid 2-3MB full buffer
const [termRes, stateRes] = await Promise.all([
fetch(`/api/sessions/${sessionId}/terminal?tail=2048`),
fetch(`/api/sessions/${sessionId}`)
]);
const termData = await termRes.json();
const statusData = await stateRes.json();
const termBuf = termData?.terminalBuffer || '';
// /init is complete when we see the prompt indicator (❯) and not working
const hasPrompt = termBuf.includes('❯');
const isIdle = !statusData.isWorking;