Download packages/kap-server/test/protocolMessages.test.ts from SaylorTwift/kimi-code: direct link, hf CLI and curl.
- Browser
- Download file 19 kB
-
https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/kap-server/test/protocolMessages.test.ts
- Command line
-
hf download hf://SaylorTwift/kimi-code/packages/kap-server/test/protocolMessages.test.ts
-
curl -L -o protocolMessages.test.ts https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/kap-server/test/protocolMessages.test.ts
19 kB
| import { describe, expect, it } from 'vitest'; | |
| import { | |
| ContractViolation, | |
| ackMessageSchema, | |
| agentStateMessageSchema, | |
| assistantDeltaMessageSchema, | |
| assistantMessageSchema, | |
| capabilityMessageSchema, | |
| clientMessageSchema, | |
| configMessageSchema, | |
| configWarningMessageSchema, | |
| entityId, | |
| entityKey, | |
| errorMessageSchema, | |
| helloMessageSchema, | |
| historyQuerySchema, | |
| historyResponseSchema, | |
| interactionMessageSchema, | |
| modelCatalogMessageSchema, | |
| parseServerMessage, | |
| pluginMessageSchema, | |
| serverMessageSchema, | |
| sessionMessageSchema, | |
| sessionStateMessageSchema, | |
| stepMessageSchema, | |
| subscribeMessageSchema, | |
| systemMessageSchema, | |
| taskMessageSchema, | |
| thinkingDeltaMessageSchema, | |
| thinkingMessageSchema, | |
| todoMessageSchema, | |
| toolCallDeltaMessageSchema, | |
| toolCallMessageSchema, | |
| toolProgressMessageSchema, | |
| turnMessageSchema, | |
| unsubscribeMessageSchema, | |
| userMessageSchema, | |
| workspaceMessageSchema, | |
| type CapabilityChangedMessage, | |
| type ConfigWarningMessage, | |
| type DeltaMessage, | |
| type ModelCatalogChangedMessage, | |
| type PluginChangedMessage, | |
| } from '../src/protocol/messages'; | |
| const TS = '2026-09-04T08:00:00.000Z'; | |
| const TS_MS = 1_756_963_200_000; | |
| const timeline = { | |
| session_id: 'sess_1', | |
| agent_id: 'agent_1', | |
| timestamp: TS_MS, | |
| }; | |
| const sessionScope = { | |
| session_id: 'sess_1', | |
| timestamp: TS_MS, | |
| }; | |
| const globalScope = { | |
| timestamp: TS_MS, | |
| }; | |
| const sessionInfo = { | |
| id: 'sess_1', | |
| workspace_id: 'wd_example_0123456789ab', | |
| title: 'demo', | |
| created_at: TS, | |
| updated_at: TS, | |
| busy: false, | |
| metadata: { cwd: '/repo' }, | |
| agent_config: { model: 'kimi-k2' }, | |
| usage: { | |
| input_tokens: 0, | |
| output_tokens: 0, | |
| cache_read_tokens: 0, | |
| cache_creation_tokens: 0, | |
| context_tokens: 0, | |
| }, | |
| permission_rules: [], | |
| message_count: 0, | |
| last_seq: 0, | |
| }; | |
| const turn = { | |
| type: 'turn', | |
| ...timeline, | |
| turn_id: 't1', | |
| ordinal: 0, | |
| status: 'running', | |
| origin: { kind: 'user' }, | |
| }; | |
| const step = { | |
| type: 'step', | |
| ...timeline, | |
| step_id: 't1.0', | |
| turn_id: 't1', | |
| ordinal: 0, | |
| status: 'running', | |
| }; | |
| const user = { | |
| type: 'user', | |
| session_id: 'sess_1', | |
| agent_id: 'agent_1', | |
| message_id: 't1.u0', | |
| turn_id: 't1', | |
| status: 'read', | |
| timestamp: TS_MS, | |
| text: [{ type: 'text', text: 'hello', meta: {} }], | |
| }; | |
| const assistant = { | |
| type: 'assistant', | |
| ...timeline, | |
| message_id: 't1.0.a0', | |
| turn_id: 't1', | |
| step_id: 't1.0', | |
| status: 'streaming', | |
| text: 'partial', | |
| }; | |
| const assistantDelta = { | |
| type: 'assistant.delta', | |
| ...timeline, | |
| message_id: 't1.0.a0', | |
| text: ' chunk', | |
| }; | |
| const thinking = { | |
| type: 'thinking', | |
| ...timeline, | |
| message_id: 't1.0.a1', | |
| turn_id: 't1', | |
| step_id: 't1.0', | |
| status: 'completed', | |
| text: 'reasoning', | |
| }; | |
| const thinkingDelta = { | |
| type: 'thinking.delta', | |
| ...timeline, | |
| message_id: 't1.0.a1', | |
| text: ' bit', | |
| }; | |
| const toolCall = { | |
| type: 'tool_call', | |
| ...timeline, | |
| tool_call_id: 'tc1', | |
| turn_id: 't1', | |
| step_id: 't1.0', | |
| name: 'Bash', | |
| status: 'running', | |
| }; | |
| const toolCallDelta = { | |
| type: 'tool_call.delta', | |
| ...timeline, | |
| tool_call_id: 'tc1', | |
| input_text: '{"command":"ls', | |
| }; | |
| const toolProgress = { | |
| type: 'tool.progress', | |
| ...timeline, | |
| tool_call_id: 'tc1', | |
| progress: { kind: 'stdout', text: 'line' }, | |
| }; | |
| const systemUndo = { | |
| type: 'system', | |
| ...timeline, | |
| system_id: 'sys1', | |
| subtype: 'undo', | |
| payload: { removed_ids: ['t1'] }, | |
| }; | |
| const systemNotice = { | |
| type: 'system', | |
| ...timeline, | |
| system_id: 'sys2', | |
| subtype: 'notice', | |
| payload: { text: 'heads up' }, | |
| }; | |
| const interactionApproval = { | |
| type: 'interaction', | |
| ...timeline, | |
| interaction_id: 'ia1', | |
| kind: 'approval', | |
| status: 'pending', | |
| tool_call_id: 'tc1', | |
| request: { tool_name: 'Bash', action: 'run command', tool_input_display: { command: 'ls' } }, | |
| }; | |
| const interactionQuestion = { | |
| type: 'interaction', | |
| ...timeline, | |
| interaction_id: 'ia2', | |
| kind: 'question', | |
| status: 'pending', | |
| request: { | |
| questions: [ | |
| { | |
| id: 'q_0', | |
| question: 'pick one', | |
| options: [ | |
| { id: 'opt_0_0', label: 'a' }, | |
| { id: 'opt_0_1', label: 'b' }, | |
| { id: 'opt_0_2', label: 'c' }, | |
| { id: 'opt_0_3', label: 'd' }, | |
| { id: 'opt_0_4', label: 'e' }, | |
| ], | |
| }, | |
| ], | |
| }, | |
| }; | |
| const task = { | |
| type: 'task', | |
| ...timeline, | |
| task_id: 'task1', | |
| kind: 'shell', | |
| status: 'running', | |
| detached: true, | |
| output_tail: 'tail', | |
| }; | |
| const todo = { | |
| type: 'todo', | |
| ...timeline, | |
| todo_id: 'todo1', | |
| items: [{ title: 'write tests', status: 'in_progress' }], | |
| }; | |
| const agentState = { | |
| type: 'agent.state', | |
| session_id: 'sess_1', | |
| agent_id: 'agent-1', | |
| profile: { kind: 'coder' }, | |
| timestamp: TS_MS, | |
| origin: { kind: 'tool-agent', tool_call_id: 'call_1', parent_agent_id: 'main' }, | |
| created_at: TS, | |
| status: 'running', | |
| turn: { status: 'acting' }, | |
| }; | |
| const sessionState = { | |
| type: 'session.state', | |
| ...sessionScope, | |
| status: 'running', | |
| pending_interaction: 'approval', | |
| }; | |
| const session = { | |
| type: 'session', | |
| ...globalScope, | |
| subtype: 'created', | |
| session: sessionInfo, | |
| }; | |
| const workspace = { | |
| type: 'workspace', | |
| ...globalScope, | |
| subtype: 'updated', | |
| workspace: { | |
| id: 'wd_example_0123456789ab', | |
| root: '/repo', | |
| name: 'repo', | |
| created_at: TS, | |
| last_opened_at: TS, | |
| session_count: 1, | |
| }, | |
| }; | |
| const config = { | |
| type: 'config', | |
| ...globalScope, | |
| config: { model: 'kimi-k2' }, | |
| }; | |
| const configWarning = { | |
| type: 'config.warning', | |
| ...globalScope, | |
| warnings: ['deprecated key'], | |
| }; | |
| const modelCatalog = { | |
| type: 'model_catalog', | |
| ...globalScope, | |
| }; | |
| const plugin = { | |
| type: 'plugin', | |
| ...globalScope, | |
| }; | |
| const capability = { | |
| type: 'capability', | |
| ...globalScope, | |
| capability_id: 'cap1', | |
| }; | |
| const hello = { | |
| type: 'hello', | |
| protocol_version: '3', | |
| server_id: 'srv1', | |
| capabilities: ['step_replay_v1'], | |
| }; | |
| const ack = { | |
| type: 'ack', | |
| id: 1, | |
| code: 0, | |
| }; | |
| const error = { | |
| type: 'error', | |
| code: 40001, | |
| msg: 'validation failed', | |
| }; | |
| const subscribe = { | |
| type: 'subscribe', | |
| id: 1, | |
| session_id: 'sess_1', | |
| }; | |
| const unsubscribe = { | |
| type: 'unsubscribe', | |
| id: 2, | |
| session_id: 'sess_1', | |
| }; | |
| const serverCases = [ | |
| ['turn', turnMessageSchema, turn], | |
| ['step', stepMessageSchema, step], | |
| ['user', userMessageSchema, user], | |
| ['assistant', assistantMessageSchema, assistant], | |
| ['assistant.delta', assistantDeltaMessageSchema, assistantDelta], | |
| ['thinking', thinkingMessageSchema, thinking], | |
| ['thinking.delta', thinkingDeltaMessageSchema, thinkingDelta], | |
| ['tool_call', toolCallMessageSchema, toolCall], | |
| ['tool_call.delta', toolCallDeltaMessageSchema, toolCallDelta], | |
| ['tool.progress', toolProgressMessageSchema, toolProgress], | |
| ['system(undo)', systemMessageSchema, systemUndo], | |
| ['system(notice)', systemMessageSchema, systemNotice], | |
| ['interaction(approval)', interactionMessageSchema, interactionApproval], | |
| ['interaction(question)', interactionMessageSchema, interactionQuestion], | |
| ['task', taskMessageSchema, task], | |
| ['todo', todoMessageSchema, todo], | |
| ['agent.state', agentStateMessageSchema, agentState], | |
| ['session.state', sessionStateMessageSchema, sessionState], | |
| ['session', sessionMessageSchema, session], | |
| ['workspace', workspaceMessageSchema, workspace], | |
| ['config', configMessageSchema, config], | |
| ['config.warning', configWarningMessageSchema, configWarning], | |
| ['model_catalog', modelCatalogMessageSchema, modelCatalog], | |
| ['plugin', pluginMessageSchema, plugin], | |
| ['capability', capabilityMessageSchema, capability], | |
| ['hello', helloMessageSchema, hello], | |
| ['ack', ackMessageSchema, ack], | |
| ['error', errorMessageSchema, error], | |
| ] as const; | |
| const serverNegativeCases = [ | |
| ['turn missing origin', turnMessageSchema, { ...turn, origin: undefined }], | |
| ['step bad status', stepMessageSchema, { ...step, status: 'cancelled' }], | |
| ['user bad status', userMessageSchema, { ...user, status: 'running' }], | |
| ['assistant missing text', assistantMessageSchema, { ...assistant, text: undefined }], | |
| ['assistant.delta missing text', assistantDeltaMessageSchema, { ...assistantDelta, text: undefined }], | |
| ['thinking bad status', thinkingMessageSchema, { ...thinking, status: 'done' }], | |
| ['thinking.delta missing message_id', thinkingDeltaMessageSchema, { ...thinkingDelta, message_id: undefined }], | |
| ['tool_call missing name', toolCallMessageSchema, { ...toolCall, name: undefined }], | |
| ['tool_call.delta missing input_text', toolCallDeltaMessageSchema, { ...toolCallDelta, input_text: undefined }], | |
| ['tool.progress bad kind', toolProgressMessageSchema, { ...toolProgress, progress: { kind: 'unknown' } }], | |
| ['system undo missing payload', systemMessageSchema, { ...systemUndo, payload: undefined }], | |
| ['interaction wrong kind', interactionMessageSchema, { ...interactionApproval, kind: 'command' }], | |
| ['task missing output_tail', taskMessageSchema, { ...task, output_tail: undefined }], | |
| ['todo bad item status', todoMessageSchema, { ...todo, items: [{ title: 'x', status: 'doing' }] }], | |
| ['agent.state bad status', agentStateMessageSchema, { ...agentState, status: 'paused' }], | |
| ['session.state bad status', sessionStateMessageSchema, { ...sessionState, status: 'busy' }], | |
| ['session bad subtype', sessionMessageSchema, { ...session, subtype: 'renamed' }], | |
| ['workspace bad id', workspaceMessageSchema, { ...workspace, workspace: { ...workspace.workspace, id: 'ws_1' } }], | |
| ['config missing config', configMessageSchema, { ...config, config: undefined }], | |
| ['config.warning missing warnings', configWarningMessageSchema, { ...configWarning, warnings: undefined }], | |
| ['model_catalog missing timestamp', modelCatalogMessageSchema, { ...modelCatalog, timestamp: undefined }], | |
| ['plugin missing timestamp', pluginMessageSchema, { ...plugin, timestamp: undefined }], | |
| ['capability bad capability_id', capabilityMessageSchema, { ...capability, capability_id: 7 }], | |
| ['hello missing capabilities', helloMessageSchema, { ...hello, capabilities: undefined }], | |
| ['ack bad code', ackMessageSchema, { ...ack, code: '0' }], | |
| ['error missing msg', errorMessageSchema, { ...error, msg: undefined }], | |
| ] as const; | |
| describe('serverMessageSchema', () => { | |
| it.each(serverCases)('accepts %s', (_label, schema, message) => { | |
| expect(schema.safeParse(message).success).toBe(true); | |
| const parsed = serverMessageSchema.safeParse(message); | |
| expect(parsed.success).toBe(true); | |
| if (parsed.success) { | |
| expect(parsed.data.type).toBe(message.type); | |
| } | |
| }); | |
| it.each(serverNegativeCases)('rejects %s', (_label, schema, message) => { | |
| expect(schema.safeParse(message).success).toBe(false); | |
| expect(serverMessageSchema.safeParse(message).success).toBe(false); | |
| }); | |
| it('rejects unknown message types', () => { | |
| expect(serverMessageSchema.safeParse({ type: 'future.message', ...globalScope }).success).toBe(false); | |
| }); | |
| it('rejects messages without a type discriminator', () => { | |
| expect(serverMessageSchema.safeParse({ ...globalScope }).success).toBe(false); | |
| }); | |
| it('tolerates unknown fields for open evolution', () => { | |
| const evolved = { | |
| ...turn, | |
| future_field: 1, | |
| origin: { kind: 'user', future_flag: true }, | |
| usage: { input_tokens: 3, future_counter: 9 }, | |
| }; | |
| const parsed = serverMessageSchema.safeParse(evolved); | |
| expect(parsed.success).toBe(true); | |
| if (parsed.success) { | |
| expect('future_field' in parsed.data).toBe(false); | |
| expect('future_counter' in (parsed.data as { usage?: object }).usage!).toBe(false); | |
| } | |
| }); | |
| }); | |
| describe('clientMessageSchema', () => { | |
| it('accepts subscribe and unsubscribe', () => { | |
| expect(clientMessageSchema.safeParse(subscribe).success).toBe(true); | |
| expect(clientMessageSchema.safeParse(unsubscribe).success).toBe(true); | |
| expect(subscribeMessageSchema.safeParse({ ...subscribe, agent_ids: ['a1'], omit: ['assistant.delta'] }).success).toBe(true); | |
| }); | |
| it('rejects entity and server-only control messages', () => { | |
| expect(clientMessageSchema.safeParse(turn).success).toBe(false); | |
| expect(clientMessageSchema.safeParse(hello).success).toBe(false); | |
| expect(clientMessageSchema.safeParse(ack).success).toBe(false); | |
| }); | |
| it('rejects malformed subscriptions', () => { | |
| expect(subscribeMessageSchema.safeParse({ ...subscribe, id: '1' }).success).toBe(false); | |
| expect(subscribeMessageSchema.safeParse({ ...subscribe, session_id: undefined }).success).toBe(false); | |
| expect(unsubscribeMessageSchema.safeParse({ ...unsubscribe, session_id: '' }).success).toBe(false); | |
| }); | |
| }); | |
| describe('systemMessageSchema payload discrimination', () => { | |
| it('requires removed_ids for undo and clear', () => { | |
| expect(systemMessageSchema.safeParse(systemUndo).success).toBe(true); | |
| expect(systemMessageSchema.safeParse({ ...systemUndo, subtype: 'clear' }).success).toBe(true); | |
| expect(systemMessageSchema.safeParse({ ...systemUndo, payload: {} }).success).toBe(false); | |
| expect(systemMessageSchema.safeParse({ ...systemUndo, payload: { removed_ids: 't1' } }).success).toBe(false); | |
| }); | |
| it('allows arbitrary payloads for open subtypes', () => { | |
| expect(systemMessageSchema.safeParse(systemNotice).success).toBe(true); | |
| expect(systemMessageSchema.safeParse({ ...systemNotice, payload: undefined }).success).toBe(true); | |
| expect(systemMessageSchema.safeParse({ ...systemNotice, subtype: 'interruption', payload: { reason: 'aborted' } }).success).toBe(true); | |
| }); | |
| it('rejects unknown subtypes', () => { | |
| expect(systemMessageSchema.safeParse({ ...systemNotice, subtype: 'explosion' }).success).toBe(false); | |
| }); | |
| }); | |
| describe('interactionMessageSchema kind discrimination', () => { | |
| it('binds request and response shapes to kind', () => { | |
| expect(interactionMessageSchema.safeParse(interactionApproval).success).toBe(true); | |
| expect(interactionMessageSchema.safeParse(interactionQuestion).success).toBe(true); | |
| expect( | |
| interactionMessageSchema.safeParse({ ...interactionApproval, request: interactionQuestion.request }).success, | |
| ).toBe(false); | |
| expect( | |
| interactionMessageSchema.safeParse({ ...interactionQuestion, request: interactionApproval.request }).success, | |
| ).toBe(false); | |
| expect( | |
| interactionMessageSchema.safeParse({ | |
| ...interactionQuestion, | |
| status: 'answered', | |
| response: { answers: { q_0: { kind: 'single', option_id: 'opt_0_0' } } }, | |
| }).success, | |
| ).toBe(true); | |
| expect( | |
| interactionMessageSchema.safeParse({ | |
| ...interactionApproval, | |
| status: 'approved', | |
| response: { answers: {} }, | |
| }).success, | |
| ).toBe(false); | |
| }); | |
| }); | |
| describe('historyResponseSchema', () => { | |
| it('accepts a timeline entity page with in_flight marker', () => { | |
| const page = { | |
| messages: [turn, step, user, assistant, thinking, toolCall, systemUndo, interactionApproval, task, todo], | |
| has_more: false, | |
| in_flight: { turn_id: 't2', step_id: 't2.0' }, | |
| }; | |
| expect(historyResponseSchema.safeParse(page).success).toBe(true); | |
| expect(historyResponseSchema.safeParse({ messages: [], has_more: true }).success).toBe(true); | |
| expect(historyResponseSchema.safeParse({ messages: [] }).success).toBe(false); | |
| }); | |
| it('rejects volatile and non-persisted entities in history', () => { | |
| expect(historyResponseSchema.safeParse({ messages: [assistantDelta] }).success).toBe(false); | |
| expect(historyResponseSchema.safeParse({ messages: [toolProgress] }).success).toBe(false); | |
| }); | |
| it('validates cursor query params', () => { | |
| expect(historyQuerySchema.safeParse({ before_turn: 't4', page_size: 50 }).success).toBe(true); | |
| expect(historyQuerySchema.safeParse({ after_step: 't4.2' }).success).toBe(true); | |
| expect(historyQuerySchema.safeParse({}).success).toBe(true); | |
| expect(historyQuerySchema.safeParse({ page_size: 0 }).success).toBe(false); | |
| expect(historyQuerySchema.safeParse({ page_size: 1.5 }).success).toBe(false); | |
| }); | |
| }); | |
| describe('timestamp contract', () => { | |
| it('requires epoch-millisecond timestamps on every message base', () => { | |
| expect(turnMessageSchema.safeParse({ ...turn, timestamp: TS }).success).toBe(false); | |
| expect(turnMessageSchema.safeParse({ ...turn, timestamp: TS_MS }).success).toBe(true); | |
| expect(assistantDeltaMessageSchema.safeParse({ ...assistantDelta, timestamp: TS }).success).toBe(false); | |
| expect(configMessageSchema.safeParse({ ...config, timestamp: TS }).success).toBe(false); | |
| expect(configMessageSchema.safeParse({ ...config, timestamp: TS_MS }).success).toBe(true); | |
| }); | |
| it('normalizes offset datetimes to UTC on field-level time fields', () => { | |
| const parsed = turnMessageSchema.safeParse({ ...turn, started_at: '2026-09-04T16:00:00+08:00' }); | |
| expect(parsed.success).toBe(true); | |
| if (parsed.success) { | |
| expect(parsed.data.started_at).toBe(TS); | |
| } | |
| }); | |
| }); | |
| describe('parseServerMessage + entityKey', () => { | |
| it('parses valid messages, throws detailed violations and derives replace-by-id keys', () => { | |
| expect(parseServerMessage(turn)).toMatchObject({ type: 'turn', turn_id: 't1' }); | |
| expect(() => parseServerMessage({ type: 'turn' })).toThrow(ContractViolation); | |
| try { | |
| parseServerMessage({ type: 'turn' }); | |
| expect.unreachable(); | |
| } catch (error) { | |
| const violation = error as ContractViolation; | |
| expect(violation.issues.length).toBeGreaterThan(0); | |
| expect(violation.raw).toEqual({ type: 'turn' }); | |
| expect(violation.message).toContain('contract violation'); | |
| } | |
| expect(entityId(assistant as never)).toBe('t1.0.a0'); | |
| expect(entityId(toolCall as never)).toBe('tc1'); | |
| expect(entityId(interactionApproval as never)).toBe('tc1'); | |
| expect(entityId(interactionQuestion as never)).toBe('ia2'); | |
| expect(entityId(task as never)).toBe('task1'); | |
| expect(entityId(todo as never)).toBe('todo1'); | |
| expect(entityId(systemUndo as never)).toBe('sys1'); | |
| expect(entityId(step as never)).toBe('t1.0'); | |
| expect(entityId(turn as never)).toBe('t1'); | |
| expect(entityKey(turn as never)).toBe('agent_1:turn:t1'); | |
| expect(entityKey(sessionState as never)).toBe(':session.state:'); | |
| expect(entityKey(config as never)).toBe(':config:'); | |
| }); | |
| it('exposes the delta union and client-facing type aliases', () => { | |
| const deltas: DeltaMessage[] = [assistantDelta, thinkingDelta, toolCallDelta, toolProgress].map( | |
| (m) => parseServerMessage(m) as DeltaMessage, | |
| ); | |
| expect(deltas.map((m) => m.type)).toEqual([ | |
| 'assistant.delta', | |
| 'thinking.delta', | |
| 'tool_call.delta', | |
| 'tool.progress', | |
| ]); | |
| const warning: ConfigWarningMessage = configWarningMessageSchema.parse(configWarning); | |
| const catalog: ModelCatalogChangedMessage = modelCatalogMessageSchema.parse(modelCatalog); | |
| const plug: PluginChangedMessage = pluginMessageSchema.parse(plugin); | |
| const cap: CapabilityChangedMessage = capabilityMessageSchema.parse(capability); | |
| expect([warning.type, catalog.type, plug.type, cap.type]).toEqual([ | |
| 'config.warning', | |
| 'model_catalog', | |
| 'plugin', | |
| 'capability', | |
| ]); | |
| }); | |
| }); | |