chore: establish YCode replication baseline and TODO
This commit is contained in:
commit
6dc59f89ba
1118 files changed
+196002
No files matched your search
@@ -0,0 +1,121 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { applyAssistantEvent, getMessageParts } from '../src/shared/message-parts'
|
||||
import type { ChatEvent, ChatMessage, ToolCallRecord } from '../src/shared/types'
|
||||
|
||||
function tool(overrides: Partial<ToolCallRecord> = {}): ToolCallRecord {
|
||||
return {
|
||||
id: 'tool-1',
|
||||
name: 'read_file',
|
||||
args: { path: 'notes.txt' },
|
||||
argsRaw: '{"path":"notes.txt"}',
|
||||
status: 'running',
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
function reduce(message: ChatMessage, event: ChatEvent): ChatMessage {
|
||||
return applyAssistantEvent(message, event)
|
||||
}
|
||||
|
||||
function testOrderedPartsAndImmutableUpdates(): void {
|
||||
const initial: ChatMessage = {
|
||||
id: 'assistant-1',
|
||||
role: 'assistant',
|
||||
content: '',
|
||||
tools: [],
|
||||
parts: [],
|
||||
createdAt: 1,
|
||||
status: 'streaming'
|
||||
}
|
||||
|
||||
const afterFirstText = reduce(initial, { runId: 'run-1', type: 'text', delta: 'before' })
|
||||
const afterToolCall = reduce(afterFirstText, { runId: 'run-1', type: 'tool_call', call: tool() })
|
||||
const afterSecondText = reduce(afterToolCall, { runId: 'run-1', type: 'text', delta: 'after' })
|
||||
const afterToolResult = reduce(afterSecondText, {
|
||||
runId: 'run-1',
|
||||
type: 'tool_result',
|
||||
call: tool({ status: 'ok', output: 'file contents', summary: 'file contents', durationMs: 12 })
|
||||
})
|
||||
const final = reduce(afterToolResult, {
|
||||
runId: 'run-1',
|
||||
type: 'done',
|
||||
turnDurationMs: 22000,
|
||||
content: 'beforeafter',
|
||||
tools: [tool({ status: 'ok', output: 'file contents', summary: 'file contents', durationMs: 12 })]
|
||||
})
|
||||
|
||||
assert.deepEqual(final.parts, [
|
||||
{ type: 'text', text: 'before' },
|
||||
{ type: 'tool', toolId: 'tool-1' },
|
||||
{ type: 'text', text: 'after' }
|
||||
])
|
||||
assert.equal(final.content, 'beforeafter')
|
||||
assert.equal(final.status, 'done')
|
||||
assert.equal(final.turnDurationMs, 22000, 'terminal duration survives the shared live/persistence projection')
|
||||
assert.equal(final.tools?.length, 1, 'tool updates must not create duplicate tool records')
|
||||
assert.equal(final.parts?.filter((part) => part.type === 'tool' && part.toolId === 'tool-1').length, 1,
|
||||
'tool updates must not create duplicate ordered references')
|
||||
assert.equal(final.tools?.[0].status, 'ok')
|
||||
assert.equal(final.tools?.[0].output, 'file contents')
|
||||
|
||||
assert.equal(initial.content, '', 'reducing events must not mutate the original message')
|
||||
assert.deepEqual(initial.parts, [], 'reducing events must not mutate the original parts array')
|
||||
assert.deepEqual(afterFirstText.parts, [{ type: 'text', text: 'before' }],
|
||||
'later text and tool updates must not mutate prior message snapshots')
|
||||
assert.equal(afterFirstText.content, 'before')
|
||||
assert.deepEqual(afterToolResult.parts, [
|
||||
{ type: 'text', text: 'before' },
|
||||
{ type: 'tool', toolId: 'tool-1' },
|
||||
{ type: 'text', text: 'after' }
|
||||
])
|
||||
}
|
||||
|
||||
function testLegacyFallback(): void {
|
||||
const legacy: ChatMessage = {
|
||||
id: 'legacy-1',
|
||||
role: 'assistant',
|
||||
content: 'legacy response',
|
||||
tools: [tool({ id: 'legacy-tool', status: 'ok' })],
|
||||
createdAt: 2
|
||||
}
|
||||
|
||||
assert.deepEqual(getMessageParts(legacy), [
|
||||
{ type: 'text', text: 'legacy response' },
|
||||
{ type: 'tool', toolId: 'legacy-tool' }
|
||||
])
|
||||
}
|
||||
|
||||
function testTerminalToolFinalization(): void {
|
||||
const initial: ChatMessage = {
|
||||
id: 'assistant-terminal',
|
||||
role: 'assistant',
|
||||
content: 'partial answer',
|
||||
tools: [tool({ output: 'retained output' })],
|
||||
parts: [
|
||||
{ type: 'text', text: 'partial answer' },
|
||||
{ type: 'tool', toolId: 'tool-1' }
|
||||
],
|
||||
createdAt: 3,
|
||||
status: 'streaming'
|
||||
}
|
||||
|
||||
const cancelled = reduce(initial, { runId: 'run-cancel', type: 'cancelled' })
|
||||
assert.equal(cancelled.status, 'cancelled')
|
||||
assert.equal(cancelled.tools?.[0].status, 'cancelled')
|
||||
assert.equal(cancelled.tools?.[0].output, 'retained output')
|
||||
|
||||
const errored = reduce(initial, { runId: 'run-error', type: 'error', message: 'connection lost' })
|
||||
assert.equal(errored.status, 'error')
|
||||
assert.equal(errored.error, 'connection lost')
|
||||
assert.equal(errored.tools?.[0].status, 'error')
|
||||
assert.equal(errored.tools?.[0].output, 'retained output')
|
||||
|
||||
assert.equal(initial.status, 'streaming', 'terminal updates must leave the prior message unchanged')
|
||||
assert.equal(initial.tools?.[0].status, 'running', 'terminal updates must leave prior tool state unchanged')
|
||||
assert.equal(initial.tools?.[0].output, 'retained output')
|
||||
}
|
||||
|
||||
testOrderedPartsAndImmutableUpdates()
|
||||
testLegacyFallback()
|
||||
testTerminalToolFinalization()
|
||||
console.log('message-order-regression: all checks passed')
|
||||
Reference in new issue
Block a user