fix(opencode): broadcast activity idle after cooldown (#1249)

Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
This commit is contained in:
Isaac Sanchez-Hawkins
2026-05-13 10:32:30 +03:00
committed by GitHub
co-authored by Isaac Sanchez
parent 05c5683ecb
commit f01391dd72
2 changed files with 57 additions and 3 deletions
@@ -96,7 +96,8 @@ export const createSessionRuntime = ({ writeSseEvent, getNotificationClients, br
const timer = setTimeout(() => {
const now = sessionActivityPhases.get(sessionId);
if (now?.phase === 'cooldown') {
sessionActivityPhases.set(sessionId, { phase: 'idle', updatedAt: Date.now() });
setSessionActivityPhase(sessionId, 'idle');
return;
}
sessionActivityCooldowns.delete(sessionId);
}, SESSION_COOLDOWN_DURATION_MS);
@@ -178,7 +179,9 @@ export const createSessionRuntime = ({ writeSseEvent, getNotificationClients, br
}
const phase = status === 'busy' || status === 'retry' ? 'busy' : 'idle';
setSessionActivityPhase(sessionId, phase);
if (phase !== 'idle' || sessionActivityPhases.get(sessionId)?.phase !== 'cooldown') {
setSessionActivityPhase(sessionId, phase);
}
};
const getSessionStateSnapshot = () => {
@@ -1,4 +1,4 @@
import { afterEach, describe, expect, it } from 'vitest';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { createSessionRuntime } from './session-runtime.js';
@@ -97,4 +97,55 @@ describe('session runtime', () => {
}),
});
});
it('broadcasts idle activity when cooldown expires', () => {
vi.useFakeTimers();
const events = [];
const runtime = createSessionRuntime({
writeSseEvent() {
throw new Error('SSE fallback should not be used when broadcastEvent is provided');
},
getNotificationClients: () => new Set(),
broadcastEvent: (payload) => {
events.push(payload);
},
});
try {
runtime.processOpenCodeSsePayload({
type: 'session.status',
properties: {
sessionID: 'session-activity-1',
status: {
type: 'busy',
},
},
});
runtime.processOpenCodeSsePayload({
type: 'session.status',
properties: {
sessionID: 'session-activity-1',
status: {
type: 'idle',
},
},
});
const activityPhases = () => events
.filter((event) => event.type === 'openchamber:session-activity')
.map((event) => event.properties.phase);
expect(activityPhases()).toEqual(['busy', 'cooldown']);
vi.advanceTimersByTime(1999);
expect(activityPhases()).toEqual(['busy', 'cooldown']);
vi.advanceTimersByTime(1);
expect(activityPhases()).toEqual(['busy', 'cooldown', 'idle']);
} finally {
runtime.dispose();
vi.useRealTimers();
}
});
});