refactor: create normalizeSDKResponse helper and replace scattered patterns across 37 files
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { log } from "../../shared/logger"
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
import { isSqliteBackend } from "../../shared/opencode-storage-detection"
|
||||
import {
|
||||
findEmptyMessages,
|
||||
@@ -64,7 +65,7 @@ async function findEmptyMessageIdsFromSDK(
|
||||
const response = (await client.session.messages({
|
||||
path: { id: sessionID },
|
||||
})) as { data?: SDKMessage[] }
|
||||
const messages = ((response.data ?? response) as unknown as SDKMessage[]) ?? []
|
||||
const messages = normalizeSDKResponse(response, [] as SDKMessage[], { preferResponseOnMissingData: true })
|
||||
|
||||
const emptyIds: string[] = []
|
||||
for (const message of messages) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { existsSync, readdirSync } from "node:fs"
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { getMessageDir } from "../../shared/opencode-message-dir"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
|
||||
export { getMessageDir }
|
||||
|
||||
@@ -17,7 +18,7 @@ export async function getMessageIdsFromSDK(
|
||||
): Promise<string[]> {
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
const messages = ((response.data ?? response) as unknown as SDKMessage[]) ?? []
|
||||
const messages = normalizeSDKResponse(response, [] as SDKMessage[], { preferResponseOnMissingData: true })
|
||||
return messages.map(msg => msg.info.id)
|
||||
} catch {
|
||||
return []
|
||||
|
||||
@@ -6,6 +6,7 @@ import { estimateTokens } from "./pruning-types"
|
||||
import { log } from "../../shared/logger"
|
||||
import { getMessageDir } from "../../shared/opencode-message-dir"
|
||||
import { isSqliteBackend } from "../../shared/opencode-storage-detection"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
|
||||
type OpencodeClient = PluginInput["client"]
|
||||
|
||||
@@ -72,7 +73,7 @@ function readMessages(sessionID: string): MessagePart[] {
|
||||
async function readMessagesFromSDK(client: OpencodeClient, sessionID: string): Promise<MessagePart[]> {
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
const rawMessages = ((response.data ?? response) as unknown as Array<{ parts?: ToolPart[] }>) ?? []
|
||||
const rawMessages = normalizeSDKResponse(response, [] as Array<{ parts?: ToolPart[] }>, { preferResponseOnMissingData: true })
|
||||
return rawMessages.filter((m) => m.parts) as MessagePart[]
|
||||
} catch {
|
||||
return []
|
||||
|
||||
@@ -7,6 +7,7 @@ import { truncateToolResultAsync } from "./tool-result-storage-sdk"
|
||||
import { log } from "../../shared/logger"
|
||||
import { getMessageDir } from "../../shared/opencode-message-dir"
|
||||
import { isSqliteBackend } from "../../shared/opencode-storage-detection"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
|
||||
type OpencodeClient = PluginInput["client"]
|
||||
|
||||
@@ -108,7 +109,7 @@ async function truncateToolOutputsByCallIdFromSDK(
|
||||
): Promise<{ truncatedCount: number }> {
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
const messages = ((response.data ?? response) as unknown as SDKMessage[]) ?? []
|
||||
const messages = normalizeSDKResponse(response, [] as SDKMessage[], { preferResponseOnMissingData: true })
|
||||
let truncatedCount = 0
|
||||
|
||||
for (const msg of messages) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { AggressiveTruncateResult } from "./tool-part-types"
|
||||
import { findToolResultsBySize, truncateToolResult } from "./tool-result-storage"
|
||||
import { truncateToolResultAsync } from "./tool-result-storage-sdk"
|
||||
import { isSqliteBackend } from "../../shared/opencode-storage-detection"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
|
||||
type OpencodeClient = PluginInput["client"]
|
||||
|
||||
@@ -66,7 +67,7 @@ export async function truncateUntilTargetTokens(
|
||||
const response = (await client.session.messages({
|
||||
path: { id: sessionID },
|
||||
})) as { data?: SDKMessage[] }
|
||||
const messages = (response.data ?? response) as SDKMessage[]
|
||||
const messages = normalizeSDKResponse(response, [] as SDKMessage[], { preferResponseOnMissingData: true })
|
||||
toolPartsByKey = new Map<string, SDKToolPart>()
|
||||
|
||||
for (const message of messages) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { TRUNCATION_MESSAGE } from "./storage-paths"
|
||||
import type { ToolResultInfo } from "./tool-part-types"
|
||||
import { patchPart } from "../../shared/opencode-http-api"
|
||||
import { log } from "../../shared/logger"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
|
||||
type OpencodeClient = PluginInput["client"]
|
||||
|
||||
@@ -32,7 +33,7 @@ export async function findToolResultsBySizeFromSDK(
|
||||
): Promise<ToolResultInfo[]> {
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
const messages = ((response.data ?? response) as unknown as SDKMessage[]) ?? []
|
||||
const messages = normalizeSDKResponse(response, [] as SDKMessage[], { preferResponseOnMissingData: true })
|
||||
const results: ToolResultInfo[] = []
|
||||
|
||||
for (const msg of messages) {
|
||||
@@ -98,7 +99,7 @@ export async function countTruncatedResultsFromSDK(
|
||||
): Promise<number> {
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
const messages = ((response.data ?? response) as unknown as SDKMessage[]) ?? []
|
||||
const messages = normalizeSDKResponse(response, [] as SDKMessage[], { preferResponseOnMissingData: true })
|
||||
let count = 0
|
||||
|
||||
for (const msg of messages) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
findNearestMessageWithFields,
|
||||
findNearestMessageWithFieldsFromSDK,
|
||||
} from "../../features/hook-message-injector"
|
||||
import { getMessageDir, isSqliteBackend } from "../../shared"
|
||||
import { getMessageDir, isSqliteBackend, normalizeSDKResponse } from "../../shared"
|
||||
import type { ModelInfo } from "./types"
|
||||
|
||||
export async function resolveRecentModelForSession(
|
||||
@@ -12,9 +12,9 @@ export async function resolveRecentModelForSession(
|
||||
): Promise<ModelInfo | undefined> {
|
||||
try {
|
||||
const messagesResp = await ctx.client.session.messages({ path: { id: sessionID } })
|
||||
const messages = (messagesResp.data ?? []) as Array<{
|
||||
const messages = normalizeSDKResponse(messagesResp, [] as Array<{
|
||||
info?: { model?: ModelInfo; modelID?: string; providerID?: string }
|
||||
}>
|
||||
}>)
|
||||
|
||||
for (let i = messages.length - 1; i >= 0; i--) {
|
||||
const info = messages[i].info
|
||||
|
||||
@@ -3,6 +3,7 @@ import { log } from "../../shared/logger"
|
||||
import { findNearestMessageWithFields } from "../../features/hook-message-injector"
|
||||
import { getMessageDir } from "./message-storage-directory"
|
||||
import { withTimeout } from "./with-timeout"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
|
||||
type MessageInfo = {
|
||||
agent?: string
|
||||
@@ -25,7 +26,7 @@ export async function injectContinuationPrompt(
|
||||
}),
|
||||
options.apiTimeoutMs,
|
||||
)
|
||||
const messages = (messagesResp.data ?? []) as Array<{ info?: MessageInfo }>
|
||||
const messages = normalizeSDKResponse(messagesResp, [] as Array<{ info?: MessageInfo }>)
|
||||
for (let i = messages.length - 1; i >= 0; i--) {
|
||||
const info = messages[i]?.info
|
||||
if (info?.agent || info?.model || (info?.modelID && info?.providerID)) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { createOpencodeClient } from "@opencode-ai/sdk"
|
||||
import type { MessageData } from "./types"
|
||||
import { extractMessageIndex } from "./detect-error-type"
|
||||
import { META_TYPES, THINKING_TYPES } from "./constants"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
|
||||
type Client = ReturnType<typeof createOpencodeClient>
|
||||
|
||||
@@ -136,7 +137,7 @@ function sdkMessageHasContent(message: MessageData): boolean {
|
||||
async function readMessagesFromSDK(client: Client, sessionID: string): Promise<MessageData[]> {
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
return ((response.data ?? response) as unknown as MessageData[]) ?? []
|
||||
return normalizeSDKResponse(response, [] as MessageData[], { preferResponseOnMissingData: true })
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { findMessageByIndexNeedingThinking, findMessagesWithOrphanThinking, prep
|
||||
import { isSqliteBackend } from "../../shared/opencode-storage-detection"
|
||||
import { prependThinkingPartAsync } from "./storage/thinking-prepend"
|
||||
import { THINKING_TYPES } from "./constants"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
|
||||
type Client = ReturnType<typeof createOpencodeClient>
|
||||
|
||||
@@ -77,7 +78,7 @@ async function findMessagesWithOrphanThinkingFromSDK(
|
||||
let messages: MessageData[]
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
messages = ((response.data ?? response) as unknown as MessageData[]) ?? []
|
||||
messages = normalizeSDKResponse(response, [] as MessageData[], { preferResponseOnMissingData: true })
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
@@ -111,7 +112,7 @@ async function findMessageByIndexNeedingThinkingFromSDK(
|
||||
let messages: MessageData[]
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
messages = ((response.data ?? response) as unknown as MessageData[]) ?? []
|
||||
messages = normalizeSDKResponse(response, [] as MessageData[], { preferResponseOnMissingData: true })
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { isSqliteBackend } from "../../shared/opencode-storage-detection"
|
||||
import { stripThinkingPartsAsync } from "./storage/thinking-strip"
|
||||
import { THINKING_TYPES } from "./constants"
|
||||
import { log } from "../../shared/logger"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
|
||||
type Client = ReturnType<typeof createOpencodeClient>
|
||||
|
||||
@@ -38,7 +39,7 @@ async function recoverThinkingDisabledViolationFromSDK(
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
const messages = ((response.data ?? response) as unknown as MessageData[]) ?? []
|
||||
const messages = normalizeSDKResponse(response, [] as MessageData[], { preferResponseOnMissingData: true })
|
||||
|
||||
const messageIDsWithThinking: string[] = []
|
||||
for (const msg of messages) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { createOpencodeClient } from "@opencode-ai/sdk"
|
||||
import type { MessageData } from "./types"
|
||||
import { readParts } from "./storage"
|
||||
import { isSqliteBackend } from "../../shared/opencode-storage-detection"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
|
||||
type Client = ReturnType<typeof createOpencodeClient>
|
||||
|
||||
@@ -28,7 +29,7 @@ async function readPartsFromSDKFallback(
|
||||
): Promise<MessagePart[]> {
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
const messages = ((response.data ?? response) as unknown as MessageData[]) ?? []
|
||||
const messages = normalizeSDKResponse(response, [] as MessageData[], { preferResponseOnMissingData: true })
|
||||
const target = messages.find((m) => m.info?.id === messageID)
|
||||
if (!target?.parts) return []
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { StoredPart, StoredTextPart, MessageData } from "../types"
|
||||
import { readMessages } from "./messages-reader"
|
||||
import { readParts } from "./parts-reader"
|
||||
import { log, isSqliteBackend, patchPart } from "../../../shared"
|
||||
import { normalizeSDKResponse } from "../../../shared"
|
||||
|
||||
type OpencodeClient = PluginInput["client"]
|
||||
|
||||
@@ -51,7 +52,7 @@ export async function replaceEmptyTextPartsAsync(
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
const messages = ((response.data ?? response) as unknown as MessageData[]) ?? []
|
||||
const messages = normalizeSDKResponse(response, [] as MessageData[], { preferResponseOnMissingData: true })
|
||||
|
||||
const targetMsg = messages.find((m) => m.info?.id === messageID)
|
||||
if (!targetMsg?.parts) return false
|
||||
@@ -101,7 +102,7 @@ export async function findMessagesWithEmptyTextPartsFromSDK(
|
||||
): Promise<string[]> {
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
const messages = ((response.data ?? response) as unknown as MessageData[]) ?? []
|
||||
const messages = normalizeSDKResponse(response, [] as MessageData[], { preferResponseOnMissingData: true })
|
||||
const result: string[] = []
|
||||
|
||||
for (const msg of messages) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { join } from "node:path"
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import type { StoredMessageMeta } from "../types"
|
||||
import { getMessageDir } from "./message-dir"
|
||||
import { isSqliteBackend } from "../../../shared"
|
||||
import { isSqliteBackend, normalizeSDKResponse } from "../../../shared"
|
||||
import { isRecord } from "../../../shared/record-type-guard"
|
||||
|
||||
type OpencodeClient = PluginInput["client"]
|
||||
@@ -62,7 +62,9 @@ export async function readMessagesFromSDK(
|
||||
): Promise<StoredMessageMeta[]> {
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
const data: unknown = response.data ?? response
|
||||
const data = normalizeSDKResponse(response, [] as unknown[], {
|
||||
preferResponseOnMissingData: true,
|
||||
})
|
||||
if (!Array.isArray(data)) return []
|
||||
|
||||
const messages = data
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { MessageData } from "../types"
|
||||
import { readMessages } from "./messages-reader"
|
||||
import { readParts } from "./parts-reader"
|
||||
import { log, isSqliteBackend, patchPart } from "../../../shared"
|
||||
import { normalizeSDKResponse } from "../../../shared"
|
||||
|
||||
type OpencodeClient = PluginInput["client"]
|
||||
|
||||
@@ -74,7 +75,7 @@ async function findLastThinkingContentFromSDK(
|
||||
): Promise<string> {
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
const messages = ((response.data ?? response) as unknown as MessageData[]) ?? []
|
||||
const messages = normalizeSDKResponse(response, [] as MessageData[], { preferResponseOnMissingData: true })
|
||||
|
||||
const currentIndex = messages.findIndex((m) => m.info?.id === beforeMessageID)
|
||||
if (currentIndex === -1) return ""
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { PART_STORAGE, THINKING_TYPES } from "../constants"
|
||||
import type { StoredPart } from "../types"
|
||||
import { log, isSqliteBackend, deletePart } from "../../../shared"
|
||||
import { normalizeSDKResponse } from "../../../shared"
|
||||
|
||||
type OpencodeClient = PluginInput["client"]
|
||||
|
||||
@@ -42,7 +43,7 @@ export async function stripThinkingPartsAsync(
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const response = await client.session.messages({ path: { id: sessionID } })
|
||||
const messages = ((response.data ?? response) as unknown as Array<{ parts?: Array<{ type: string; id: string }> }>) ?? []
|
||||
const messages = normalizeSDKResponse(response, [] as Array<{ parts?: Array<{ type: string; id: string }> }>, { preferResponseOnMissingData: true })
|
||||
|
||||
const targetMsg = messages.find((m) => {
|
||||
const info = (m as Record<string, unknown>)["info"] as Record<string, unknown> | undefined
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { normalizeSDKResponse } from "../shared"
|
||||
|
||||
interface Todo {
|
||||
content: string
|
||||
@@ -10,7 +11,7 @@ interface Todo {
|
||||
export async function hasIncompleteTodos(ctx: PluginInput, sessionID: string): Promise<boolean> {
|
||||
try {
|
||||
const response = await ctx.client.session.todo({ path: { id: sessionID } })
|
||||
const todos = (response.data ?? response) as Todo[]
|
||||
const todos = normalizeSDKResponse(response, [] as Todo[], { preferResponseOnMissingData: true })
|
||||
if (!todos || todos.length === 0) return false
|
||||
return todos.some((todo) => todo.status !== "completed" && todo.status !== "cancelled")
|
||||
} catch {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
|
||||
import type { BackgroundManager } from "../../features/background-agent"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
import {
|
||||
findNearestMessageWithFields,
|
||||
findNearestMessageWithFieldsFromSDK,
|
||||
@@ -63,7 +64,7 @@ export async function injectContinuation(args: {
|
||||
let todos: Todo[] = []
|
||||
try {
|
||||
const response = await ctx.client.session.todo({ path: { id: sessionID } })
|
||||
todos = (response.data ?? response) as Todo[]
|
||||
todos = normalizeSDKResponse(response, [] as Todo[], { preferResponseOnMissingData: true })
|
||||
} catch (error) {
|
||||
log(`[${HOOK_NAME}] Failed to fetch todos`, { sessionID, error: String(error) })
|
||||
return
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { PluginInput } from "@opencode-ai/plugin"
|
||||
|
||||
import type { BackgroundManager } from "../../features/background-agent"
|
||||
import type { ToolPermission } from "../../features/hook-message-injector"
|
||||
import { normalizeSDKResponse } from "../../shared"
|
||||
import { log } from "../../shared/logger"
|
||||
|
||||
import {
|
||||
@@ -67,7 +68,7 @@ export async function handleSessionIdle(args: {
|
||||
path: { id: sessionID },
|
||||
query: { directory: ctx.directory },
|
||||
})
|
||||
const messages = (messagesResp as { data?: Array<{ info?: MessageInfo }> }).data ?? []
|
||||
const messages = normalizeSDKResponse(messagesResp, [] as Array<{ info?: MessageInfo }>)
|
||||
if (isLastAssistantMessageAborted(messages)) {
|
||||
log(`[${HOOK_NAME}] Skipped: last assistant message was aborted (API fallback)`, { sessionID })
|
||||
return
|
||||
@@ -79,7 +80,7 @@ export async function handleSessionIdle(args: {
|
||||
let todos: Todo[] = []
|
||||
try {
|
||||
const response = await ctx.client.session.todo({ path: { id: sessionID } })
|
||||
todos = (response.data ?? response) as Todo[]
|
||||
todos = normalizeSDKResponse(response, [] as Todo[], { preferResponseOnMissingData: true })
|
||||
} catch (error) {
|
||||
log(`[${HOOK_NAME}] Todo fetch failed`, { sessionID, error: String(error) })
|
||||
return
|
||||
@@ -139,7 +140,7 @@ export async function handleSessionIdle(args: {
|
||||
const messagesResp = await ctx.client.session.messages({
|
||||
path: { id: sessionID },
|
||||
})
|
||||
const messages = (messagesResp.data ?? []) as Array<{ info?: MessageInfo }>
|
||||
const messages = normalizeSDKResponse(messagesResp, [] as Array<{ info?: MessageInfo }>)
|
||||
for (let i = messages.length - 1; i >= 0; i--) {
|
||||
const info = messages[i].info
|
||||
if (info?.agent === "compaction") {
|
||||
|
||||
Reference in New Issue
Block a user