refactor(sdk): narrow response fallbacks

Replace unsafe unknown-array assertions with generic fallback types and explicit todo status narrowing.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-05-15 16:31:13 +09:00
parent 4785767a0c
commit d92e78c956
3 changed files with 19 additions and 15 deletions
+16 -12
View File
@@ -2,7 +2,7 @@ import pc from "picocolors"
import type { RunContext } from "./types" import type { RunContext } from "./types"
import type { EventState } from "./events" import type { EventState } from "./events"
import { checkCompletionConditions } from "./completion" import { checkCompletionConditions } from "./completion"
import { normalizeSDKResponse } from "../../shared" import { isRecord, normalizeSDKResponse } from "../../shared"
const DEFAULT_POLL_INTERVAL_MS = 500 const DEFAULT_POLL_INTERVAL_MS = 500
const DEFAULT_REQUIRED_CONSECUTIVE = 1 const DEFAULT_REQUIRED_CONSECUTIVE = 1
@@ -11,6 +11,17 @@ const MIN_STABILIZATION_MS = 1_000
const DEFAULT_EVENT_WATCHDOG_MS = 30_000 // 30 seconds const DEFAULT_EVENT_WATCHDOG_MS = 30_000 // 30 seconds
const DEFAULT_SECONDARY_MEANINGFUL_WORK_TIMEOUT_MS = 60_000 // 60 seconds const DEFAULT_SECONDARY_MEANINGFUL_WORK_TIMEOUT_MS = 60_000 // 60 seconds
type SessionStatusMap = Record<string, { type?: string }>
function isIncompleteTodo(value: unknown): boolean {
if (!isRecord(value)) {
return true
}
const status = value.status
return status !== "completed" && status !== "cancelled"
}
export interface PollOptions { export interface PollOptions {
pollIntervalMs?: number pollIntervalMs?: number
requiredConsecutive?: number requiredConsecutive?: number
@@ -123,22 +134,18 @@ export async function pollForCompletion(
path: { id: ctx.sessionID }, path: { id: ctx.sessionID },
query: { directory: ctx.directory }, query: { directory: ctx.directory },
}) })
const children = normalizeSDKResponse(childrenRes, [] as unknown[]) const children = normalizeSDKResponse<unknown[]>(childrenRes, [])
const todosRes = await ctx.client.session.todo({ const todosRes = await ctx.client.session.todo({
path: { id: ctx.sessionID }, path: { id: ctx.sessionID },
query: { directory: ctx.directory }, query: { directory: ctx.directory },
}) })
const todos = normalizeSDKResponse(todosRes, [] as unknown[]) const todos = normalizeSDKResponse<unknown[]>(todosRes, [])
const hasActiveChildren = const hasActiveChildren =
Array.isArray(children) && children.length > 0 Array.isArray(children) && children.length > 0
const hasActiveTodos = const hasActiveTodos =
Array.isArray(todos) && Array.isArray(todos) &&
todos.some( todos.some(isIncompleteTodo)
(t: unknown) =>
(t as { status?: string })?.status !== "completed" &&
(t as { status?: string })?.status !== "cancelled"
)
const hasActiveWork = hasActiveChildren || hasActiveTodos const hasActiveWork = hasActiveChildren || hasActiveTodos
if (hasActiveWork) { if (hasActiveWork) {
@@ -189,10 +196,7 @@ async function getMainSessionStatus(
const statusesRes = await ctx.client.session.status({ const statusesRes = await ctx.client.session.status({
query: { directory: ctx.directory }, query: { directory: ctx.directory },
}) })
const statuses = normalizeSDKResponse( const statuses = normalizeSDKResponse<SessionStatusMap>(statusesRes, {})
statusesRes,
{} as Record<string, { type?: string }>
)
if (!(ctx.sessionID in statuses)) { if (!(ctx.sessionID in statuses)) {
return "idle" return "idle"
} }
+2 -2
View File
@@ -189,8 +189,8 @@ export function createReadImageResizerHook(_ctx: PluginInput) {
} }
} }
if (attachmentsToRemove.length > 0) { if (attachmentsToRemove.length > 0 && Array.isArray(outputRecord.attachments)) {
const rawAttachments = outputRecord.attachments as unknown[] const rawAttachments = outputRecord.attachments
for (const toRemove of attachmentsToRemove) { for (const toRemove of attachmentsToRemove) {
const removeIndex = rawAttachments.indexOf(toRemove) const removeIndex = rawAttachments.indexOf(toRemove)
if (removeIndex !== -1) { if (removeIndex !== -1) {
@@ -62,7 +62,7 @@ export async function readMessagesFromSDK(
): Promise<StoredMessageMeta[]> { ): Promise<StoredMessageMeta[]> {
try { try {
const response = await client.session.messages({ path: { id: sessionID } }) const response = await client.session.messages({ path: { id: sessionID } })
const data = normalizeSDKResponse(response, [] as unknown[], { const data = normalizeSDKResponse<unknown[]>(response, [], {
preferResponseOnMissingData: true, preferResponseOnMissingData: true,
}) })
if (!Array.isArray(data)) return [] if (!Array.isArray(data)) return []