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:
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 []
|
||||||
|
|||||||
Reference in New Issue
Block a user