fix(agent-claude-code): map bookkeeping JSONL types to ready, not active
Claude writes several types AFTER finishing a turn — `file-history-snapshot`, `attachment`, `pr-link`, `queue-operation`, `permission-mode`, `last-prompt`, `ai-title`, `agent-color`, `agent-name`, `custom-title`. Until now they fell through to the `default` switch branch and looked `active` for 30s, making finished sessions appear busy. This was almost certainly the root cause of "Claude looks like it's still working when it's done" reports (the #1908 family). Add explicit cases mapping each to `ready`/`idle` by age, same as `assistant`/`summary`. Three existing tests that asserted these returned `active` were testing the buggy behavior — updated to the correct behavior, plus four new tests covering `attachment`, `permission-mode`, and `ai-title`.
This commit is contained in:
parent
a610601158
commit
39c2b1ddea
|
|
@ -276,19 +276,38 @@ describe("Claude Code Activity Detection", () => {
|
|||
expect((await agent.getActivityState(makeSession()))?.state).toBe("ready");
|
||||
});
|
||||
|
||||
it("returns 'active' for recent 'file-history-snapshot' (bookkeeping)", async () => {
|
||||
// Bookkeeping types Claude writes AFTER finishing a turn — these are
|
||||
// turn-end markers, not "Claude is working" signals. They must map to
|
||||
// ready/idle by age, NOT active. Previously they fell through to the
|
||||
// default branch and looked "active" for 30s; fixed in this PR.
|
||||
it("returns 'ready' for recent 'file-history-snapshot' (bookkeeping)", async () => {
|
||||
writeJsonl([{ type: "file-history-snapshot" }]);
|
||||
expect((await agent.getActivityState(makeSession()))?.state).toBe("active");
|
||||
expect((await agent.getActivityState(makeSession()))?.state).toBe("ready");
|
||||
});
|
||||
|
||||
it("returns 'active' for recent 'queue-operation' (bookkeeping)", async () => {
|
||||
it("returns 'ready' for recent 'queue-operation' (bookkeeping)", async () => {
|
||||
writeJsonl([{ type: "queue-operation" }]);
|
||||
expect((await agent.getActivityState(makeSession()))?.state).toBe("active");
|
||||
expect((await agent.getActivityState(makeSession()))?.state).toBe("ready");
|
||||
});
|
||||
|
||||
it("returns 'active' for recent 'pr-link' (bookkeeping)", async () => {
|
||||
it("returns 'ready' for recent 'pr-link' (bookkeeping)", async () => {
|
||||
writeJsonl([{ type: "pr-link" }]);
|
||||
expect((await agent.getActivityState(makeSession()))?.state).toBe("active");
|
||||
expect((await agent.getActivityState(makeSession()))?.state).toBe("ready");
|
||||
});
|
||||
|
||||
it("returns 'ready' for recent 'attachment' (bookkeeping)", async () => {
|
||||
writeJsonl([{ type: "attachment", attachment: { type: "skill_listing" } }]);
|
||||
expect((await agent.getActivityState(makeSession()))?.state).toBe("ready");
|
||||
});
|
||||
|
||||
it("returns 'ready' for recent 'permission-mode' (bookkeeping)", async () => {
|
||||
writeJsonl([{ type: "permission-mode", permissionMode: "bypassPermissions" }]);
|
||||
expect((await agent.getActivityState(makeSession()))?.state).toBe("ready");
|
||||
});
|
||||
|
||||
it("returns 'ready' for recent UI-metadata bookkeeping (ai-title)", async () => {
|
||||
writeJsonl([{ type: "ai-title", title: "Fix login bug" }]);
|
||||
expect((await agent.getActivityState(makeSession()))?.state).toBe("ready");
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -304,6 +304,25 @@ export async function getClaudeActivityState(
|
|||
case "result":
|
||||
return { state: ageMs > threshold ? "idle" : "ready", timestamp };
|
||||
|
||||
// Bookkeeping types Claude writes AFTER finishing a turn (UI metadata,
|
||||
// file snapshots, queue housekeeping). When one of these is the LAST
|
||||
// entry, Claude is done — not active. Without these explicit cases
|
||||
// they'd fall through to `default` and look `active` for 30s, which
|
||||
// is the root cause of "Claude looks busy when it's done" reports.
|
||||
// Survey of types observed in real ~/.claude/projects/ JSONL: see
|
||||
// PR #1927 description.
|
||||
case "file-history-snapshot":
|
||||
case "attachment":
|
||||
case "pr-link":
|
||||
case "queue-operation":
|
||||
case "permission-mode":
|
||||
case "last-prompt":
|
||||
case "ai-title":
|
||||
case "agent-color":
|
||||
case "agent-name":
|
||||
case "custom-title":
|
||||
return { state: ageMs > threshold ? "idle" : "ready", timestamp };
|
||||
|
||||
default:
|
||||
if (ageMs <= activeWindowMs) return { state: "active", timestamp };
|
||||
return { state: ageMs > threshold ? "idle" : "ready", timestamp };
|
||||
|
|
|
|||
Loading…
Reference in New Issue