refactor(api): extract versioned update handling to shared utility

Extract duplicate version acknowledgment logic from apiMachine and
apiSession into a reusable applyVersionedAck utility. This handles
success, version-mismatch, and error responses consistently with
customizable parsing and error handling via options object.
This commit is contained in:
weishu
2026-01-03 22:23:29 +08:00
parent 705933e17b
commit f2f46e9383
4 changed files with 266 additions and 172 deletions
+41 -86
View File
@@ -31,6 +31,7 @@ import {
TerminalResizePayloadSchema,
TerminalWritePayloadSchema
} from '@/terminal/types'
import { applyVersionedAck } from './versionedUpdate'
export class ApiSessionClient extends EventEmitter {
private readonly token: string
@@ -459,49 +460,26 @@ export class ApiSessionClient extends EventEmitter {
metadata: updated
}) as unknown
if (!answer || typeof answer !== 'object') {
throw new Error('Invalid update-metadata response')
}
const obj = answer as { result?: unknown; version?: unknown; metadata?: unknown }
if (obj.result === 'success' && typeof obj.version === 'number') {
const next = obj.metadata
if (next == null) {
this.metadata = null
} else {
const parsed = MetadataSchema.safeParse(next)
if (parsed.success) {
this.metadata = parsed.data
} else {
logger.debug('[API] Ignoring invalid metadata value from ack', { version: obj.version })
}
}
this.metadataVersion = obj.version
return
}
if (obj.result === 'version-mismatch' && typeof obj.version === 'number') {
const next = obj.metadata
if (next == null) {
this.metadata = null
} else {
const parsed = MetadataSchema.safeParse(next)
if (parsed.success) {
this.metadata = parsed.data
} else {
logger.debug('[API] Ignoring invalid metadata value from version-mismatch ack', { version: obj.version })
}
}
this.metadataVersion = obj.version
throw new Error('Metadata version mismatch')
}
if (obj.result === 'error') {
const reason = typeof (obj as { reason?: unknown }).reason === 'string'
? (obj as { reason?: string }).reason
: 'unknown'
throw new Error(`Metadata update failed (${reason})`)
}
applyVersionedAck(answer, {
valueKey: 'metadata',
parseValue: (value) => {
const parsed = MetadataSchema.safeParse(value)
return parsed.success ? parsed.data : null
},
applyValue: (value) => {
this.metadata = value
},
applyVersion: (version) => {
this.metadataVersion = version
},
logInvalidValue: (context, version) => {
const suffix = context === 'success' ? 'ack' : 'version-mismatch ack'
logger.debug(`[API] Ignoring invalid metadata value from ${suffix}`, { version })
},
invalidResponseMessage: 'Invalid update-metadata response',
errorMessage: 'Metadata update failed',
versionMismatchMessage: 'Metadata version mismatch'
})
})
})
}
@@ -518,49 +496,26 @@ export class ApiSessionClient extends EventEmitter {
agentState: updated
}) as unknown
if (!answer || typeof answer !== 'object') {
throw new Error('Invalid update-state response')
}
const obj = answer as { result?: unknown; version?: unknown; agentState?: unknown }
if (obj.result === 'success' && typeof obj.version === 'number') {
const next = obj.agentState
if (next == null) {
this.agentState = null
} else {
const parsed = AgentStateSchema.safeParse(next)
if (parsed.success) {
this.agentState = parsed.data
} else {
logger.debug('[API] Ignoring invalid agentState value from ack', { version: obj.version })
}
}
this.agentStateVersion = obj.version
return
}
if (obj.result === 'version-mismatch' && typeof obj.version === 'number') {
const next = obj.agentState
if (next == null) {
this.agentState = null
} else {
const parsed = AgentStateSchema.safeParse(next)
if (parsed.success) {
this.agentState = parsed.data
} else {
logger.debug('[API] Ignoring invalid agentState value from version-mismatch ack', { version: obj.version })
}
}
this.agentStateVersion = obj.version
throw new Error('Agent state version mismatch')
}
if (obj.result === 'error') {
const reason = typeof (obj as { reason?: unknown }).reason === 'string'
? (obj as { reason?: string }).reason
: 'unknown'
throw new Error(`Agent state update failed (${reason})`)
}
applyVersionedAck(answer, {
valueKey: 'agentState',
parseValue: (value) => {
const parsed = AgentStateSchema.safeParse(value)
return parsed.success ? parsed.data : null
},
applyValue: (value) => {
this.agentState = value
},
applyVersion: (version) => {
this.agentStateVersion = version
},
logInvalidValue: (context, version) => {
const suffix = context === 'success' ? 'ack' : 'version-mismatch ack'
logger.debug(`[API] Ignoring invalid agentState value from ${suffix}`, { version })
},
invalidResponseMessage: 'Invalid update-state response',
errorMessage: 'Agent state update failed',
versionMismatchMessage: 'Agent state version mismatch'
})
})
})
}