From 36e7f214db720459c4121f35b2edbf7077b98b6f Mon Sep 17 00:00:00 2001 From: xingzhou Date: Wed, 1 Jul 2026 18:54:57 +0800 Subject: [PATCH] fix(sessions): preserve lineage metadata in JSON --- src/commands/sessions-table.ts | 26 +++++++++++++++ src/commands/sessions.test.ts | 59 ++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/src/commands/sessions-table.ts b/src/commands/sessions-table.ts index 2db82da8cdb0..97949d14815a 100644 --- a/src/commands/sessions-table.ts +++ b/src/commands/sessions-table.ts @@ -14,6 +14,19 @@ export type SessionDisplayRow = { updatedAt: number | null; ageMs: number | null; sessionId?: string; + sessionFile?: string; + spawnedBy?: string; + spawnedWorkspaceDir?: string; + spawnedCwd?: string; + parentSessionKey?: string; + forkedFromParent?: boolean; + spawnDepth?: number; + subagentRole?: SessionEntry["subagentRole"]; + subagentControlScope?: SessionEntry["subagentControlScope"]; + sessionStartedAt?: number; + lastInteractionAt?: number; + label?: string; + status?: SessionEntry["status"]; systemSent?: boolean; abortedLastRun?: boolean; thinkingLevel?: string; @@ -47,6 +60,19 @@ export function toSessionDisplayRow(key: string, entry: SessionEntry): SessionDi updatedAt, ageMs: updatedAt ? Date.now() - updatedAt : null, sessionId: entry?.sessionId, + sessionFile: entry?.sessionFile, + spawnedBy: entry?.spawnedBy, + spawnedWorkspaceDir: entry?.spawnedWorkspaceDir, + spawnedCwd: entry?.spawnedCwd, + parentSessionKey: entry?.parentSessionKey, + forkedFromParent: entry?.forkedFromParent, + spawnDepth: entry?.spawnDepth, + subagentRole: entry?.subagentRole, + subagentControlScope: entry?.subagentControlScope, + sessionStartedAt: entry?.sessionStartedAt, + lastInteractionAt: entry?.lastInteractionAt, + label: entry?.label, + status: entry?.status, systemSent: entry?.systemSent, abortedLastRun: entry?.abortedLastRun, thinkingLevel: entry?.thinkingLevel, diff --git a/src/commands/sessions.test.ts b/src/commands/sessions.test.ts index cafc56fc6186..8aeb594bae3b 100644 --- a/src/commands/sessions.test.ts +++ b/src/commands/sessions.test.ts @@ -181,6 +181,65 @@ describe("sessionsCommand", () => { expect(group?.totalTokensFresh).toBe(false); }); + it("exports subagent lineage metadata in JSON output", async () => { + const store = writeStore({ + "agent:child:main": { + sessionId: "child-session", + updatedAt: Date.now() - 10 * 60_000, + sessionFile: "/tmp/openclaw/child-session.jsonl", + spawnedBy: "agent:main:main", + spawnedWorkspaceDir: "/workspace/project", + spawnedCwd: "/workspace/project/tasks", + parentSessionKey: "agent:main:main", + forkedFromParent: true, + spawnDepth: 1, + subagentRole: "leaf", + subagentControlScope: "none", + sessionStartedAt: Date.now() - 20 * 60_000, + lastInteractionAt: Date.now() - 5 * 60_000, + label: "research helper", + status: "done", + model: "test:opus", + }, + }); + + const payload = await runSessionsJson<{ + sessions?: Array<{ + key: string; + sessionFile?: string; + spawnedBy?: string; + spawnedWorkspaceDir?: string; + spawnedCwd?: string; + parentSessionKey?: string; + forkedFromParent?: boolean; + spawnDepth?: number; + subagentRole?: string; + subagentControlScope?: string; + sessionStartedAt?: number; + lastInteractionAt?: number; + label?: string; + status?: string; + }>; + }>(sessionsCommand, store); + + const child = payload.sessions?.find((row) => row.key === "agent:child:main"); + expect(child).toMatchObject({ + sessionFile: "/tmp/openclaw/child-session.jsonl", + spawnedBy: "agent:main:main", + spawnedWorkspaceDir: "/workspace/project", + spawnedCwd: "/workspace/project/tasks", + parentSessionKey: "agent:main:main", + forkedFromParent: true, + spawnDepth: 1, + subagentRole: "leaf", + subagentControlScope: "none", + sessionStartedAt: Date.now() - 20 * 60_000, + lastInteractionAt: Date.now() - 5 * 60_000, + label: "research helper", + status: "done", + }); + }); + it("shows preserved stale totals in JSON output", async () => { const store = writeStore({ main: {