fix: close peer socket listener handoff window

CodeRabbit and Claude review found that documenting caller-owned raw socket errors still left a Promise handoff window and a stale timeout-listener risk. The peer connection API now requires a caller error handler and installs it before resolving, while cleanup removes internal error and timeout listeners on every path.

Constraint: Keep the fix precise to PR #375 review feedback and avoid warning suppression or fallback behavior.
Rejected: Leave the behavior documented only | still permits an unhandled socket error window between resolve and caller listener attachment.
Rejected: Keep a no-op internal error listener | would silently swallow caller-owned socket errors.
Confidence: high
Scope-risk: narrow
Directive: Do not add raw connectToPeer callers without providing a real onSocketError handler and capability handshake.
Tested: bun test src/utils/__tests__/udsMessaging.test.ts src/services/AgentSummary/__tests__/agentSummary.test.ts
Tested: bunx tsc --noEmit --pretty false
Tested: bun run lint
Tested: bun run test:all
Tested: bun test --coverage --coverage-reporter lcov --coverage-dir coverage
Tested: bun run build
Tested: bun run build:vite
Tested: bun audit
Not-tested: Manual external ACP peer runtime beyond repository tests.
This commit is contained in:
unraid
2026-04-27 17:20:51 +08:00
parent a5ede237f0
commit a90c16431b
3 changed files with 48 additions and 17 deletions

View File

@@ -307,7 +307,9 @@ describe('UDS inbox retention', () => {
'../udsClient.js'
)
const error = await connectToPeer(path).then(
const error = await connectToPeer(path, () => {
throw new Error('Unexpected post-connect socket error')
}).then(
() => undefined,
err => err,
)
@@ -338,13 +340,25 @@ describe('UDS inbox retention', () => {
})
let client: Socket | undefined
const socketErrors: Error[] = []
try {
const { connectToPeer } = await import('../udsClient.js')
client = await connectToPeer(path, 1000)
client = await connectToPeer(
path,
error => {
socketErrors.push(error)
},
1000,
)
await new Promise(resolve => setTimeout(resolve, 100))
expect(client.destroyed).toBe(false)
expect(client.listenerCount('error')).toBe(0)
expect(client.listenerCount('error')).toBe(1)
expect(client.listenerCount('timeout')).toBe(0)
const socketError = new Error('post-connect failure')
client.emit('error', socketError)
expect(socketErrors).toEqual([socketError])
} finally {
client?.destroy()
for (const socket of sockets) {