diff --git a/scripts/control-ui-mock-dev.ts b/scripts/control-ui-mock-dev.ts index 0e0cc09ab80a..c778fd0e16c7 100644 --- a/scripts/control-ui-mock-dev.ts +++ b/scripts/control-ui-mock-dev.ts @@ -30,12 +30,26 @@ type CliOptions = { }; type SessionListOptions = { + creators?: readonly SessionCreatorFixture[]; hasMore: boolean; nextOffset: number | null; offset?: number; totalCount: number; }; +type SessionCreatorFixture = { type: "human" | "agent"; id: string; label: string }; + +// Two creator identities so the sidebar's collaborative ownership chrome +// (owner avatars + People filter) renders in the mock harness. +const MOCK_SESSION_CREATORS: readonly SessionCreatorFixture[] = [ + { type: "human", id: "profile-peter", label: "Peter" }, + { type: "human", id: "profile-mira", label: "Mira" }, +]; +const [MOCK_CREATOR_PETER, MOCK_CREATOR_MIRA] = MOCK_SESSION_CREATORS as [ + SessionCreatorFixture, + SessionCreatorFixture, +]; + const SESSION_PAGE_SIZE = 50; const TOTAL_MOCK_SESSIONS = 650; const TOTAL_TELEGRAM_SESSIONS = 180; @@ -175,6 +189,7 @@ function sessionsListResponse(sessions: unknown[], options: SessionListOptions) hasMore: options.hasMore, limitApplied: 50, nextOffset: options.nextOffset, + ...(options.creators ? { creators: options.creators } : {}), offset: options.offset ?? 0, path: "", sessions, @@ -183,11 +198,16 @@ function sessionsListResponse(sessions: unknown[], options: SessionListOptions) }; } -function pagedSessionsListResponse(sessions: unknown[], offset: number) { +function pagedSessionsListResponse( + sessions: unknown[], + offset: number, + creators?: readonly SessionCreatorFixture[], +) { const normalizedOffset = Math.max(0, Math.floor(offset)); const page = sessions.slice(normalizedOffset, normalizedOffset + SESSION_PAGE_SIZE); const nextOffset = normalizedOffset + SESSION_PAGE_SIZE; return sessionsListResponse(page, { + creators, hasMore: nextOffset < sessions.length, nextOffset: nextOffset < sessions.length ? nextOffset : null, offset: normalizedOffset, @@ -218,17 +238,18 @@ function buildSessionRows(params: { function buildSessionListCases( sessions: unknown[], matchBase: Record = {}, + creators?: readonly SessionCreatorFixture[], ): Array<{ match: Record; response: unknown }> { const cases: Array<{ match: Record; response: unknown }> = []; for (let offset = SESSION_PAGE_SIZE; offset < sessions.length; offset += SESSION_PAGE_SIZE) { cases.push({ match: { ...matchBase, offset }, - response: pagedSessionsListResponse(sessions, offset), + response: pagedSessionsListResponse(sessions, offset, creators), }); } cases.push({ match: matchBase, - response: pagedSessionsListResponse(sessions, 0), + response: pagedSessionsListResponse(sessions, 0, creators), }); return cases; } @@ -1080,6 +1101,7 @@ async function createChatPickerScenario(): Promise status: "running", }), sessionRow(NARRATION_DEMO_SESSION_KEY, "Sidebar narration demo", baseTime - 15_000, { + createdActor: MOCK_CREATOR_MIRA, hasActiveRun: true, startedAt: baseTime - 45_000, status: "running", @@ -1092,6 +1114,7 @@ async function createChatPickerScenario(): Promise icon: "name:spark", }), sessionRow("agent:main:production-export", "Production export", baseTime - 75_000, { + createdActor: MOCK_CREATOR_MIRA, execCwd: "/Users/peter/Projects/clawdbot", }), sessionRow("agent:main:model-budget", "Model budget review", baseTime - 80_000, { @@ -1100,6 +1123,7 @@ async function createChatPickerScenario(): Promise lastRunError: "Model out of credits: openai/gpt-5.6", }), sessionRow("agent:main:work-openclaw", "OpenClaw work checkout", baseTime - 85_000, { + createdActor: MOCK_CREATOR_PETER, execCwd: "/Users/peter/Work/openclaw", lastReadAt: baseTime - 120_000, observerDigest: { @@ -1144,6 +1168,8 @@ async function createChatPickerScenario(): Promise const archivedSessions = [ sessionRow("agent:main:archived-launch-notes", "Archived launch notes", baseTime - 86_400_000, { archived: true, + archivedBy: MOCK_CREATOR_MIRA, + createdActor: MOCK_CREATOR_PETER, totalTokens: 42_000, }), sessionRow( @@ -1708,7 +1734,7 @@ async function createChatPickerScenario(): Promise ...searchPrefixes("claude-sonnet-4-6"), ...searchPrefixes("anthropic"), ]), - ...buildSessionListCases([...sessions, ...archivedSessions]), + ...buildSessionListCases([...sessions, ...archivedSessions], {}, MOCK_SESSION_CREATORS), ], }, }, diff --git a/ui/src/components/app-sidebar-session-list-render.ts b/ui/src/components/app-sidebar-session-list-render.ts index 09a5ff25af20..ae0e485cbd45 100644 --- a/ui/src/components/app-sidebar-session-list-render.ts +++ b/ui/src/components/app-sidebar-session-list-render.ts @@ -168,7 +168,9 @@ function renderSessionSection(params: { ? html`