Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/lib/cloud-agent-sdk/session-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,22 @@ describe('createSessionManager', () => {
);
});

it('restores canSend and canInterrupt on interrupt failure', async () => {
const config = createMockConfig();
const mgr = createSessionManager(config);

await mgr.switchSession(kiloId('ses-1'));
expect(atomValue<boolean>(config.store, mgr.atoms.canSend)).toBe(true);
expect(atomValue<boolean>(config.store, mgr.atoms.canInterrupt)).toBe(true);

mockSession.interrupt.mockRejectedValueOnce(new Error('transient failure'));
await mgr.interrupt();

// After a failed interrupt, atoms should be restored from session state
expect(atomValue<boolean>(config.store, mgr.atoms.canSend)).toBe(true);
expect(atomValue<boolean>(config.store, mgr.atoms.canInterrupt)).toBe(true);
});

it('is a no-op without active session', async () => {
const config = createMockConfig();
const mgr = createSessionManager(config);
Expand Down
18 changes: 14 additions & 4 deletions src/lib/cloud-agent-sdk/session-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -601,19 +601,29 @@ function createSessionManager(config: SessionManagerConfig): SessionManager {

async function interrupt(): Promise<void> {
if (!currentSession) return;
// Snapshot before await — switchSession()/destroy() can swap currentSession while in flight.
const session = currentSession;
// Eagerly disable send/interrupt to prevent the user from sending a
// message while the async interrupt HTTP call is in flight. We do NOT
// call disconnect() — interrupt stops the agent but keeps the transport
// alive so the user can continue the session.
store.set(canSendAtom, false);
store.set(canInterruptAtom, false);
try {
if (currentSession.canInterrupt) {
await currentSession.interrupt();
if (session.canInterrupt) {
await session.interrupt();
}
if (currentSession === session) {
setIndicator({ type: 'info', message: 'Session stopped', timestamp: Date.now() });
}
setIndicator({ type: 'info', message: 'Session stopped', timestamp: Date.now() });
} catch {
store.set(errorAtom, 'Failed to stop execution');
if (currentSession === session) {
Comment thread
eshurakov marked this conversation as resolved.
store.set(canInterruptAtom, session.canInterrupt);
const cs = store.get(cloudStatusAtom);
const cloudReady = cs === null || cs.type === 'ready';
store.set(canSendAtom, session.canSend && cloudReady);
store.set(errorAtom, 'Failed to stop execution');
}
}
}

Expand Down
Loading