mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-08-24 02:34:15 -05:00
fix(typecheck): reduce error baseline by 89 across 8 files (#1595)
* fix: resolve 28 typecheck errors in openaiShim.ts Add null guards for nullable `reader`, `response`, and `responsesResponse` variables, and use type assertions to bridge Node vs Web ReadableStream type mismatches in stream processing helpers. * fix(typecheck): resolve 17 errors in agentSdkTypes.ts - Add @ts-expect-error for settingsTypes.generated.js (generated at build time) - Fix type imports: redirect 5 types from ./sdk/runtimeTypes.js to ./sdk/shared.js - Remove 11 unused type imports that don't exist (AnyZodRawShape, InferShape, etc.) * fix(typecheck): resolve 26 errors in openaiShim.ts — nullable guards, ReadableStream types * fix(typecheck): resolve 23 errors in messages.ts + groupToolUses.ts MessageWithoutProgress resolved to `never` because all message types are `any` stubs, making `Exclude<any, any>` = `never`. Widen types and use boolean wrappers to avoid type-predicate narrowing. Add missing return in getToolUseID switch statement. * fix(typecheck): resolve 14 errors in toolExecution.ts — fix never[] inference * fix(typecheck): resolve type errors in claude.ts - Cast nested block params to BetaContentBlockParam for SDK type union mismatch - Add missing CACHE_EDITING_BETA_HEADER constant to betas.ts - Type-assert getCachedMCConfig() return for supportedModels access - Add missing imports: getContextWindowForModel, COMPACT_MAX_OUTPUT_TOKENS, getSdkBetas - Fix model variable reference to use options.model in compact context - Add optional signature property to ConnectorTextBlock type * ci: re-trigger checks * fix: address CodeRabbit review feedback - Throw error instead of silent return when response body is not readable - Clamp hybrid context budget to non-negative floor (Math.max(0, ...)) - Remove unused isResult wrapper in messages.ts
This commit is contained in:
@@ -28,6 +28,7 @@ export const AFK_MODE_BETA_HEADER = feature('TRANSCRIPT_CLASSIFIER')
|
||||
: ''
|
||||
export const CLI_INTERNAL_BETA_HEADER =
|
||||
process.env.USER_TYPE === 'ant' ? 'cli-internal-2026-02-09' : ''
|
||||
export const CACHE_EDITING_BETA_HEADER = 'cache-editing-2025-12-01'
|
||||
export const ADVISOR_BETA_HEADER = 'advisor-tool-2026-03-01'
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,6 +21,7 @@ export * from './sdk/coreTypes.js'
|
||||
export * from './sdk/runtimeTypes.js'
|
||||
|
||||
// Re-export settings types (generated from settings JSON schema)
|
||||
// @ts-expect-error — generated at build time
|
||||
export type { Settings } from './sdk/settingsTypes.generated.js'
|
||||
// Re-export tool types (all marked @internal until SDK API stabilizes)
|
||||
export * from './sdk/toolTypes.js'
|
||||
@@ -49,23 +50,12 @@ export {
|
||||
|
||||
// Import types needed for @internal function signatures kept below
|
||||
import type {
|
||||
AnyZodRawShape,
|
||||
ForkSessionOptions,
|
||||
ForkSessionResult,
|
||||
GetSessionInfoOptions,
|
||||
GetSessionMessagesOptions,
|
||||
InferShape,
|
||||
InternalOptions,
|
||||
InternalQuery,
|
||||
ListSessionsOptions,
|
||||
Options,
|
||||
Query,
|
||||
SDKSession,
|
||||
SDKSessionOptions,
|
||||
SdkMcpToolDefinition,
|
||||
SessionMessage,
|
||||
SessionMutationOptions,
|
||||
} from './sdk/runtimeTypes.js'
|
||||
} from './sdk/shared.js'
|
||||
|
||||
import type {
|
||||
SDKMessage,
|
||||
|
||||
@@ -119,6 +119,7 @@ import {
|
||||
getCacheEditingHeaderLatched,
|
||||
getFastModeHeaderLatched,
|
||||
getLastApiCompletionTimestamp,
|
||||
getSdkBetas,
|
||||
getPromptCache1hAllowlist,
|
||||
getPromptCache1hEligible,
|
||||
getSessionId,
|
||||
@@ -164,7 +165,7 @@ import {
|
||||
} from 'src/utils/betas.js'
|
||||
import { CLAUDE_IN_CHROME_MCP_SERVER_NAME } from 'src/utils/claudeInChrome/common.js'
|
||||
import { CHROME_TOOL_SEARCH_INSTRUCTIONS } from 'src/utils/claudeInChrome/prompt.js'
|
||||
import { getMaxThinkingTokensForModel } from 'src/utils/context.js'
|
||||
import { COMPACT_MAX_OUTPUT_TOKENS, getContextWindowForModel, getMaxThinkingTokensForModel } from 'src/utils/context.js'
|
||||
import { logForDebugging } from 'src/utils/debug.js'
|
||||
import { logForDiagnosticsNoPII } from 'src/utils/diagLogs.js'
|
||||
import { type EffortValue, modelSupportsEffort } from 'src/utils/effort.js'
|
||||
@@ -978,7 +979,7 @@ export function stripExcessMediaItems(
|
||||
if (isMedia(block)) toRemove++
|
||||
if (isToolResult(block) && Array.isArray(block.content)) {
|
||||
for (const nested of block.content) {
|
||||
if (isMedia(nested)) toRemove++
|
||||
if (isMedia(nested as BetaContentBlockParam)) toRemove++
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1001,7 +1002,7 @@ export function stripExcessMediaItems(
|
||||
)
|
||||
return block
|
||||
const filtered = block.content.filter(n => {
|
||||
if (toRemove > 0 && isMedia(n)) {
|
||||
if (toRemove > 0 && isMedia(n as BetaContentBlockParam)) {
|
||||
toRemove--
|
||||
return false
|
||||
}
|
||||
@@ -1214,7 +1215,7 @@ async function* queryModel(
|
||||
cachedMCEnabled = featureEnabled && modelSupported
|
||||
const config = getCachedMCConfig()
|
||||
logForDebugging(
|
||||
`Cached MC gate: enabled=${featureEnabled} modelSupported=${modelSupported} model=${options.model} supportedModels=${jsonStringify(config?.supportedModels)}`,
|
||||
`Cached MC gate: enabled=${featureEnabled} modelSupported=${modelSupported} model=${options.model} supportedModels=${jsonStringify((config as Record<string, unknown> | null)?.supportedModels)}`,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1287,9 +1288,12 @@ async function* queryModel(
|
||||
const strategyResult = applyHybridStrategy(messagesForAPI, {
|
||||
cacheWeight: 0.4,
|
||||
freshWeight: 0.6,
|
||||
maxTotalTokens: Math.min(
|
||||
getContextWindowForModel(model, getSdkBetas()) - COMPACT_MAX_OUTPUT_TOKENS,
|
||||
200000
|
||||
maxTotalTokens: Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
getContextWindowForModel(options.model, getSdkBetas()) - COMPACT_MAX_OUTPUT_TOKENS,
|
||||
200000,
|
||||
),
|
||||
),
|
||||
})
|
||||
messagesForAPI = strategyResult.selectedMessages
|
||||
|
||||
@@ -1055,15 +1055,17 @@ async function* anthropicSsePassthrough(
|
||||
_model: string,
|
||||
signal?: AbortSignal,
|
||||
): AsyncGenerator<AnthropicStreamEvent> {
|
||||
const reader = response.body?.getReader()
|
||||
if (!reader) return
|
||||
const readerOrNull = response.body?.getReader()
|
||||
if (!readerOrNull) throw new Error('Response body is not readable')
|
||||
const reader: ReadableStreamDefaultReader<Uint8Array> = readerOrNull
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ''
|
||||
|
||||
// Read helper that properly cleans up abort listeners (mirrors codexShim.ts pattern).
|
||||
function readWithAbort(): Promise<ReadableStreamReadResult<Uint8Array>> {
|
||||
type ReadResult = Awaited<ReturnType<typeof reader.read>>
|
||||
function readWithAbort(): Promise<ReadResult> {
|
||||
if (!signal) return reader.read()
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise<ReadResult>((resolve, reject) => {
|
||||
const onAbort = () => reject(new DOMException('Aborted', 'AbortError'))
|
||||
signal.addEventListener('abort', onAbort, { once: true })
|
||||
reader.read().then(
|
||||
@@ -1116,8 +1118,8 @@ async function* geminiSseToAnthropic(
|
||||
model: string,
|
||||
signal?: AbortSignal,
|
||||
): AsyncGenerator<AnthropicStreamEvent> {
|
||||
const reader = response.body?.getReader()
|
||||
if (!reader) return
|
||||
const reader: ReadableStreamDefaultReader<Uint8Array> | undefined = response.body?.getReader()
|
||||
if (!reader) throw new Error('Response body is not readable')
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ''
|
||||
const messageId = makeMessageId()
|
||||
@@ -1129,12 +1131,12 @@ async function* geminiSseToAnthropic(
|
||||
let finishReason: string | undefined
|
||||
|
||||
function readWithAbort(): Promise<ReadableStreamReadResult<Uint8Array>> {
|
||||
if (!signal) return reader.read()
|
||||
if (!signal) return reader!.read() as Promise<ReadableStreamReadResult<Uint8Array>>
|
||||
return new Promise((resolve, reject) => {
|
||||
const onAbort = () => reject(new DOMException('Aborted', 'AbortError'))
|
||||
signal.addEventListener('abort', onAbort, { once: true })
|
||||
reader.read().then(
|
||||
result => { signal.removeEventListener('abort', onAbort); resolve(result) },
|
||||
reader!.read().then(
|
||||
result => { signal.removeEventListener('abort', onAbort); resolve(result as ReadableStreamReadResult<Uint8Array>) },
|
||||
err => { signal.removeEventListener('abort', onAbort); reject(err) },
|
||||
)
|
||||
})
|
||||
@@ -1332,8 +1334,9 @@ async function* openaiStreamToAnthropic(
|
||||
},
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader()
|
||||
if (!reader) return
|
||||
const readerOrNull = response.body?.getReader()
|
||||
if (!readerOrNull) throw new Error('Response body is not readable')
|
||||
const reader: ReadableStreamDefaultReader<Uint8Array> = readerOrNull
|
||||
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ''
|
||||
@@ -1348,8 +1351,9 @@ async function* openaiStreamToAnthropic(
|
||||
* Respects the caller's AbortSignal — clears the idle timer on abort
|
||||
* so the rejection reason is AbortError, not a spurious idle timeout.
|
||||
*/
|
||||
async function readWithTimeout(): Promise<ReadableStreamReadResult<Uint8Array>> {
|
||||
return new Promise((resolve, reject) => {
|
||||
type ReadResult = Awaited<ReturnType<typeof reader.read>>
|
||||
async function readWithTimeout(): Promise<ReadResult> {
|
||||
return new Promise<ReadResult>((resolve, reject) => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
const elapsed = Math.round((Date.now() - lastDataTime) / 1000)
|
||||
reject(new Error(
|
||||
@@ -2823,6 +2827,10 @@ class OpenAIShimMessages {
|
||||
throwClassifiedTransportError(error, requestUrl, failure)
|
||||
}
|
||||
|
||||
// After the try/catch, response is guaranteed to be defined — the catch
|
||||
// block always throws (throwClassifiedTransportError returns never).
|
||||
if (!response) continue
|
||||
|
||||
if (response.ok) {
|
||||
let tokensIn = 0
|
||||
let tokensOut = 0
|
||||
@@ -2867,7 +2875,7 @@ class OpenAIShimMessages {
|
||||
const responsesUrl = `${request.baseUrl}/responses`
|
||||
const responsesBody = buildResponsesBody()
|
||||
|
||||
let responsesResponse: Response
|
||||
let responsesResponse!: Response
|
||||
try {
|
||||
responsesResponse = await fetchWithProxyRetry(responsesUrl, {
|
||||
method: 'POST',
|
||||
|
||||
@@ -826,7 +826,7 @@ async function checkPermissionsAndCallTool(
|
||||
)
|
||||
}
|
||||
|
||||
const resultingMessages = []
|
||||
const resultingMessages: MessageUpdateLazy[] = []
|
||||
|
||||
// Defense-in-depth: strip _simulatedSedEdit from model-provided Bash input.
|
||||
// This field is internal-only — it must only be injected by the permission
|
||||
@@ -1375,7 +1375,7 @@ async function checkPermissionsAndCallTool(
|
||||
|
||||
// Run PostToolUse hooks
|
||||
let toolOutput = result.data
|
||||
const hookResults = []
|
||||
const hookResults: MessageUpdateLazy[] = []
|
||||
const toolContextModifier = result.contextModifier
|
||||
const mcpMeta = result.mcpMeta
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ export type ConnectorTextBlock = {
|
||||
type: 'connector_text'
|
||||
connector_text: string
|
||||
connector?: string
|
||||
signature?: string
|
||||
}
|
||||
|
||||
export type ConnectorTextDelta = {
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
RenderableMessage,
|
||||
} from '../types/message.js'
|
||||
|
||||
export type MessageWithoutProgress = Exclude<NormalizedMessage, ProgressMessage>
|
||||
export type MessageWithoutProgress = NormalizedMessage
|
||||
|
||||
export type GroupingResult = {
|
||||
messages: RenderableMessage[]
|
||||
|
||||
+19
-25
@@ -861,19 +861,14 @@ export function isToolUseResultMessage(
|
||||
|
||||
// Re-order, to move result messages to be after their tool use messages
|
||||
export function reorderMessagesInUI(
|
||||
messages: (
|
||||
| NormalizedUserMessage
|
||||
| NormalizedAssistantMessage
|
||||
| AttachmentMessage
|
||||
| SystemMessage
|
||||
)[],
|
||||
messages: any[], // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
syntheticStreamingToolUseMessages: NormalizedAssistantMessage[],
|
||||
): (
|
||||
| NormalizedUserMessage
|
||||
| NormalizedAssistantMessage
|
||||
| AttachmentMessage
|
||||
| SystemMessage
|
||||
)[] {
|
||||
): any[] { // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
// Boolean wrappers to avoid type-predicate narrowing (all message types are `any` stubs,
|
||||
// so `Exclude<any, any>` = `never` after type guards)
|
||||
const isToolUse = (m: any): boolean => isToolUseRequestMessage(m) // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
const isHook = (m: any): boolean => isHookAttachmentMessage(m) // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
|
||||
// Maps tool use ID to its related messages
|
||||
const toolUseGroups = new Map<
|
||||
string,
|
||||
@@ -886,9 +881,10 @@ export function reorderMessagesInUI(
|
||||
>()
|
||||
|
||||
// First pass: group messages by tool use ID
|
||||
for (const message of messages) {
|
||||
for (const _msg of messages) {
|
||||
const message: any = _msg // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
// Handle tool use messages
|
||||
if (isToolUseRequestMessage(message)) {
|
||||
if (isToolUse(message)) {
|
||||
const toolUseID = message.message.content[0]?.id
|
||||
if (toolUseID) {
|
||||
if (!toolUseGroups.has(toolUseID)) {
|
||||
@@ -906,7 +902,7 @@ export function reorderMessagesInUI(
|
||||
|
||||
// Handle pre-tool-use hooks
|
||||
if (
|
||||
isHookAttachmentMessage(message) &&
|
||||
isHook(message) &&
|
||||
message.attachment.hookEvent === 'PreToolUse'
|
||||
) {
|
||||
const toolUseID = message.attachment.toolUseID
|
||||
@@ -942,7 +938,7 @@ export function reorderMessagesInUI(
|
||||
|
||||
// Handle post-tool-use hooks
|
||||
if (
|
||||
isHookAttachmentMessage(message) &&
|
||||
isHook(message) &&
|
||||
message.attachment.hookEvent === 'PostToolUse'
|
||||
) {
|
||||
const toolUseID = message.attachment.toolUseID
|
||||
@@ -960,17 +956,13 @@ export function reorderMessagesInUI(
|
||||
}
|
||||
|
||||
// Second pass: reconstruct the message list in the correct order
|
||||
const result: (
|
||||
| NormalizedUserMessage
|
||||
| NormalizedAssistantMessage
|
||||
| AttachmentMessage
|
||||
| SystemMessage
|
||||
)[] = []
|
||||
const result: any[] = [] // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
const processedToolUses = new Set<string>()
|
||||
|
||||
for (const message of messages) {
|
||||
for (const _msg of messages) {
|
||||
const message: any = _msg // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
// Check if this is a tool use
|
||||
if (isToolUseRequestMessage(message)) {
|
||||
if (isToolUse(message)) {
|
||||
const toolUseID = message.message.content[0]?.id
|
||||
if (toolUseID && !processedToolUses.has(toolUseID)) {
|
||||
processedToolUses.add(toolUseID)
|
||||
@@ -990,7 +982,7 @@ export function reorderMessagesInUI(
|
||||
|
||||
// Check if this message is part of a tool use group
|
||||
if (
|
||||
isHookAttachmentMessage(message) &&
|
||||
isHook(message) &&
|
||||
(message.attachment.hookEvent === 'PreToolUse' ||
|
||||
message.attachment.hookEvent === 'PostToolUse')
|
||||
) {
|
||||
@@ -2863,6 +2855,8 @@ export function getToolUseID(message: NormalizedMessage): string | null {
|
||||
return message.subtype === 'informational'
|
||||
? (message.toolUseID ?? null)
|
||||
: null
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user