mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-15 12:55:51 +00:00
Tighten the UDS auth, framing, and response-reader boundaries while keeping the AgentSummary lifecycle covered so Codecov and CI fail on real regressions instead of missing coverage. The poorMode settings mock mirrors unrelated real settings defaults to avoid Bun mock retention changing later permission tests. Constraint: PR #369 must fix Codecov/CI precisely without warning suppression, fallback masking, or mock pollution Rejected: Delete AgentSummary lifecycle coverage | would hide Codecov loss and stale-summary behavior Rejected: Store inline UDS rejection in a hidden input sentinel | cloned observable inputs can drop it and bypass rejection Rejected: Ignore malformed UDS frames until timeout | leaves client slots and SendMessage calls open to exhaustion Confidence: high Scope-risk: moderate Directive: Keep empty #token= markers rejected; do not require a non-empty token value in hasInlineUdsToken Tested: bun test packages/builtin-tools/src/tools/SendMessageTool/__tests__/udsRecipientSanitization.test.ts src/utils/__tests__/udsMessaging.test.ts src/utils/__tests__/udsResponseReader.test.ts src/utils/__tests__/ndjsonFramer.test.ts Tested: bunx tsc --noEmit --pretty false Tested: bun run lint Tested: bun test --coverage --coverage-reporter lcov --coverage-dir coverage Tested: bun run test:all Tested: bun audit Tested: bun run build Tested: bun run build:vite Not-tested: GitHub-hosted Codecov upload until pushed PR checks rerun
213 lines
6.0 KiB
TypeScript
213 lines
6.0 KiB
TypeScript
import { beforeEach, describe, expect, test } from 'bun:test'
|
|
import { asAgentId } from '../../../types/ids.js'
|
|
import type { Message } from '../../../types/message.js'
|
|
import type {
|
|
CacheSafeParams,
|
|
ForkedAgentResult,
|
|
} from '../../../utils/forkedAgent.js'
|
|
import {
|
|
type AgentSummaryDependencies,
|
|
startAgentSummarization,
|
|
} from '../agentSummary.js'
|
|
|
|
const transcriptMessages = [
|
|
{ type: 'user', message: { content: 'start' }, uuid: 'u1' },
|
|
{
|
|
type: 'assistant',
|
|
message: { content: [{ type: 'text', text: 'working' }] },
|
|
uuid: 'a1',
|
|
},
|
|
{ type: 'user', message: { content: 'continue' }, uuid: 'u2' },
|
|
] as unknown as Message[]
|
|
|
|
type ForkCall = {
|
|
cacheSafeParams: CacheSafeParams
|
|
}
|
|
|
|
describe('startAgentSummarization', () => {
|
|
let scheduled: (() => void | Promise<void>) | undefined
|
|
let handle: { stop: () => void } | undefined
|
|
let forkCalls: ForkCall[]
|
|
let updateCalls: Array<{ taskId: string; summary: string }>
|
|
let transcriptMessagesForTest: Message[]
|
|
let debugLogs: string[]
|
|
let loggedErrors: Error[]
|
|
let clearedHandles: unknown[]
|
|
|
|
function startTestSummarization(
|
|
dependencies: AgentSummaryDependencies = {},
|
|
): { stop: () => void } {
|
|
return startAgentSummarization(
|
|
'task-1',
|
|
asAgentId('a0000000000000000'),
|
|
{
|
|
forkContextMessages: [
|
|
{ type: 'user', message: { content: 'stale' }, uuid: 'old' },
|
|
],
|
|
model: 'claude-test',
|
|
} as unknown as CacheSafeParams,
|
|
() => undefined,
|
|
{
|
|
clearTimeout: ((timeoutId: unknown) => {
|
|
clearedHandles.push(timeoutId)
|
|
}) as typeof clearTimeout,
|
|
getAgentTranscript: async () => ({
|
|
messages: transcriptMessagesForTest,
|
|
contentReplacements: [],
|
|
}),
|
|
isPoorModeActive: () => false,
|
|
logError: error => {
|
|
loggedErrors.push(
|
|
error instanceof Error ? error : new Error(String(error)),
|
|
)
|
|
},
|
|
logForDebugging: message => {
|
|
debugLogs.push(message)
|
|
},
|
|
runForkedAgent: async (args: ForkCall) => {
|
|
forkCalls.push(args)
|
|
return {
|
|
messages: [
|
|
{
|
|
type: 'assistant',
|
|
message: {
|
|
content: [{ type: 'text', text: 'Reading udsClient.ts' }],
|
|
},
|
|
},
|
|
],
|
|
} as unknown as ForkedAgentResult
|
|
},
|
|
setTimeout: ((callback: TimerHandler) => {
|
|
if (typeof callback !== 'function') {
|
|
throw new Error('Expected timer callback')
|
|
}
|
|
scheduled = callback as () => void | Promise<void>
|
|
return 1 as unknown as ReturnType<typeof setTimeout>
|
|
}) as unknown as typeof setTimeout,
|
|
updateAgentSummary: (taskId: string, summary: string) => {
|
|
updateCalls.push({ taskId, summary })
|
|
},
|
|
...dependencies,
|
|
},
|
|
)
|
|
}
|
|
|
|
beforeEach(() => {
|
|
forkCalls = []
|
|
updateCalls = []
|
|
scheduled = undefined
|
|
handle = undefined
|
|
transcriptMessagesForTest = transcriptMessages
|
|
debugLogs = []
|
|
loggedErrors = []
|
|
clearedHandles = []
|
|
})
|
|
|
|
test('summarizes bounded transcript once and skips unchanged fingerprints', async () => {
|
|
handle = startTestSummarization()
|
|
|
|
expect(typeof scheduled).toBe('function')
|
|
await scheduled!()
|
|
|
|
expect(forkCalls).toHaveLength(1)
|
|
expect(updateCalls).toEqual([
|
|
{ taskId: 'task-1', summary: 'Reading udsClient.ts' },
|
|
])
|
|
|
|
const forkContext = forkCalls[0].cacheSafeParams.forkContextMessages ?? []
|
|
expect(forkContext.map(message => String(message.uuid))).toEqual([
|
|
'u1',
|
|
'a1',
|
|
'u2',
|
|
])
|
|
expect(forkContext.some(message => String(message.uuid) === 'old')).toBe(
|
|
false,
|
|
)
|
|
|
|
await scheduled!()
|
|
|
|
expect(forkCalls).toHaveLength(1)
|
|
expect(updateCalls).toHaveLength(1)
|
|
})
|
|
|
|
test('skips summarization when filtering leaves too little bounded context', async () => {
|
|
transcriptMessagesForTest = [
|
|
{ type: 'user', message: { content: 'start' }, uuid: 'u1' },
|
|
{
|
|
type: 'assistant',
|
|
uuid: 'a1',
|
|
message: {
|
|
content: [{ type: 'tool_use', id: 'missing', name: 'Read' }],
|
|
},
|
|
},
|
|
{ type: 'user', message: { content: 'continue' }, uuid: 'u2' },
|
|
] as unknown as Message[]
|
|
|
|
handle = startTestSummarization()
|
|
|
|
expect(typeof scheduled).toBe('function')
|
|
await scheduled!()
|
|
|
|
expect(forkCalls).toEqual([])
|
|
expect(updateCalls).toEqual([])
|
|
expect(debugLogs).toContain(
|
|
'[AgentSummary] Skipping summary for task-1: no bounded context available',
|
|
)
|
|
})
|
|
|
|
test('skips summarization before building context when transcript is too short', async () => {
|
|
transcriptMessagesForTest = transcriptMessages.slice(0, 2)
|
|
handle = startTestSummarization()
|
|
|
|
expect(typeof scheduled).toBe('function')
|
|
await scheduled!()
|
|
|
|
expect(forkCalls).toEqual([])
|
|
expect(updateCalls).toEqual([])
|
|
expect(debugLogs).toContain(
|
|
'[AgentSummary] Skipping summary for task-1: not enough messages (2)',
|
|
)
|
|
})
|
|
|
|
test('skips and reschedules while poor mode is active', async () => {
|
|
handle = startTestSummarization({
|
|
isPoorModeActive: () => true,
|
|
})
|
|
|
|
expect(typeof scheduled).toBe('function')
|
|
await scheduled!()
|
|
|
|
expect(forkCalls).toEqual([])
|
|
expect(updateCalls).toEqual([])
|
|
expect(debugLogs).toContain(
|
|
'[AgentSummary] Skipping summary — poor mode active',
|
|
)
|
|
})
|
|
|
|
test('logs summary errors and keeps the next timer owned by the summarizer', async () => {
|
|
const error = new Error('fork failed')
|
|
handle = startTestSummarization({
|
|
runForkedAgent: async () => {
|
|
throw error
|
|
},
|
|
})
|
|
|
|
expect(typeof scheduled).toBe('function')
|
|
await scheduled!()
|
|
|
|
expect(loggedErrors).toEqual([error])
|
|
expect(updateCalls).toEqual([])
|
|
})
|
|
|
|
test('stop clears the pending summary timer', () => {
|
|
handle = startTestSummarization()
|
|
|
|
handle.stop()
|
|
|
|
expect(debugLogs).toContain(
|
|
'[AgentSummary] Stopping summarization for task-1',
|
|
)
|
|
expect(clearedHandles).toEqual([1])
|
|
})
|
|
})
|