243 lines
7.1 KiB
TypeScript
243 lines
7.1 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
AgentStreamMessageSchema,
|
|
FetchAgentTimelineResponseMessageSchema,
|
|
SessionInboundMessageSchema,
|
|
SessionOutboundMessageSchema,
|
|
WSOutboundMessageSchema,
|
|
} from './messages.js'
|
|
|
|
describe('shared messages stream parsing', () => {
|
|
it('parses representative fetch_agent_timeline_response payload', () => {
|
|
const parsed = FetchAgentTimelineResponseMessageSchema.parse({
|
|
type: 'fetch_agent_timeline_response',
|
|
payload: {
|
|
requestId: 'req-1',
|
|
agentId: 'agent_live',
|
|
agent: null,
|
|
direction: 'tail',
|
|
projection: 'projected',
|
|
epoch: 'epoch-1',
|
|
reset: false,
|
|
staleCursor: false,
|
|
gap: false,
|
|
window: { minSeq: 1, maxSeq: 2, nextSeq: 3 },
|
|
startCursor: { epoch: 'epoch-1', seq: 1 },
|
|
endCursor: { epoch: 'epoch-1', seq: 2 },
|
|
hasOlder: false,
|
|
hasNewer: false,
|
|
entries: [
|
|
{
|
|
provider: 'codex',
|
|
item: { type: 'assistant_message', text: 'hello' },
|
|
timestamp: '2026-02-08T20:10:00.000Z',
|
|
seqStart: 1,
|
|
seqEnd: 2,
|
|
sourceSeqRanges: [{ startSeq: 1, endSeq: 2 }],
|
|
collapsed: ['assistant_merge'],
|
|
},
|
|
],
|
|
error: null,
|
|
},
|
|
})
|
|
|
|
expect(parsed.payload.entries).toHaveLength(1)
|
|
expect(parsed.payload.entries[0]?.item.type).toBe('assistant_message')
|
|
})
|
|
|
|
it('parses explicit shutdown and restart lifecycle request payloads as distinct message types', () => {
|
|
const shutdownParsed = SessionInboundMessageSchema.safeParse({
|
|
type: 'shutdown_server_request',
|
|
requestId: 'req-shutdown-1',
|
|
})
|
|
expect(shutdownParsed.success).toBe(true)
|
|
|
|
const restartParsed = SessionInboundMessageSchema.safeParse({
|
|
type: 'restart_server_request',
|
|
requestId: 'req-restart-1',
|
|
reason: 'settings_changed',
|
|
})
|
|
expect(restartParsed.success).toBe(true)
|
|
|
|
expect(shutdownParsed.success && shutdownParsed.data.type).toBe('shutdown_server_request')
|
|
expect(restartParsed.success && restartParsed.data.type).toBe('restart_server_request')
|
|
})
|
|
|
|
it('parses representative agent_stream tool_call event', () => {
|
|
const parsed = AgentStreamMessageSchema.parse({
|
|
type: 'agent_stream',
|
|
payload: {
|
|
agentId: 'agent_live',
|
|
timestamp: '2026-02-08T20:10:00.000Z',
|
|
event: {
|
|
type: 'timeline',
|
|
provider: 'claude',
|
|
item: {
|
|
type: 'tool_call',
|
|
callId: 'call_live',
|
|
name: 'shell',
|
|
status: 'running',
|
|
detail: {
|
|
type: 'shell',
|
|
command: 'ls',
|
|
},
|
|
error: null,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
expect(parsed.payload.event.type).toBe('timeline')
|
|
if (parsed.payload.event.type === 'timeline') {
|
|
expect(parsed.payload.event.item.type).toBe('tool_call')
|
|
if (parsed.payload.event.item.type === 'tool_call') {
|
|
expect(parsed.payload.event.item.status).toBe('running')
|
|
}
|
|
}
|
|
})
|
|
|
|
it('parses representative sub_agent tool_call event', () => {
|
|
const parsed = AgentStreamMessageSchema.parse({
|
|
type: 'agent_stream',
|
|
payload: {
|
|
agentId: 'agent_live',
|
|
timestamp: '2026-02-08T20:10:00.000Z',
|
|
event: {
|
|
type: 'timeline',
|
|
provider: 'claude',
|
|
item: {
|
|
type: 'tool_call',
|
|
callId: 'call_sub_agent_live',
|
|
name: 'Task',
|
|
status: 'running',
|
|
detail: {
|
|
type: 'sub_agent',
|
|
subAgentType: 'Explore',
|
|
description: 'Inspect repository structure',
|
|
log: '[Read] README.md',
|
|
actions: [
|
|
{
|
|
index: 1,
|
|
toolName: 'Read',
|
|
summary: 'README.md',
|
|
},
|
|
],
|
|
},
|
|
error: null,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
expect(parsed.payload.event.type).toBe('timeline')
|
|
if (parsed.payload.event.type === 'timeline') {
|
|
expect(parsed.payload.event.item.type).toBe('tool_call')
|
|
if (parsed.payload.event.item.type === 'tool_call') {
|
|
expect(parsed.payload.event.item.detail.type).toBe('sub_agent')
|
|
}
|
|
}
|
|
})
|
|
|
|
it('rejects removed initialize_agent_request inbound payload', () => {
|
|
const parsed = SessionInboundMessageSchema.safeParse({
|
|
type: 'initialize_agent_request',
|
|
agentId: 'agent-legacy',
|
|
requestId: 'req-legacy-1',
|
|
})
|
|
expect(parsed.success).toBe(false)
|
|
})
|
|
|
|
it('rejects removed initialize_agent_request outbound payload', () => {
|
|
const parsed = SessionOutboundMessageSchema.safeParse({
|
|
type: 'initialize_agent_request',
|
|
payload: {
|
|
requestId: 'req-legacy-1',
|
|
agentId: 'agent-legacy',
|
|
agentStatus: 'running',
|
|
timelineSize: 12,
|
|
},
|
|
})
|
|
expect(parsed.success).toBe(false)
|
|
})
|
|
|
|
it('parses directory suggestions request and response payloads', () => {
|
|
const requestParsed = SessionInboundMessageSchema.safeParse({
|
|
type: 'directory_suggestions_request',
|
|
query: 'proj',
|
|
cwd: '/tmp/project',
|
|
includeFiles: true,
|
|
includeDirectories: true,
|
|
limit: 20,
|
|
requestId: 'req-dir-1',
|
|
})
|
|
expect(requestParsed.success).toBe(true)
|
|
|
|
const responseParsed = SessionOutboundMessageSchema.safeParse({
|
|
type: 'directory_suggestions_response',
|
|
payload: {
|
|
directories: ['/Users/test/projects/paseo'],
|
|
entries: [{ path: '/Users/test/projects/paseo', kind: 'directory' }],
|
|
error: null,
|
|
requestId: 'req-dir-1',
|
|
},
|
|
})
|
|
expect(responseParsed.success).toBe(true)
|
|
})
|
|
|
|
it('rejects websocket envelope for removed agent_stream_snapshot message type', () => {
|
|
const fixture = {
|
|
type: 'agent_stream_snapshot',
|
|
payload: {
|
|
agentId: 'agent-legacy',
|
|
events: [],
|
|
},
|
|
}
|
|
const wrapped = WSOutboundMessageSchema.safeParse({
|
|
type: 'session',
|
|
message: fixture,
|
|
})
|
|
expect(wrapped.success).toBe(false)
|
|
})
|
|
|
|
it('rejects removed legacy git diff request messages', () => {
|
|
const gitDiffParsed = SessionInboundMessageSchema.safeParse({
|
|
type: 'git_diff_request',
|
|
agentId: 'agent-1',
|
|
requestId: 'req-1',
|
|
})
|
|
expect(gitDiffParsed.success).toBe(false)
|
|
|
|
const highlightedParsed = SessionInboundMessageSchema.safeParse({
|
|
type: 'highlighted_diff_request',
|
|
agentId: 'agent-1',
|
|
requestId: 'req-2',
|
|
})
|
|
expect(highlightedParsed.success).toBe(false)
|
|
})
|
|
|
|
it('rejects removed legacy git diff response messages', () => {
|
|
const gitDiffParsed = SessionOutboundMessageSchema.safeParse({
|
|
type: 'git_diff_response',
|
|
payload: {
|
|
agentId: 'agent-1',
|
|
diff: '',
|
|
error: null,
|
|
requestId: 'req-1',
|
|
},
|
|
})
|
|
expect(gitDiffParsed.success).toBe(false)
|
|
|
|
const highlightedParsed = SessionOutboundMessageSchema.safeParse({
|
|
type: 'highlighted_diff_response',
|
|
payload: {
|
|
agentId: 'agent-1',
|
|
files: [],
|
|
error: null,
|
|
requestId: 'req-2',
|
|
},
|
|
})
|
|
expect(highlightedParsed.success).toBe(false)
|
|
})
|
|
})
|