Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { describe, expect, it } from 'bun:test'
import type { Message } from '@craft-agent/core/types'
import type { Workspace } from '@craft-agent/shared/config'
import { createManagedSession, SessionManager } from './SessionManager.ts'

type TestManagedSession = ReturnType<typeof createManagedSession>

const workspace: Workspace = {
id: 'workspace-qwen',
name: 'qwen-code',
slug: 'qwen-code',
rootPath: '/tmp/qwen-code',
createdAt: Date.parse('2026-06-17T12:00:00.000Z'),
}

function addSession(
manager: SessionManager,
managed: TestManagedSession,
): void {
(
manager as unknown as { sessions: Map<string, TestManagedSession> }
).sessions.set(managed.id, managed)
}

describe('SessionManager Qwen canonical mirror filtering', () => {
it('hides empty placeholder mirrors even when they have timestamps', () => {
const manager = new SessionManager()
const sessionId = '8390af4d-5db6-4e4c-b7e8-040d002690c7'
const timestamp = Date.parse('2026-06-17T10:15:30.000Z')

addSession(
manager,
createManagedSession(
{
id: sessionId,
sdkSessionId: sessionId,
name: '(session)',
messageCount: 0,
createdAt: timestamp,
lastUsedAt: timestamp,
lastMessageAt: timestamp,
llmConnection: 'qwen-code',
},
workspace,
),
)

expect(manager.getSessions(workspace.id)).toEqual([])
})

it('keeps external sessions with a real title or content', () => {
const manager = new SessionManager()
const titledSessionId = '12eb7d24-4c31-4ff5-8a9b-f243f9fd1b28'
const contentSessionId = 'bbc6bd08-a4f7-4b50-b605-51dbe51ea2de'
const timestamp = Date.parse('2026-06-17T10:15:30.000Z')

addSession(
manager,
createManagedSession(
{
id: titledSessionId,
sdkSessionId: titledSessionId,
name: 'Investigate Windows path expansion',
messageCount: 0,
lastMessageAt: timestamp,
llmConnection: 'qwen-code',
},
workspace,
),
)
addSession(
manager,
createManagedSession(
{
id: contentSessionId,
sdkSessionId: contentSessionId,
name: '(session)',
lastMessageAt: timestamp - 1,
llmConnection: 'qwen-code',
},
workspace,
{
messages: [
{
id: 'msg_1',
role: 'user',
content: 'real conversation content',
timestamp,
} as Message,
],
},
),
)

expect(
manager
.getSessions(workspace.id)
.map((session) => session.id)
.sort(),
).toEqual([contentSessionId, titledSessionId].sort())
})

it('treats malformed empty placeholder records as filterable', () => {
const manager = new SessionManager()
const sessionId = 'malformed-placeholder'
const internals = manager as unknown as {
isUnresolvedQwenCanonicalMirror: (
managed: Record<string, unknown>,
) => boolean
}
const malformed = {
id: sessionId,
sdkSessionId: sessionId,
name: '(session)',
messageCount: 0,
createdAt: Date.parse('2026-06-17T10:15:30.000Z'),
lastMessageAt: Date.parse('2026-06-17T10:15:30.000Z'),
llmConnection: 'qwen-code',
}

expect(() =>
internals.isUnresolvedQwenCanonicalMirror(malformed),
).not.toThrow()
expect(internals.isUnresolvedQwenCanonicalMirror(malformed)).toBe(true)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -2868,7 +2868,7 @@ export class SessionManager implements ISessionManager {
}

private hasNoRenderableLocalMessages(managed: ManagedSession): boolean {
const loadedMessageCount = managed.messages.length
const loadedMessageCount = managed.messages?.length ?? 0
const persistedMessageCount = managed.messageCount ?? loadedMessageCount
return loadedMessageCount === 0 && persistedMessageCount === 0
}
Expand All @@ -2882,16 +2882,11 @@ export class SessionManager implements ISessionManager {
QWEN_CODE_CONNECTION_SLUG
)
return false
if (managed.messages.length > 0) return false
if (managed.name && !this.isExternalSessionPlaceholderTitle(managed.name))
if (!this.hasNoRenderableLocalMessages(managed)) return false
const title = typeof managed.name === 'string' ? managed.name : undefined
if (title && !this.isExternalSessionPlaceholderTitle(title))
return false
if (managed.preview || managed.lastMessageRole) return false
if (
[managed.createdAt, managed.lastUsedAt, managed.lastMessageAt].some(
(timestamp) => typeof timestamp === 'number' && timestamp > 0,
)
)
return false

return true
}
Expand Down
33 changes: 33 additions & 0 deletions packages/desktop/packages/shared/src/utils/__tests__/paths.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { describe, expect, it } from 'bun:test'
import { homedir } from 'node:os'
import { join, normalize, resolve } from 'node:path'
import { expandPath } from '../paths.ts'

describe('expandPath', () => {
it('expands Windows-style tilde paths under home', () => {
expect(expandPath('~\\.craft-agent\\foo')).toBe(
join(homedir(), '.craft-agent', 'foo'),
)
})

it('expands bare Windows-style tilde prefixes', () => {
expect(expandPath('~\\')).toBe(normalize(homedir()))
})

it('keeps existing home expansion behavior', () => {
expect(expandPath('~')).toBe(homedir())
expect(expandPath('~/Documents')).toBe(join(homedir(), 'Documents'))
expect(expandPath('${HOME}/projects')).toBe(join(homedir(), 'projects'))
expect(expandPath('$HOME/projects')).toBe(join(homedir(), 'projects'))
})

it('keeps absolute and relative path behavior unchanged', () => {
const absolutePath = join(homedir(), 'already-absolute')
const basePath = join(homedir(), 'base')

expect(expandPath(absolutePath, basePath)).toBe(normalize(absolutePath))
expect(expandPath('relative/path', basePath)).toBe(
resolve(basePath, 'relative/path'),
)
})
})
12 changes: 9 additions & 3 deletions packages/desktop/packages/shared/src/utils/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,15 @@ export function expandPath(inputPath: string, basePath?: string): string {
return home;
}

// Handle ~/ prefix
if (expanded.startsWith('~/')) {
expanded = join(home, expanded.slice(2));
// Handle ~/ and ~\ prefixes
if (expanded.startsWith('~/') || expanded.startsWith('~\\')) {
expanded = join(
home,
...expanded
.slice(2)
.split(/[/\\]+/)
.filter(Boolean),
);
}

// Handle ${HOME} and $HOME variables
Expand Down
Loading