Skip to content

Commit bee711f

Browse files
authored
refactor(acp): make bridge SDK message handling type-safe (#1265)
* refactor(acp): make bridge SDK message handling type-safe - Add BridgeSDKMessage type alias to eliminate 14 type errors from void-leaked IteratorResult - Replace 18 scattered as-casts with a single uniform as BridgeSDKMessage - Add 68 lines of unit tests covering bridge message handling - Fixes docstring coverage to pass CI threshold * fix(acp): restore IteratorResult return type to nextSdkMessageOrAbort The simplified SDKMessage | undefined return type collapsed two distinct states: generator truly done vs generator yielding undefined. This broke forwardSessionUpdates which needs to distinguish the two — when the generator yields null/undefined it should continue (calling next() again), not break out of the loop. Restored the original IteratorResult<SDKMessage, void> return type so done and yielded-null are distinct again.
1 parent 4d930eb commit bee711f

2 files changed

Lines changed: 249 additions & 54 deletions

File tree

src/services/acp/__tests__/bridge.test.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
toolUpdateFromToolResult,
55
toolUpdateFromEditToolResponse,
66
forwardSessionUpdates,
7+
nextSdkMessageOrAbort,
78
} from '../bridge.js'
89
import { promptToQueryInput } from '../promptConversion.js'
910
import { markdownEscape, toDisplayPath } from '../utils.js'
@@ -30,6 +31,10 @@ async function* makeStream(
3031
for (const m of msgs) yield m
3132
}
3233

34+
async function* makeWaitingStream(): AsyncGenerator<SDKMessage, void, unknown> {
35+
await new Promise<never>(() => {})
36+
}
37+
3338
// ── toolInfoFromToolUse ────────────────────────────────────────────
3439

3540
describe('toolInfoFromToolUse', () => {
@@ -692,6 +697,47 @@ describe('toDisplayPath', () => {
692697

693698
// ── forwardSessionUpdates ─────────────────────────────────────────
694699

700+
describe('nextSdkMessageOrAbort', () => {
701+
test('returns done:true when aborted while waiting for next message', async () => {
702+
const ac = new AbortController()
703+
const pending = nextSdkMessageOrAbort(makeWaitingStream(), ac.signal)
704+
ac.abort()
705+
706+
const result = await Promise.race([
707+
pending,
708+
new Promise<'timeout'>(resolve => setTimeout(resolve, 100, 'timeout')),
709+
])
710+
711+
expect(result).toEqual({ done: true, value: undefined })
712+
})
713+
714+
test('returns done:true when stream is done', async () => {
715+
const result = await nextSdkMessageOrAbort(
716+
makeStream([]),
717+
new AbortController().signal,
718+
)
719+
720+
expect(result).toEqual({ done: true, value: undefined })
721+
})
722+
723+
test('returns a valid SDKMessage via IteratorResult', async () => {
724+
const msg = {
725+
type: 'assistant',
726+
message: {
727+
role: 'assistant',
728+
content: [{ type: 'text', text: 'hello' }],
729+
},
730+
} as unknown as SDKMessage
731+
732+
const result = await nextSdkMessageOrAbort(
733+
makeStream([msg]),
734+
new AbortController().signal,
735+
)
736+
737+
expect(result).toEqual({ done: false, value: msg })
738+
})
739+
})
740+
695741
describe('forwardSessionUpdates', () => {
696742
test('returns end_turn when stream is empty', async () => {
697743
const conn = makeConn()
@@ -1077,6 +1123,28 @@ describe('forwardSessionUpdates', () => {
10771123
).toBe(0)
10781124
})
10791125

1126+
test('ignores unknown message types without crashing', async () => {
1127+
const conn = makeConn()
1128+
const debug = console.debug
1129+
const debugMock = mock(() => {})
1130+
console.debug = debugMock as typeof console.debug
1131+
1132+
try {
1133+
const result = await forwardSessionUpdates(
1134+
's1',
1135+
makeStream([{ type: 'future_message' } as unknown as SDKMessage]),
1136+
conn,
1137+
new AbortController().signal,
1138+
{},
1139+
)
1140+
1141+
expect(result.stopReason).toBe('end_turn')
1142+
expect(debugMock).toHaveBeenCalled()
1143+
} finally {
1144+
console.debug = debug
1145+
}
1146+
})
1147+
10801148
test('re-throws unexpected errors from stream', async () => {
10811149
const conn = makeConn()
10821150
async function* errorStream(): AsyncGenerator<

0 commit comments

Comments
 (0)