feat(client): improve workspace session browsing

This commit is contained in:
_Kerman
2026-08-11 13:20:16 +08:00
parent c172faed37
commit 8e0cb2bdba
19 changed files with 198 additions and 62 deletions

View File

@@ -16,6 +16,7 @@ export interface SessionsPortSummary {
/** Empty-log bit (blank sessions are reused by New Session instead of minting another). */
blank: boolean
cwd?: string
createdAt: number
updatedAt: number
}

View File

@@ -17,6 +17,7 @@ export interface TitledSessionSummary extends SessionSummary {
export interface SessionListEntry {
sessionId: SessionId
title?: string
createdAt: number
updatedAt: number
running: boolean
/** Empty-log bit mirrored from the summary; lists hide blank sessions (filtering stays with the consumer). */
@@ -77,6 +78,7 @@ export function flattenLineage(
const pendingInteraction = pendingInteractions?.get(s.sessionId)
out.push({
...s,
createdAt: s.createdAt ?? s.updatedAt,
...(pendingInteraction === undefined ? {} : { pendingInteraction }),
completed: completed?.has(s.sessionId) ?? false,
depth,

View File

@@ -540,8 +540,9 @@ export class SessionManager {
: { ...(opts.cwd === undefined ? {} : { cwd: opts.cwd }), ...shared }
const { result } = await this.api.sessions.create(payload)
if (result.ok) {
const createdAt = Date.now()
this.recordMutation({ kind: 'upsert', summary: {
sessionId: result.value.sessionId, updatedAt: Date.now(), running: false, blank: true,
sessionId: result.value.sessionId, createdAt, updatedAt: createdAt, running: false, blank: true,
...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),
...(result.value.agentPreset !== undefined ? { agentPreset: result.value.agentPreset } : {}),
} })
@@ -551,9 +552,11 @@ export class SessionManager {
// so expose it immediately as Ungrouped while the caller keeps the
// prompt buffer and decides whether to retry attachment.
if (publishedSessionId !== undefined) {
const createdAt = Date.now()
this.recordMutation({ kind: 'upsert', summary: {
sessionId: publishedSessionId,
updatedAt: Date.now(),
createdAt,
updatedAt: createdAt,
running: false,
blank: true,
} })
@@ -587,8 +590,9 @@ export class SessionManager {
? result.value.sessionId
: workspaceAttachSessionId(result.error)
if (childId !== undefined) {
const createdAt = Date.now()
this.recordMutation({ kind: 'upsert', summary: {
sessionId: childId, updatedAt: Date.now(), running: false, blank: false,
sessionId: childId, createdAt, updatedAt: createdAt, running: false, blank: false,
parentSessionId: opts.sessionId,
...(source?.cwd !== undefined ? { cwd: source.cwd } : {}),
} })
@@ -615,8 +619,9 @@ export class SessionManager {
* @param agentPreset - the preset id the host confirmed.
*/
noteAgentPreset(sessionId: SessionId, agentPreset: string): void {
const createdAt = Date.now()
this.recordMutation({ kind: 'upsert', summary: {
sessionId, updatedAt: Date.now(), running: false, blank: true, agentPreset,
sessionId, createdAt, updatedAt: createdAt, running: false, blank: true, agentPreset,
} })
}
@@ -783,8 +788,10 @@ export class SessionManager {
const frame = envelope.payload
switch (frame.type) {
case 'host/session-added': {
const createdAt = frame.createdAt ?? Date.now()
this.mergeSummary({
sessionId: frame.sessionId, updatedAt: Date.now(), running: false, blank: frame.blank,
sessionId: frame.sessionId, createdAt, updatedAt: createdAt,
running: false, blank: frame.blank,
...(frame.parentSessionId !== undefined ? { parentSessionId: frame.parentSessionId } : {}),
...(frame.origin !== undefined ? { origin: frame.origin } : {}),
...(frame.cwd !== undefined ? { cwd: frame.cwd } : {}),
@@ -1037,7 +1044,8 @@ export class SessionManager {
const items = fresh.map((entry) => {
const prev = this.entryCache.get(entry.sessionId)
if (
prev !== undefined && prev.updatedAt === entry.updatedAt && prev.running === entry.running
prev !== undefined && prev.createdAt === entry.createdAt
&& prev.updatedAt === entry.updatedAt && prev.running === entry.running
&& prev.blank === entry.blank && prev.agentPreset === entry.agentPreset
&& prev.parentSessionId === entry.parentSessionId && prev.cwd === entry.cwd
&& prev.origin === entry.origin && prev.title === entry.title && prev.depth === entry.depth

View File

@@ -66,6 +66,8 @@ export interface SessionSummary {
* selected blank entry.
*/
blank: boolean
/** Durable session creation time. */
createdAt: number
updatedAt: number
/** Current host-computed projection values retained by the object layer. */
projectionValues?: Readonly<Partial<SessionProjectionMap>>
@@ -667,6 +669,7 @@ export class SessionsService implements ISessions {
running: entry.running,
...(entry.completed ? { completed: true } : {}),
blank: entry.blank,
createdAt: entry.createdAt,
updatedAt: entry.updatedAt,
...(entry.pendingInteraction === undefined
? {}
@@ -700,6 +703,7 @@ export class SessionsService implements ISessions {
origin: 'subagent',
running: child.activity === 'running',
blank: false,
createdAt: 0,
updatedAt: 0,
}
} else if (summary.displayTitle !== displayTitle) {