Files
NetMesh/infrastructure/ai/harness/agentEventAdapter.test.ts
zhaolei 3c72efcb7f
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

126 lines
4.7 KiB
TypeScript

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import {
isStepHandleNoticeMessage,
mapCattyStreamChunkToAgentEvents,
mapSdkStreamEventToAgentEvents,
} from './agentEventAdapter';
describe('agentEventAdapter', () => {
it('maps tool-output-denied chunks to approval_resolved denied and tool_result', () => {
const events = mapCattyStreamChunkToAgentEvents(
{
type: 'tool-output-denied',
toolCallId: 'call-1',
toolName: 'sftp_write_file',
},
{ sessionId: 'chat-1', turnId: 'turn-1' },
);
assert.equal(events.length, 2);
assert.equal(events[0]?.type, 'approval_resolved');
assert.equal((events[0] as { outcome?: string }).outcome, 'denied');
assert.equal(events[1]?.type, 'tool_result');
assert.equal((events[1] as { isError?: boolean }).isError, true);
});
it('maps tool-error chunks to tool_result with isError', () => {
const events = mapCattyStreamChunkToAgentEvents(
{
type: 'tool-error',
toolCallId: 'call-2',
toolName: 'terminal_execute',
error: new Error('timeout'),
},
{ sessionId: 'chat-1', turnId: 'turn-1' },
);
assert.equal(events.length, 1);
assert.equal(events[0]?.type, 'tool_result');
assert.equal((events[0] as { isError?: boolean }).isError, true);
assert.match(String((events[0] as { result?: string }).result), /timeout/);
});
it('maps denied tool-approval-response with nested toolCall to tool_result', () => {
const events = mapCattyStreamChunkToAgentEvents(
{
type: 'tool-approval-response',
approvalId: 'approval-1',
approved: false,
reason: 'Observer mode blocks write operations.',
toolCall: {
toolCallId: 'call-3',
toolName: 'sftp_write_file',
input: { path: '/tmp/x' },
},
},
{ sessionId: 'chat-1', turnId: 'turn-1' },
);
assert.equal(events.length, 2);
assert.equal(events[0]?.type, 'approval_resolved');
assert.equal((events[0] as { outcome?: string }).outcome, 'denied');
assert.equal(events[1]?.type, 'tool_result');
assert.match(String((events[1] as { result?: string }).result), /Observer mode/);
});
it('detects step handle notice messages for prepareStep dedup', () => {
assert.equal(
isStepHandleNoticeMessage('[step 2] Tool output handles available: tool-output-abc'),
true,
);
assert.equal(isStepHandleNoticeMessage('regular user message'), false);
});
it('maps SDK activity events into the unified trace protocol', () => {
const context = { sessionId: 'chat-1', turnId: 'turn-1' };
const fileChange = mapSdkStreamEventToAgentEvents({
type: 'file-change',
itemId: 'patch-1',
status: 'completed',
changes: [{ path: 'src/app.ts', kind: 'update' }],
}, context);
const webSearch = mapSdkStreamEventToAgentEvents({
type: 'web-search', itemId: 'search-1', query: 'Codex events', status: 'running',
}, context);
const plan = mapSdkStreamEventToAgentEvents({
type: 'plan-update', itemId: 'plan-1', status: 'completed',
items: [{ text: 'Map events', completed: true }],
}, context);
const warning = mapSdkStreamEventToAgentEvents({
type: 'warning', itemId: 'warning-1', message: 'recoverable',
}, context);
assert.equal(fileChange[0]?.type, 'file_change');
assert.equal(webSearch[0]?.type, 'web_search');
assert.equal(plan[0]?.type, 'plan_update');
assert.equal(warning[0]?.type, 'error');
assert.equal((warning[0] as { recoverable?: boolean }).recoverable, true);
});
it('maps actual SDK usage including cached and reasoning tokens', () => {
const events = mapSdkStreamEventToAgentEvents({
type: 'usage',
inputTokens: 100,
cachedInputTokens: 40,
outputTokens: 25,
reasoningTokens: 10,
totalTokens: 125,
}, { sessionId: 'chat-1', turnId: 'turn-1' });
assert.deepEqual(events[0] && {
type: events[0].type,
promptTokens: 'promptTokens' in events[0] ? events[0].promptTokens : undefined,
cachedPromptTokens: 'cachedPromptTokens' in events[0] ? events[0].cachedPromptTokens : undefined,
completionTokens: 'completionTokens' in events[0] ? events[0].completionTokens : undefined,
reasoningTokens: 'reasoningTokens' in events[0] ? events[0].reasoningTokens : undefined,
totalTokens: 'totalTokens' in events[0] ? events[0].totalTokens : undefined,
estimated: 'estimated' in events[0] ? events[0].estimated : undefined,
}, {
type: 'usage',
promptTokens: 100,
cachedPromptTokens: 40,
completionTokens: 25,
reasoningTokens: 10,
totalTokens: 125,
estimated: false,
});
});
});