mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-13 04:03:30 +00:00
Merge remote-tracking branch 'origin/worktree/session-format-04-live-assistant-stream' into worktree/session-format-05-v1-v2-chunk-migration
# Conflicts: # apps/web/tests/scaffold.ts
This commit is contained in:
@@ -133,8 +133,9 @@ describe.skipIf(MODE === 'record')('web e2e: file and session references through
|
||||
|
||||
beforeAll(async () => {
|
||||
scaffold = await launchWebScaffold({})
|
||||
await seedSession(scaffold, sourceSessionFixture(), SOURCE_SESSION_ID)
|
||||
await seedSession(scaffold, targetSessionFixture(), TARGET_SESSION_ID)
|
||||
const targetCreatedAt = Date.now() - 60_000
|
||||
await seedSession(scaffold, sourceSessionFixture(), SOURCE_SESSION_ID, undefined, undefined, { createdAt: targetCreatedAt - 1 })
|
||||
await seedSession(scaffold, targetSessionFixture(), TARGET_SESSION_ID, undefined, undefined, { createdAt: targetCreatedAt })
|
||||
browser = await chromium.launch()
|
||||
page = await newEnglishPage(browser)
|
||||
tripwire = watchConsole(page)
|
||||
@@ -377,7 +378,11 @@ describe.skipIf(MODE === 'record')('web e2e: file and session references through
|
||||
const group = page.getByRole('treeitem', { name: /Ungrouped/ })
|
||||
await group.waitFor({ timeout: 15_000 })
|
||||
if (await group.getAttribute('aria-expanded') !== 'true') await group.click()
|
||||
const target = page.getByRole('treeitem', { name: /Reference order target/ })
|
||||
// Both logs were written behind the running Host and have no cache rows, so
|
||||
// cold listing uses their shared Workspace fallback. Explicit creation
|
||||
// times keep the target first without opening either body for a title.
|
||||
const groupSection = group.locator('xpath=ancestor::*[contains(@class, "groupSection")][1]')
|
||||
const target = groupSection.locator('[role="treeitem"]').nth(1)
|
||||
await target.waitFor({ timeout: 15_000 })
|
||||
await target.click()
|
||||
await page.getByRole('button', { name: /^Session recall\s*Research notes$/ }).waitFor({ timeout: 15_000 })
|
||||
|
||||
@@ -1227,6 +1227,7 @@ export async function seedSession(
|
||||
id: string,
|
||||
agentPreset?: string,
|
||||
fixturePath?: string,
|
||||
options: { readonly createdAt?: number } = {},
|
||||
): Promise<SessionId> {
|
||||
const decoded = parseSeedFixture(realizeSeedFixture(scaffold, fixtureText, id), fixturePath)
|
||||
const events = decoded.events
|
||||
@@ -1235,10 +1236,11 @@ export async function seedSession(
|
||||
// An open final turn would be mutated by resume's crash repair on first
|
||||
// open; a committed seed must be a closed recording.
|
||||
if (last.type !== 'turn/end') throw new Error(`seed fixture must end in turn/end, got ${last.type}`)
|
||||
const createdAt = options.createdAt ?? Date.now() - 60_000
|
||||
const meta: SessionHeader = {
|
||||
version: SESSION_FORMAT_VERSION,
|
||||
id: SessionId(id),
|
||||
createdAt: Date.now() - 60_000,
|
||||
createdAt,
|
||||
isSeeded: false,
|
||||
cwd: scaffold.workspaceCwd,
|
||||
delegationDepth: 0,
|
||||
@@ -1248,7 +1250,7 @@ export async function seedSession(
|
||||
if (typeof fixtureCreatedAt !== 'number') {
|
||||
throw new Error('seed fixture requires a numeric createdAt header')
|
||||
}
|
||||
const timeAnchor = fixtureCreatedAt === 0 ? meta.createdAt : fixtureCreatedAt
|
||||
const timeAnchor = fixtureCreatedAt === 0 ? createdAt : fixtureCreatedAt
|
||||
let nextTime = timeAnchor
|
||||
const materializedEvents: SessionEvent[] = events.map((event) => {
|
||||
const time = nextTime
|
||||
|
||||
@@ -172,42 +172,39 @@ describe('sessions.list cold merge', () => {
|
||||
expect(inspect).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('prefers a live row attached during the query without folding its seed', async () => {
|
||||
it('prefers a live row attached during cache lookup without folding its seed', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
const meta = header('attached-during-list', 100)
|
||||
const started = Promise.withResolvers<undefined>()
|
||||
const release = Promise.withResolvers<undefined>()
|
||||
providePersistence(ctx, {
|
||||
list: async () => {
|
||||
started.resolve(undefined)
|
||||
await release.promise
|
||||
return [meta]
|
||||
},
|
||||
list: () => Promise.resolve([meta]),
|
||||
})
|
||||
const cacheLookup = vi.fn(() => {
|
||||
const session = ctx.sessions.create(meta.id, {
|
||||
seed: [
|
||||
{ type: 'turn/start', seq: SessionSeq(0), time: 200, data: { turn: 1 } },
|
||||
{
|
||||
type: 'user/message', seq: SessionSeq(1), time: 300,
|
||||
data: createUserMessage({ content: [{ type: 'text', text: 'live' }], source: { kind: 'user' } }),
|
||||
surfaceOp: 'append',
|
||||
},
|
||||
],
|
||||
meta: {
|
||||
...meta.cwd === undefined ? {} : { cwd: meta.cwd },
|
||||
createdAt: meta.createdAt,
|
||||
},
|
||||
})
|
||||
ctx.agents.register({ id: session.id, session, status: 'running', ctx } as Agent)
|
||||
return undefined
|
||||
})
|
||||
ctx.provide('sessionProjectionCache', {
|
||||
cachedSnapshot: cacheLookup,
|
||||
cachedPredecessorTitle: () => undefined,
|
||||
} as never)
|
||||
const remote = createSessionTestRemote(ctx, { defaultModelSelection: () => ({ provider: 'p', model: 'm' }), cwd: '/tmp' })
|
||||
|
||||
const listing = remote.list(request({}))
|
||||
await started.promise
|
||||
const session = ctx.sessions.create(meta.id, {
|
||||
seed: [
|
||||
{ type: 'turn/start', seq: SessionSeq(0), time: 200, data: { turn: 1 } },
|
||||
{
|
||||
type: 'user/message', seq: SessionSeq(1), time: 300,
|
||||
data: createUserMessage({ content: [{ type: 'text', text: 'live' }], source: { kind: 'user' } }),
|
||||
surfaceOp: 'append',
|
||||
},
|
||||
],
|
||||
meta: {
|
||||
...meta.cwd === undefined ? {} : { cwd: meta.cwd },
|
||||
createdAt: meta.createdAt,
|
||||
},
|
||||
})
|
||||
ctx.agents.register({ id: session.id, session, status: 'running', ctx } as Agent)
|
||||
release.resolve(undefined)
|
||||
|
||||
const response = await listing
|
||||
const response = await remote.list(request({}))
|
||||
if (!response.ok) throw new Error('list failed')
|
||||
expect(response.value.items).toEqual([
|
||||
expect.objectContaining({
|
||||
@@ -217,6 +214,7 @@ describe('sessions.list cold merge', () => {
|
||||
updatedAt: 100,
|
||||
}),
|
||||
])
|
||||
expect(cacheLookup).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user