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:
@@ -1,5 +1,6 @@
|
||||
import type { Context } from 'hono';
|
||||
import { createRouter } from '../../create-router';
|
||||
import * as sidecar from '@@/sidecar-registry';
|
||||
import { getUserSettings } from 'officerdb';
|
||||
import {
|
||||
getGeneralChatSessionsCwd,
|
||||
@@ -91,6 +92,18 @@ chatRouter.get('/sessions/:id', async (ctx) => {
|
||||
});
|
||||
});
|
||||
|
||||
// GET /chat/live — the sessions the agent has a process behind RIGHT NOW, as opposed to the transcripts
|
||||
// on disk that `/chat/sessions` lists. Asked over the wire because only the agent can answer: officer's
|
||||
// own session records are in memory and die with `pm2 restart officer`, while the agent is a PM2 peer
|
||||
// and keeps running. Without this a surviving session is invisible until a browser reconnects to it by
|
||||
// id, which is a thing you can only do if you already knew the id.
|
||||
//
|
||||
// `pendingTasks` is background work started but not yet notified — with `isGenerating` it is what the
|
||||
// agent's own idle GC consults, so a caller can tell "busy" from "merely open" the same way it does.
|
||||
chatRouter.get('/live', async (ctx) => {
|
||||
return ctx.json({ sessions: await sidecar.listLiveClaudeSessions() });
|
||||
});
|
||||
|
||||
// DELETE /chat/sessions/:id[?cwd=] — remove a conversation from the owning harness's store. For a
|
||||
// Claude `/clear` chain that is every part of it: the list shows the chain as one conversation, so
|
||||
// deleting it deletes one conversation.
|
||||
|
||||
Reference in New Issue
Block a user