ask the agent what it is still running
Officer's session records are in memory and die with `pm2 restart officer`, while the agent is a PM2 peer and keeps generating. `adoptOrphanedSession` rebuilds a binding — but only when a browser reconnects to a session *by id*, which you can only do if you already knew the id. So a session that survived a restart was invisible, and nothing could answer "what is running right now". `claude:list` returns each live session with `isGenerating` and `pendingTasks` — the same two fields the agent's own `armIdle` consults before collecting a session, so a caller can tell "busy" from "merely open" the way it does. Surfaced as `GET /chat/live`, which sits beside `/chat/sessions`: those are transcripts on disk, these are the ones with a process behind them. `getActiveSessionKeys` is replaced rather than joined. It returned bare keys, could not distinguish a session mid-turn from one merely open, and had never been called by anything. `listLiveClaudeSessions` fails toward EMPTY, where `isClaudeGenerating` beside it fails toward alive. The asymmetry is deliberate: not knowing there means leaving a spinner up, and not knowing here would mean inventing sessions. Step 2 of docs/chat-session-lifetime.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import { homedir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { query, type Query } from '@anthropic-ai/claude-agent-sdk';
|
||||
import type { ChatEvent, PromptImage } from '../../api/chat/types';
|
||||
import type { ClaudeSpawnParams, ClaudeSpawnStreamingParams, ClaudeCodeResult } from '../protocol';
|
||||
import type { ClaudeSpawnParams, ClaudeSpawnStreamingParams, ClaudeCodeResult, LiveClaudeSession } from '../protocol';
|
||||
import { setClaudeSession, clearClaudeSession, getClaudeSession } from './state';
|
||||
import { createParseState, processMessage } from './stream-parser';
|
||||
|
||||
@@ -501,8 +501,19 @@ export function clearSession(sessionKey: string): void {
|
||||
clearClaudeSession(sessionKey);
|
||||
}
|
||||
|
||||
export function getActiveSessionKeys(): string[] {
|
||||
return Array.from(sessions.keys());
|
||||
/**
|
||||
* Everything this process is holding, with the two facts that decide whether it is busy.
|
||||
*
|
||||
* Replaces a `getActiveSessionKeys` that returned bare keys and was never called by anything — the keys
|
||||
* alone could not distinguish a session mid-turn from one merely open, which is the whole question a
|
||||
* caller has. These are the same two fields `armIdle` consults before collecting a session.
|
||||
*/
|
||||
export function listSessions(): LiveClaudeSession[] {
|
||||
return Array.from(sessions.values()).map((session) => ({
|
||||
sessionKey: session.sessionKey,
|
||||
isGenerating: session.isGenerating,
|
||||
pendingTasks: session.pendingTasks.size,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user