mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
* fix(native-chat): publish retained history corrections to capable clients * test(native-chat): expose canonical reads in phone history fixture * test(native-chat): migrate remaining history reader fixtures * fix(native-chat): preserve expanded history on late acceptance * fix(native-chat): share lazy correction metadata by journal source * fix(native-chat): reuse raw legacy display selections
169 lines
6.0 KiB
JavaScript
169 lines
6.0 KiB
JavaScript
import { readFile } from 'node:fs/promises'
|
|
import { expect, it } from 'vitest'
|
|
import { readAgentSessionHistory } from '../../src/main/native-chat/agent-session-wire/agent-session-history-page.ts'
|
|
import { projectStructuredAgentSessionMessages } from '../../src/renderer/src/components/native-chat/structured-agent-session-message-projection.ts'
|
|
import { nativeChatTurnMembership } from '../../src/shared/native-chat-turn-membership.ts'
|
|
import { nativeChatRowsInDrawOrder } from '../../src/shared/native-chat-turn-grouping.ts'
|
|
import { selectNativeChatTurnStatuses } from '../../src/shared/native-chat-turn-status.ts'
|
|
import { selectStructuredAgentSettledTurns } from '../../src/shared/structured-agent-session-turn-timing.ts'
|
|
import { buildNativeChatTranscriptSlots } from '../../src/renderer/src/components/native-chat/native-chat-transcript-slots.ts'
|
|
|
|
async function snapshotWithLaterHistory() {
|
|
const captured = JSON.parse(
|
|
await readFile(
|
|
new URL('../../src/main/acp/fixtures/omp-v17-compact-skip.render.jsonl', import.meta.url),
|
|
'utf8'
|
|
)
|
|
)
|
|
const items = [...captured.items]
|
|
// Later turns are constructed test history; the skipped outcome is captured.
|
|
for (let n = 0; n < 66; n++) {
|
|
const sequence = 24 + n * 3
|
|
const startedAt = 1791468228000 + n * 1000
|
|
const userItemId = `later-user-${n}`
|
|
const turnItemId = `later-turn-${n}`
|
|
items.push(
|
|
{
|
|
itemId: userItemId,
|
|
revision: 1,
|
|
sequence,
|
|
observedAt: startedAt,
|
|
turnScope: { kind: 'thread' },
|
|
body: {
|
|
kind: 'message',
|
|
role: 'user',
|
|
blocks: [{ type: 'text', text: `later request ${n}` }]
|
|
}
|
|
},
|
|
{
|
|
itemId: turnItemId,
|
|
revision: 2,
|
|
sequence: sequence + 1,
|
|
observedAt: startedAt,
|
|
turnScope: { kind: 'thread' },
|
|
body: {
|
|
kind: 'turn',
|
|
turnId: turnItemId,
|
|
state: 'completed',
|
|
outcome: 'success',
|
|
userItemId,
|
|
requestedAt: startedAt,
|
|
startedAt,
|
|
completedAt: startedAt + 7
|
|
}
|
|
},
|
|
{
|
|
itemId: `later-answer-${n}`,
|
|
revision: 1,
|
|
sequence: sequence + 2,
|
|
observedAt: startedAt + 7,
|
|
turnScope: { kind: 'turn', turnItemId },
|
|
body: {
|
|
kind: 'message',
|
|
role: 'assistant',
|
|
blocks: [{ type: 'text', text: `later answer ${n}` }]
|
|
}
|
|
}
|
|
)
|
|
}
|
|
return {
|
|
sessionId: 'session-1',
|
|
cursor: { epoch: 'epoch-1', sequence: 221 },
|
|
items,
|
|
submissions: captured.submissions
|
|
}
|
|
}
|
|
|
|
function slotsFor(page) {
|
|
const messages = projectStructuredAgentSessionMessages(page.items, [], page.submissions)
|
|
const membership = nativeChatTurnMembership(messages, page)
|
|
const turnStatuses = selectNativeChatTurnStatuses(
|
|
{},
|
|
{
|
|
activeTurnKey: membership.liveTurnKey ?? '',
|
|
isWorking: false,
|
|
thinking: false,
|
|
settledByTurn: selectStructuredAgentSettledTurns(page.items, page.submissions)
|
|
}
|
|
)
|
|
return buildNativeChatTranscriptSlots({
|
|
messages: nativeChatRowsInDrawOrder(messages, membership.drawOrder),
|
|
turnKeys: nativeChatRowsInDrawOrder(membership.turnKeys, membership.drawOrder),
|
|
liveTurnKey: membership.liveTurnKey,
|
|
receipts: new Map(),
|
|
turnStatuses,
|
|
turnDiffs: new Map(),
|
|
expandedTurnKeys: new Set(),
|
|
isWorking: false,
|
|
lifecycleWorking: false
|
|
})
|
|
}
|
|
|
|
it('keeps the captured skip visible when the actual 200-item history tail omits only its opener', async () => {
|
|
const snapshot = await snapshotWithLaterHistory()
|
|
const result = readAgentSessionHistory(
|
|
{ publicRead: () => ({ snapshot, normalizationKey: '' }) },
|
|
{ sessionId: 'session-1', direction: 'tail', limit: 200 }
|
|
)
|
|
expect(result.ok).toBe(true)
|
|
const page = result.page
|
|
expect(page.items).toHaveLength(200)
|
|
expect(page.items[0].itemId).toBe('orca:command-turn%3Acompact-1')
|
|
expect(page.items[1].body.text).toBe('Nothing to compact (session too small)')
|
|
expect(page.submissions).toEqual([])
|
|
const slot = slotsFor(page).find(
|
|
(slot) =>
|
|
slot.kind === 'message' &&
|
|
slot.message.blocks.some(
|
|
(block) => block.type === 'text' && block.text === 'Nothing to compact (session too small)'
|
|
)
|
|
)
|
|
expect(slot).toMatchObject({ drawsMessage: true, folded: false })
|
|
})
|
|
|
|
it('restores the captured skip after the real older-page read loads its opener', async () => {
|
|
const snapshot = await snapshotWithLaterHistory()
|
|
const tail = readAgentSessionHistory(
|
|
{ publicRead: () => ({ snapshot, normalizationKey: '' }) },
|
|
{ sessionId: 'session-1', direction: 'tail', limit: 200 }
|
|
).page
|
|
const older = readAgentSessionHistory(
|
|
{ publicRead: () => ({ snapshot, normalizationKey: '' }) },
|
|
{ sessionId: 'session-1', direction: 'before', cursor: tail.window.nextCursor, limit: 200 }
|
|
).page
|
|
expect(older.items.map((item) => item.itemId)).toEqual(['orca:compact-1'])
|
|
const slots = slotsFor({
|
|
...tail,
|
|
items: [...older.items, ...tail.items],
|
|
submissions: older.submissions
|
|
})
|
|
const slot = slots.find(
|
|
(slot) =>
|
|
slot.kind === 'message' &&
|
|
slot.message.blocks.some(
|
|
(block) => block.type === 'text' && block.text === 'Nothing to compact (session too small)'
|
|
)
|
|
)
|
|
expect(slot).toMatchObject({ drawsMessage: true, folded: false })
|
|
})
|
|
|
|
it('continues folding an ordinary warning at the same real history boundary', async () => {
|
|
const snapshot = await snapshotWithLaterHistory()
|
|
snapshot.items = snapshot.items.map((item) =>
|
|
item.body.kind === 'status' ? { ...item, itemId: 'orca:ordinary-warning' } : item
|
|
)
|
|
const result = readAgentSessionHistory(
|
|
{ publicRead: () => ({ snapshot, normalizationKey: '' }) },
|
|
{ sessionId: 'session-1', direction: 'tail', limit: 200 }
|
|
)
|
|
expect(result.ok).toBe(true)
|
|
const slot = slotsFor(result.page).find(
|
|
(slot) =>
|
|
slot.kind === 'message' &&
|
|
slot.message.blocks.some(
|
|
(block) => block.type === 'text' && block.text === 'Nothing to compact (session too small)'
|
|
)
|
|
)
|
|
expect(slot).toMatchObject({ drawsMessage: false, folded: true })
|
|
})
|