fix: resolve tsc errors (remaining upstream type fixes)

This commit is contained in:
James Feng 2026-06-01 19:01:38 +08:00
parent fe7fcf7dad
commit ed197502af
142 changed files with 722 additions and 561 deletions

View File

@ -2149,7 +2149,7 @@ async function handleScreenshot(
const monitorNote = await buildMonitorNote(
adapter,
shot.displayId,
shot.displayId ?? 0,
overrides.lastScreenshot?.displayId,
overrides.onDisplayPinned !== undefined,
);
@ -2218,7 +2218,7 @@ async function handleScreenshot(
const monitorNote = await buildMonitorNote(
adapter,
shot.displayId,
shot.displayId ?? 0,
overrides.lastScreenshot?.displayId,
overrides.onDisplayPinned !== undefined,
);

View File

@ -16,6 +16,12 @@
*/
import bidiFactory from 'bidi-js'
type BidiInstance = {
getEmbeddingLevels: (text: string, defaultDirection?: string) => { paragraphLevel: number; levels: Uint8Array }
getReorderSegments: (text: string, embeddingLevels: { paragraphLevel: number; levels: Uint8Array }, start?: number, end?: number) => [number, number][]
getVisualOrder: (reorderSegments: [number, number][]) => number[]
}
type ClusteredChar = {
value: string
width: number
@ -23,7 +29,7 @@ type ClusteredChar = {
hyperlink: string | undefined
}
let bidiInstance: ReturnType<typeof bidiFactory> | undefined
let bidiInstance: BidiInstance | undefined
let needsSoftwareBidi: boolean | undefined
function needsBidi(): boolean {
@ -38,7 +44,7 @@ function needsBidi(): boolean {
function getBidi() {
if (!bidiInstance) {
bidiInstance = bidiFactory()
bidiInstance = (bidiFactory as unknown as () => BidiInstance)()
}
return bidiInstance
}

View File

@ -1883,8 +1883,8 @@ export default class Ink {
let reentered = false
const intercept = (
chunk: Uint8Array | string,
encodingOrCb?: BufferEncoding | ((err?: Error) => void),
cb?: (err?: Error) => void,
encodingOrCb?: BufferEncoding | ((err?: Error | null) => void),
cb?: (err?: Error | null) => void,
): boolean => {
const callback = typeof encodingOrCb === 'function' ? encodingOrCb : cb
// Reentrancy guard: logger.debug → writeToStderr → here. Pass

View File

@ -564,16 +564,16 @@ export class QueryEngine {
for (const msg of messagesFromUserInput) {
if (
msg.type === 'user' &&
typeof msg.message.content === 'string' &&
(msg.message.content.includes(`<${LOCAL_COMMAND_STDOUT_TAG}>`) ||
msg.message.content.includes(`<${LOCAL_COMMAND_STDERR_TAG}>`) ||
typeof msg.message!.content === 'string' &&
(msg.message!.content.includes(`<${LOCAL_COMMAND_STDOUT_TAG}>`) ||
msg.message!.content.includes(`<${LOCAL_COMMAND_STDERR_TAG}>`) ||
msg.isCompactSummary)
) {
yield {
type: 'user',
message: {
...msg.message,
content: stripAnsi(msg.message.content),
content: stripAnsi(msg.message!.content),
},
session_id: getSessionId(),
parent_tool_use_id: null,
@ -1090,7 +1090,7 @@ export class QueryEngine {
const edeResultType = result?.type ?? 'undefined'
const edeLastContentType =
result?.type === 'assistant'
? (last(result.message.content)?.type ?? 'none')
? (last(result.message!.content as import('@anthropic-ai/sdk/resources/beta/messages/messages.js').BetaContentBlock[])?.type ?? 'none')
: 'n/a'
// Flush buffered transcript writes before yielding result.
@ -1148,7 +1148,7 @@ export class QueryEngine {
let isApiError = false
if (result.type === 'assistant') {
const lastContent = last(result.message.content)
const lastContent = last(result.message!.content as import('@anthropic-ai/sdk/resources/beta/messages/messages.js').BetaContentBlock[])
if (
lastContent?.type === 'text' &&
!SYNTHETIC_MESSAGES.has(lastContent.text)

View File

@ -1429,7 +1429,7 @@ export function registerHookCallbacks(
if (!STATE.registeredHooks[eventKey]) {
STATE.registeredHooks[eventKey] = []
}
STATE.registeredHooks[eventKey]!.push(...matchers)
STATE.registeredHooks[eventKey]!.push(...(matchers ?? []))
}
}
@ -1451,7 +1451,7 @@ export function clearRegisteredPluginHooks(): void {
const filtered: Partial<Record<HookEvent, RegisteredHookMatcher[]>> = {}
for (const [event, matchers] of Object.entries(STATE.registeredHooks)) {
// Keep only callback hooks (those without pluginRoot)
const callbackHooks = matchers.filter(m => !('pluginRoot' in m))
const callbackHooks = (matchers ?? []).filter(m => !('pluginRoot' in m))
if (callbackHooks.length > 0) {
filtered[event as HookEvent] = callbackHooks
}

View File

@ -105,12 +105,12 @@ export function extractTitleText(m: Message): string | undefined {
if (m.type !== 'user' || m.isMeta || m.toolUseResult || m.isCompactSummary)
return undefined
if (m.origin && (m.origin as { kind?: string }).kind !== 'human') return undefined
const content = m.message.content
const content = m.message!.content
let raw: string | undefined
if (typeof content === 'string') {
raw = content
} else {
for (const block of content) {
for (const block of content ?? []) {
if (block.type === 'text') {
raw = block.text
break

View File

@ -25,6 +25,7 @@ import {
waitForPolicyLimitsToLoad,
} from '../services/policyLimits/index.js'
import type { Message } from '../types/message.js'
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/index.js'
import {
checkAndRefreshOAuthTokenIfNeeded,
getClaudeAIOAuthTokens,
@ -289,7 +290,7 @@ export async function initReplBridge(
isSyntheticMessage(msg)
)
continue
const rawContent = getContentText(msg.message.content)
const rawContent = getContentText(msg.message!.content as string | ContentBlockParam[])
if (!rawContent) continue
const derived = deriveTitle(rawContent)
if (!derived) continue

View File

@ -74,13 +74,15 @@ import type { SDKResultSuccess } from '../entrypoints/sdk/coreTypes.js'
import type { PermissionMode } from '../utils/permissions/PermissionMode.js'
/**
* StdoutMessage with session_id added. The transport layer adds session_id
* to messages at runtime, but the Zod schemas don't include it. This type
* makes it explicit that we're adding session_id to each message variant.
* StdoutMessage with optional session_id. The transport layer accepts
* StdoutMessage but we add session_id at runtime. Using optional because
* the type system can't verify that adding session_id to a union type
* is always valid, even though it is at runtime.
*
* We need to use 'as StdoutMessage' when passing to transport because
* TypeScript can't verify that objects with session_id are valid StdoutMessage.
*/
type StdoutMessageWithSession = StdoutMessage extends infer T
? T & { session_id: string }
: never
type TransportMessage = StdoutMessage & { session_id?: string }
const ANTHROPIC_VERSION = '2023-06-01'
@ -619,17 +621,17 @@ export async function initEnvLessBridgeCore(
const msgs = flushGate.end()
if (msgs.length === 0) return
for (const msg of msgs) recentPostedUUIDs.add(msg.uuid)
const events: StdoutMessageWithSession[] = toSDKMessages(msgs).map(m => ({
const events: TransportMessage[] = toSDKMessages(msgs).map(m => ({
...m,
session_id: sessionId,
}))
})) as TransportMessage[]
if (msgs.some(m => m.type === 'user')) {
transport.reportState('running')
}
logForDebugging(
`[remote-bridge] Drained ${msgs.length} queued message(s) after flush`,
)
void transport.writeBatch(events)
void transport.writeBatch(events as StdoutMessage[])
}
async function flushHistory(msgs: Message[]): Promise<void> {
@ -647,10 +649,10 @@ export async function initEnvLessBridgeCore(
`[remote-bridge] Capped initial flush: ${eligible.length} -> ${capped.length} (cap=${initialHistoryCap})`,
)
}
const events: StdoutMessageWithSession[] = toSDKMessages(capped).map(m => ({
const events: TransportMessage[] = toSDKMessages(capped).map(m => ({
...m,
session_id: sessionId,
}))
})) as TransportMessage[]
if (events.length === 0) return
// Mid-turn init: if Remote Control is enabled while a query is running,
// the last eligible message is a user prompt or tool_result (both 'user'
@ -663,7 +665,7 @@ export async function initEnvLessBridgeCore(
transport.reportState('running')
}
logForDebugging(`[remote-bridge] Flushing ${events.length} history events`)
await transport.writeBatch(events)
await transport.writeBatch(events as StdoutMessage[])
}
// ── 9. Teardown ───────────────────────────────────────────────────────────
@ -686,11 +688,11 @@ export async function initEnvLessBridgeCore(
// explicit sleep. close() sets closed=true which interrupts drain at the
// next while-check, so close-before-archive drops the result.
transport.reportState('idle')
const resultMsg: StdoutMessageWithSession = {
const resultMsg = {
...makeResultMessage(sessionId),
session_id: sessionId,
}
void transport.write(resultMsg)
} as unknown as TransportMessage
void transport.write(resultMsg as StdoutMessage)
let token = getAccessToken()
let status = await archiveSession(
sessionId,
@ -809,10 +811,10 @@ export async function initEnvLessBridgeCore(
}
for (const msg of filtered) recentPostedUUIDs.add(msg.uuid)
const events: StdoutMessageWithSession[] = toSDKMessages(filtered).map(m => ({
const events: TransportMessage[] = toSDKMessages(filtered).map(m => ({
...m,
session_id: sessionId,
}))
})) as TransportMessage[]
// v2 does not derive worker_status from events server-side (unlike v1
// session-ingress session_status_updater.go). Push it from here so the
// CCR web session list shows Running instead of stuck on Idle. A user
@ -822,7 +824,7 @@ export async function initEnvLessBridgeCore(
transport.reportState('running')
}
logForDebugging(`[remote-bridge] Sending ${filtered.length} message(s)`)
void transport.writeBatch(events)
void transport.writeBatch(events as StdoutMessage[])
},
writeSdkMessages(messages: SDKMessage[]) {
const filtered = messages.filter(
@ -842,11 +844,11 @@ export async function initEnvLessBridgeCore(
)
return
}
const event: StdoutMessageWithSession = { ...request, session_id: sessionId }
const event: TransportMessage = { ...request, session_id: sessionId } as TransportMessage
if ((request as { request?: { subtype?: string } }).request?.subtype === 'can_use_tool') {
transport.reportState('requires_action')
}
void transport.write(event)
void transport.write(event as StdoutMessage)
logForDebugging(
`[remote-bridge] Sent control_request request_id=${request.request_id}`,
)
@ -858,9 +860,9 @@ export async function initEnvLessBridgeCore(
)
return
}
const event: StdoutMessageWithSession = { ...response, session_id: sessionId }
const event: TransportMessage = { ...response, session_id: sessionId } as TransportMessage
transport.reportState('running')
void transport.write(event)
void transport.write(event as StdoutMessage)
logForDebugging('[remote-bridge] Sent control_response')
},
sendControlCancelRequest(requestId: string) {
@ -870,16 +872,16 @@ export async function initEnvLessBridgeCore(
)
return
}
const event: StdoutMessageWithSession = {
const event: TransportMessage = {
type: 'control_cancel_request' as const,
request_id: requestId,
session_id: sessionId,
}
} as TransportMessage
// Hook/classifier/channel/recheck resolved the permission locally —
// interactiveHandler calls only cancelRequest (no sendResponse) on
// those paths, so without this the server stays on requires_action.
transport.reportState('running')
void transport.write(event)
void transport.write(event as StdoutMessage)
logForDebugging(
`[remote-bridge] Sent control_cancel_request request_id=${requestId}`,
)
@ -890,11 +892,11 @@ export async function initEnvLessBridgeCore(
return
}
transport.reportState('idle')
const resultMsg: StdoutMessageWithSession = {
const resultMsg = {
...makeResultMessage(sessionId),
session_id: sessionId,
}
void transport.write(resultMsg)
} as unknown as TransportMessage
void transport.write(resultMsg as StdoutMessage)
logForDebugging(`[remote-bridge] Sent result`)
},
async teardown() {

View File

@ -57,13 +57,15 @@ import type { StdoutMessage } from '../entrypoints/sdk/controlTypes.js'
import type { SDKResultSuccess } from '../entrypoints/sdk/coreTypes.js'
/**
* StdoutMessage with session_id added. The transport layer adds session_id
* to messages at runtime, but the Zod schemas don't include it. This type
* makes it explicit that we're adding session_id to each message variant.
* StdoutMessage with optional session_id. The transport layer accepts
* StdoutMessage but we add session_id at runtime. Using optional because
* the type system can't verify that adding session_id to a union type
* is always valid, even though it is at runtime.
*
* We need to use 'as StdoutMessage' when passing to transport because
* TypeScript can't verify that objects with session_id are valid StdoutMessage.
*/
type StdoutMessageWithSession = StdoutMessage extends infer T
? T & { session_id: string }
: never
type TransportMessage = StdoutMessage & { session_id?: string }
import { createCapacityWake, type CapacitySignal } from './capacityWake.js'
import { FlushGate } from './flushGate.js'
import {
@ -876,14 +878,14 @@ export async function initBridgeCore(
recentPostedUUIDs.add(msg.uuid)
}
const sdkMessages = toSDKMessages(msgs)
const events: StdoutMessageWithSession[] = sdkMessages.map(sdkMsg => ({
const events: TransportMessage[] = sdkMessages.map(sdkMsg => ({
...sdkMsg,
session_id: currentSessionId,
}))
})) as TransportMessage[]
logForDebugging(
`[bridge:repl] Drained ${msgs.length} pending message(s) after flush`,
)
void transport.writeBatch(events)
void transport.writeBatch(events as StdoutMessage[])
}
// Teardown reference — set after definition below. All callers are async
@ -1296,13 +1298,13 @@ export async function initBridgeCore(
logForDebugging(
`[bridge:repl] Flushing ${sdkMessages.length} initial message(s) via transport`,
)
const events: StdoutMessageWithSession[] = sdkMessages.map(sdkMsg => ({
const events: TransportMessage[] = sdkMessages.map(sdkMsg => ({
...sdkMsg,
session_id: currentSessionId,
}))
})) as TransportMessage[]
const dropsBefore = newTransport.droppedBatchCount
void newTransport
.writeBatch(events)
.writeBatch(events as StdoutMessage[])
.then(() => {
// If any batch was dropped during this flush (SI down for
// maxConsecutiveFailures attempts), flush() still resolved
@ -1666,11 +1668,11 @@ export async function initBridgeCore(
transport = null
flushGate.drop()
if (teardownTransport) {
const resultMsg: StdoutMessageWithSession = {
const resultMsg = {
...makeResultMessage(currentSessionId),
session_id: currentSessionId,
}
void teardownTransport.write(resultMsg)
} as unknown as TransportMessage
void teardownTransport.write(resultMsg as StdoutMessage)
}
const stopWorkP = currentWorkId
@ -1793,11 +1795,11 @@ export async function initBridgeCore(
// Convert to SDK format and send via HTTP POST (HybridTransport).
// The web UI receives them via the subscribe WebSocket.
const sdkMessages = toSDKMessages(filtered)
const events: StdoutMessageWithSession[] = sdkMessages.map(sdkMsg => ({
const events: TransportMessage[] = sdkMessages.map(sdkMsg => ({
...sdkMsg,
session_id: currentSessionId,
}))
void transport.writeBatch(events)
})) as TransportMessage[]
void transport.writeBatch(events as StdoutMessage[])
},
writeSdkMessages(messages) {
// Daemon path: query() already yields SDKMessage, skip conversion.
@ -1818,8 +1820,8 @@ export async function initBridgeCore(
for (const msg of filtered) {
if (msg.uuid) recentPostedUUIDs.add(msg.uuid as string)
}
const events: StdoutMessageWithSession[] = filtered.map(m => ({ ...m, session_id: currentSessionId }))
void transport.writeBatch(events)
const events: TransportMessage[] = filtered.map(m => ({ ...m, session_id: currentSessionId })) as TransportMessage[]
void transport.writeBatch(events as StdoutMessage[])
},
sendControlRequest(request: SDKControlRequest) {
if (!transport) {
@ -1828,8 +1830,8 @@ export async function initBridgeCore(
)
return
}
const event: StdoutMessageWithSession = { ...request, session_id: currentSessionId }
void transport.write(event)
const event: TransportMessage = { ...request, session_id: currentSessionId } as TransportMessage
void transport.write(event as StdoutMessage)
logForDebugging(
`[bridge:repl] Sent control_request request_id=${request.request_id}`,
)
@ -1841,8 +1843,8 @@ export async function initBridgeCore(
)
return
}
const event: StdoutMessageWithSession = { ...response, session_id: currentSessionId }
void transport.write(event)
const event: TransportMessage = { ...response, session_id: currentSessionId } as TransportMessage
void transport.write(event as StdoutMessage)
logForDebugging('[bridge:repl] Sent control_response')
},
sendControlCancelRequest(requestId: string) {
@ -1852,12 +1854,12 @@ export async function initBridgeCore(
)
return
}
const event: StdoutMessageWithSession = {
const event: TransportMessage = {
type: 'control_cancel_request' as const,
request_id: requestId,
session_id: currentSessionId,
}
void transport.write(event)
} as TransportMessage
void transport.write(event as StdoutMessage)
logForDebugging(
`[bridge:repl] Sent control_cancel_request request_id=${requestId}`,
)
@ -1869,11 +1871,11 @@ export async function initBridgeCore(
)
return
}
const resultMsg: StdoutMessageWithSession = {
const resultMsg = {
...makeResultMessage(currentSessionId),
session_id: currentSessionId,
}
void transport.write(resultMsg)
} as unknown as TransportMessage
void transport.write(resultMsg as StdoutMessage)
logForDebugging(
`[bridge:repl] Sent result for session=${currentSessionId}`,
)

View File

@ -22,8 +22,8 @@ export function getCompanionIntroAttachment(
// Skip if already announced for this companion.
for (const msg of messages ?? []) {
if (msg.type !== 'attachment') continue
if (msg.attachment.type !== 'companion_intro') continue
if (msg.attachment.name === companion.name) return []
if (msg.attachment!.type !== 'companion_intro') continue
if (msg.attachment!.name === companion.name) return []
}
return [

View File

@ -1180,7 +1180,7 @@ function runHeadlessStreaming(
removeInterruptedMessage(mutableMessages, turnInterruptionState.message)
enqueue({
mode: 'prompt',
value: turnInterruptionState.message.message.content,
value: turnInterruptionState.message.message!.content as string | ContentBlockParam[],
uuid: randomUUID(),
})
}
@ -1231,13 +1231,13 @@ function runHeadlessStreaming(
output.enqueue({
type: 'user',
content: crumb.message.content,
message: crumb.message,
message: crumb.message as unknown,
session_id: getSessionId(),
parent_tool_use_id: null,
uuid: crumb.uuid,
timestamp: crumb.timestamp,
isReplay: true,
} as SDKUserMessageReplay as StdoutMessage)
} as unknown as StdoutMessage)
}
}
}
@ -1969,12 +1969,12 @@ function runHeadlessStreaming(
output.enqueue({
type: 'user',
content: c.value,
message: { role: 'user', content: c.value },
message: { role: 'user', content: c.value } as unknown,
session_id: getSessionId(),
parent_tool_use_id: null,
uuid: c.uuid as string,
isReplay: true,
} as SDKUserMessageReplay as StdoutMessage)
} as unknown as StdoutMessage)
}
}
}
@ -4090,13 +4090,13 @@ function runHeadlessStreaming(
output.enqueue({
type: 'user',
content: (userMsg.message as { content?: string })?.content ?? '',
message: userMsg.message as { role: string; content: unknown },
message: userMsg.message as unknown,
session_id: sessionId,
parent_tool_use_id: null,
uuid: userMsg.uuid as string,
timestamp: (userMsg as { timestamp?: string }).timestamp,
isReplay: true,
} as unknown as SDKUserMessageReplay as StdoutMessage)
} as unknown as StdoutMessage)
}
// Historical dup = transcript already has this turn's output, so it
// ran but its lifecycle was never closed (interrupted before ack).
@ -4554,7 +4554,7 @@ async function handleRewindFiles(
)
return {
canRewind: true,
filesChanged: diffStats?.filesChanged,
filesChanged: diffStats?.filesChanged ?? [],
insertions: diffStats?.insertions,
deletions: diffStats?.deletions,
}

View File

@ -463,8 +463,8 @@ const loadAllCommands = memoize(async (cwd: string): Promise<Command[]> => {
...bundledSkills,
...builtinPluginSkills,
...skillDirCommands,
...workflowCommands,
...pluginCommands,
...(workflowCommands as Command[]),
...(pluginCommands as Command[]),
...pluginSkills,
...COMMANDS(),
]

3
src/commands/ant-trace/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

3
src/commands/autofix-pr/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

View File

@ -44,7 +44,7 @@ export function deriveFirstPrompt(
typeof content === 'string'
? content
: content.find(
(block): block is { type: 'text'; text: string } =>
(block: { type: string; text?: string }): block is { type: 'text'; text: string } =>
block.type === 'text',
)?.text
if (!raw) return 'Branched conversation'

3
src/commands/break-cache/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

View File

@ -154,7 +154,7 @@ function BridgeDisconnectDialog({ onDone }: Props): React.ReactNode {
type: 'utf8',
errorCorrectionLevel: 'L',
small: true,
})
} as Parameters<typeof qrToString>[1])
.then(setQrText)
.catch(() => setQrText(''))
}, [showQR, displayUrl])

View File

@ -162,7 +162,7 @@ function BtwSideQuestion({
*/
function stripInProgressAssistantMessage(messages: Message[]): Message[] {
const last = messages.at(-1)
if (last?.type === 'assistant' && last.message.stop_reason === null) {
if (last?.type === 'assistant' && last.message!.stop_reason === null) {
return messages.slice(0, -1)
}
return messages

View File

@ -128,7 +128,7 @@ export async function call(
return React.createElement(CompanionCard, {
companion,
lastReaction,
onDone,
onDone: onDone as unknown as Parameters<typeof CompanionCard>[0]['onDone'],
})
}

3
src/commands/bughunter/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

View File

@ -1,5 +1,6 @@
import { feature } from 'bun:bundle'
import chalk from 'chalk'
import type { SDKStatus } from '../../entrypoints/agentSdkTypes.js'
import { markPostCompaction } from 'src/bootstrap/state.js'
import { getSystemPrompt } from '../../constants/prompts.js'
import { getSystemContext, getUserContext } from '../../context.js'
@ -223,7 +224,7 @@ async function compactViaReactive(
context.setStreamMode?.('requesting')
context.setResponseLength?.(() => 0)
context.onCompactProgress?.({ type: 'compact_end' })
context.setSDKStatus?.(null)
context.setSDKStatus?.("" as SDKStatus)
}
}

3
src/commands/ctx_viz/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

3
src/commands/env/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

3
src/commands/good-claude/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

View File

@ -1,4 +1,30 @@
// Auto-generated stub — replace with real implementation
export type Workflow = any;
export type State = any;
export type Warning = any;
export type Workflow = 'claude' | 'claude-review' | string
export type Warning = {
title: string
message: string
instructions: string[]
}
export type State = {
step: string
selectedRepoName: string
currentRepo: string
useCurrentRepo: boolean
apiKeyOrOAuthToken: string
useExistingKey: boolean
currentWorkflowInstallStep: number
warnings: Warning[]
secretExists: boolean
secretName: string
useExistingSecret: boolean
workflowExists: boolean
selectedWorkflows: Workflow[]
selectedApiKeyOption: 'existing' | 'new' | 'oauth'
authType: 'api_key' | 'oauth_token'
workflowAction?: string
error?: string
errorReason?: string
errorInstructions?: string[]
[key: string]: unknown
}

3
src/commands/issue/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

3
src/commands/mock-limits/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

3
src/commands/oauth-refresh/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

3
src/commands/onboarding/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

3
src/commands/perf-issue/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

View File

@ -25,7 +25,9 @@ function getEnvVarForProvider(provider: string): string {
// Get merged env: process.env + settings.env (from userSettings)
function getMergedEnv(): Record<string, string> {
const settings = getSettings_DEPRECATED()
const merged = { ...process.env }
const merged: Record<string, string> = Object.fromEntries(
Object.entries(process.env).filter((e): e is [string, string] => e[1] !== undefined)
)
if (settings?.env) {
Object.assign(merged, settings.env)
}

View File

@ -8,7 +8,7 @@ import { BRIDGE_LOGIN_INSTRUCTION } from '../../bridge/types.js';
import { Dialog } from '../../components/design-system/Dialog.js';
import { ListItem } from '../../components/design-system/ListItem.js';
import { useRegisterOverlay } from '../../context/overlayContext.js';
import { Box, Text } from '../../ink.js';
import { Box, Text } from '@anthropic/ink';
import { useKeybindings } from '../../keybindings/useKeybinding.js';
import type { ToolUseContext } from '../../Tool.js';
import type { LocalJSXCommandContext, LocalJSXCommandOnDone } from '../../types/command.js';

View File

@ -23,6 +23,7 @@ import {
formatPreconditionError,
getRemoteTaskSessionUrl,
registerRemoteAgentTask,
type BackgroundRemoteSessionPrecondition,
} from '../../tasks/RemoteAgentTask/RemoteAgentTask.js'
import { isEnterpriseSubscriber, isTeamSubscriber } from '../../utils/auth.js'
import { detectCurrentRepositoryWithHost } from '../../utils/detectRepository.js'
@ -147,7 +148,7 @@ export async function launchRemoteReview(
',',
) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
})
const reasons = blockers.map(formatPreconditionError).join('\n')
const reasons = (blockers as BackgroundRemoteSessionPrecondition[]).map(formatPreconditionError).join('\n')
return [
{
type: 'text',

3
src/commands/share/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

3
src/commands/summary/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

3
src/commands/teleport/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import type { Command } from '../../types/command.js'
declare const _default: Command
export default _default

View File

@ -64,7 +64,7 @@ export function BridgeDialog({ onDone }: Props): React.ReactNode {
return
}
qrToString(displayUrl, {
type: 'utf8',
type: 'terminal',
errorCorrectionLevel: 'L',
small: true,
})

View File

@ -613,7 +613,7 @@ async function generateTitle(
},
})
const _firstBlock = response.message.content[0] as unknown as Record<string, unknown> | undefined
const _firstBlock = response?.message?.content?.[0] as unknown as Record<string, unknown> | undefined
const title =
_firstBlock?.type === 'text'
? (_firstBlock.text as string)

View File

@ -35,7 +35,7 @@ function hasMemoryFileRead(messages: Message[]): boolean {
if (message.type !== 'assistant') {
continue
}
const content = message.message.content
const content = message.message!.content
if (!Array.isArray(content)) {
continue
}

View File

@ -242,8 +242,8 @@ export function countUnseenAssistantTurns(
function assistantHasVisibleText(m: Message): boolean {
if (m.type !== 'assistant') return false
if (!Array.isArray(m.message.content)) return false
for (const b of m.message.content) {
if (!Array.isArray(m.message!.content)) return false
for (const b of m.message!.content) {
if (typeof b !== 'string' && b.type === 'text' && b.text.trim() !== '') return true
}
return false

View File

@ -748,9 +748,9 @@ function UserMessageOption({
)
}
const content = userMessage.message.content
const content = userMessage.message!.content
const lastBlock =
typeof content === 'string' ? null : content[content.length - 1]
typeof content === 'string' ? null : content![content!.length - 1]
const rawMessageText =
typeof content === 'string'
? content.trim()
@ -897,8 +897,8 @@ export function selectableUserMessagesFilter(
return false
}
if (
Array.isArray(message.message.content) &&
message.message.content[0]?.type === 'tool_result'
Array.isArray(message.message!.content) &&
message.message!.content[0]?.type === 'tool_result'
) {
return false
}
@ -912,9 +912,9 @@ export function selectableUserMessagesFilter(
return false
}
const content = message.message.content
const content = message.message!.content
const lastBlock =
typeof content === 'string' ? null : content[content.length - 1]
typeof content === 'string' ? null : content![content!.length - 1]
const messageText =
typeof content === 'string'
? content.trim()
@ -960,7 +960,7 @@ export function messagesAfterAreOnlySynthetic(
// Assistant with actual content = meaningful
if (msg.type === 'assistant') {
const content = msg.message.content
const content = msg.message!.content
if (Array.isArray(content)) {
const hasMeaningfulContent = content.some(
block =>

View File

@ -15,7 +15,7 @@ export function MessageTimestamp({
isTranscriptMode &&
message.timestamp &&
message.type === 'assistant' &&
(Array.isArray(message.message.content) ? (message.message.content as {type: string}[]).some(c => c.type === 'text') : false)
(Array.isArray(message.message!.content) ? (message.message!.content as {type: string}[]).some(c => c.type === 'text') : false)
if (!shouldShowTimestamp) {
return null

View File

@ -460,7 +460,7 @@ const MessagesImpl = ({
for (let i = normalizedMessages.length - 1; i >= 0; i--) {
const msg = normalizedMessages[i]
if (msg?.type === 'assistant') {
const content = msg.message.content as Array<{ type: string }>
const content = msg.message!.content as Array<{ type: string }>
// Find the last thinking block in this message
for (let j = content.length - 1; j >= 0; j--) {
if (content[j]?.type === 'thinking') {
@ -468,7 +468,7 @@ const MessagesImpl = ({
}
}
} else if (msg?.type === 'user') {
const content = msg.message.content as Array<{ type: string }>
const content = msg.message!.content as Array<{ type: string }>
const hasToolResult = content.some(
block => block.type === 'tool_result',
)
@ -488,7 +488,7 @@ const MessagesImpl = ({
for (let i = normalizedMessages.length - 1; i >= 0; i--) {
const msg = normalizedMessages[i]
if (msg?.type === 'user') {
const content = msg.message.content as Array<{ type: string; text?: string }>
const content = msg.message!.content as Array<{ type: string; text?: string }>
// Check if any text content is bash output
for (const block of content) {
if (block.type === 'text') {
@ -741,7 +741,8 @@ const MessagesImpl = ({
(msg: RenderableMessage): boolean => {
if (msg.type === 'collapsed_read_search') return true
if (msg.type === 'assistant') {
const b = msg.message.content[0] as unknown as AdvisorBlock | undefined
const content = msg.message!.content
const b = (Array.isArray(content) ? content[0] : undefined) as unknown as AdvisorBlock | undefined
return (
b != null &&
isAdvisorBlock(b) &&
@ -750,11 +751,11 @@ const MessagesImpl = ({
)
}
if (msg.type !== 'user') return false
const b = (msg.message.content as Array<{ type: string; tool_use_id?: string; is_error?: boolean; [key: string]: unknown }>)[0]
const b = (msg.message!.content as Array<{ type: string; tool_use_id?: string; is_error?: boolean; [key: string]: unknown }>)[0]
if (b?.type !== 'tool_result' || b.is_error || !msg.toolUseResult)
return false
const name = lookupsRef.current.toolUseByToolUseID.get(
b.tool_use_id,
b.tool_use_id ?? '',
)?.name
const tool = name ? findToolByName(tools, name) : undefined
return tool?.isResultTruncated?.(msg.toolUseResult as never) ?? false
@ -1111,7 +1112,7 @@ export function shouldRenderStatically(
case 'user':
case 'assistant': {
if (message.type === 'assistant') {
const block = (message.message.content as Array<{ type: string; id?: string }>)[0]
const block = (message.message!.content as Array<{ type: string; id?: string }>)[0]
if (block?.type === 'server_tool_use') {
return lookups.resolvedToolUseIDs.has(block.id!)
}
@ -1142,7 +1143,7 @@ export function shouldRenderStatically(
}
case 'grouped_tool_use': {
const allResolved = message.messages.every(msg => {
const content = (msg.message.content as Array<{ type: string; id?: string }>)[0]
const content = (msg.message!.content as Array<{ type: string; id?: string }>)[0]
return (
content?.type === 'tool_use' &&
lookups.resolvedToolUseIDs.has(content.id!)
@ -1155,5 +1156,7 @@ export function shouldRenderStatically(
// (In transcript mode, we already returned true at the top of this function)
return false
}
default:
return true
}
}

View File

@ -3140,8 +3140,8 @@ function getInitialPasteId(messages: Message[]): number {
}
}
// Check text paste references in message content
if (Array.isArray(message.message.content)) {
for (const block of message.message.content) {
if (Array.isArray(message.message!.content)) {
for (const block of message.message!.content) {
if (block.type === 'text') {
const refs = parseReferences(block.text)
for (const ref of refs) {

View File

@ -1,5 +1,5 @@
import type { CustomAgentDefinition } from '@claude-code-best/builtin-tools/tools/AgentTool/loadAgentsDir.js'
import type { AgentMemoryScope } from '@claude-code-best/builtin-tools/tools/AgentTool/agentMemory.js'
import type { AgentMemoryScope } from '../../../tools/AgentTool/agentMemory.js'
import type { CustomAgentDefinition } from '../../../tools/AgentTool/loadAgentsDir.js'
import type { SettingSource } from '../../../utils/settings/constants.js'
/**

View File

@ -87,6 +87,7 @@ export function isNavigableMessage(msg: NavigableMessage): boolean {
}
return false
}
return false
}
type PrimaryInput = {
@ -395,6 +396,7 @@ export function copyTextOf(msg: NavigableMessage): string {
return `[${a.type}]`
}
}
return ''
}
function toolResultText(r: NormalizedUserMessage): string {

View File

@ -63,6 +63,6 @@ export function isNullRenderingAttachment(
): boolean {
return (
msg.type === 'attachment' &&
NULL_RENDERING_ATTACHMENT_TYPES.has(msg.attachment.type as Attachment['type'])
NULL_RENDERING_ATTACHMENT_TYPES.has(msg.attachment!.type as Attachment['type'])
)
}

View File

@ -53,7 +53,7 @@ export function FallbackPermissionRequest({
event: 'accept',
metadata: {
language_name: 'none',
message_id: toolUseConfirm.assistantMessage.message.id,
message_id: toolUseConfirm.assistantMessage.message.id!,
platform: env.platform,
},
})
@ -66,7 +66,7 @@ export function FallbackPermissionRequest({
event: 'accept',
metadata: {
language_name: 'none',
message_id: toolUseConfirm.assistantMessage.message.id,
message_id: toolUseConfirm.assistantMessage.message.id!,
platform: env.platform,
},
})
@ -92,7 +92,7 @@ export function FallbackPermissionRequest({
event: 'reject',
metadata: {
language_name: 'none',
message_id: toolUseConfirm.assistantMessage.message.id,
message_id: toolUseConfirm.assistantMessage.message.id!,
platform: env.platform,
},
})
@ -111,7 +111,7 @@ export function FallbackPermissionRequest({
event: 'reject',
metadata: {
language_name: 'none',
message_id: toolUseConfirm.assistantMessage.message.id,
message_id: toolUseConfirm.assistantMessage.message.id!,
platform: env.platform,
},
})

View File

@ -89,7 +89,7 @@ export function useFilePermissionDialog<T extends ToolInput>({
const onChange = useCallback(
(option: PermissionOption, input: T, feedback?: string) => {
const params: PermissionHandlerParams = {
messageId: toolUseConfirm.assistantMessage.message.id,
messageId: toolUseConfirm.assistantMessage.message.id!,
path: filePath,
toolUseConfirm,
toolPermissionContext,

View File

@ -3,6 +3,7 @@ import * as React from 'react'
import { Suspense, use, useMemo } from 'react'
import { Box, NoSelect, Text } from '@anthropic/ink'
import type {
NotebookCell,
NotebookCellType,
NotebookContent,
} from '../../../types/notebook.js'
@ -79,7 +80,7 @@ function NotebookEditToolDiffInner({
}
return ''
}
const cell = notebookData.cells.find(cell => cell.id === cell_id)
const cell = notebookData.cells.find((cell: NotebookCell) => cell.id === cell_id)
if (!cell) {
return ''
}

View File

@ -129,7 +129,7 @@ export function SkillPermissionRequest(
event: 'accept',
metadata: {
language_name: 'none',
message_id: toolUseConfirm.assistantMessage.message.id,
message_id: toolUseConfirm.assistantMessage.message.id!,
platform: env.platform,
},
})
@ -142,7 +142,7 @@ export function SkillPermissionRequest(
event: 'accept',
metadata: {
language_name: 'none',
message_id: toolUseConfirm.assistantMessage.message.id,
message_id: toolUseConfirm.assistantMessage.message.id!,
platform: env.platform,
},
})
@ -169,7 +169,7 @@ export function SkillPermissionRequest(
event: 'accept',
metadata: {
language_name: 'none',
message_id: toolUseConfirm.assistantMessage.message.id,
message_id: toolUseConfirm.assistantMessage.message.id!,
platform: env.platform,
},
})
@ -201,7 +201,7 @@ export function SkillPermissionRequest(
event: 'reject',
metadata: {
language_name: 'none',
message_id: toolUseConfirm.assistantMessage.message.id,
message_id: toolUseConfirm.assistantMessage.message.id!,
platform: env.platform,
},
})
@ -220,7 +220,7 @@ export function SkillPermissionRequest(
event: 'reject',
metadata: {
language_name: 'none',
message_id: toolUseConfirm.assistantMessage.message.id,
message_id: toolUseConfirm.assistantMessage.message.id!,
platform: env.platform,
},
})

View File

@ -201,7 +201,7 @@ export function usePermissionRequestLogging(
event: 'response',
metadata: {
language_name: unaryEvent.language_name,
message_id: toolUseConfirm.assistantMessage.message.id,
message_id: toolUseConfirm.assistantMessage.message.id!,
platform: env.platform,
},
})

View File

@ -1,7 +1,7 @@
import * as React from 'react'
import { useCallback, useEffect, useState } from 'react'
// eslint-disable-next-line custom-rules/prefer-use-keybindings -- 'r' is a view-specific key, not a global keybinding
import { Box, Text, useInput } from '@anthropic/ink'
import { Box, Text, useInput, useTabHeaderFocus } from '@anthropic/ink'
import {
type AutoModeDenial,
getAutoModeDenials,

View File

@ -4,7 +4,7 @@ import { useCallback, useEffect } from 'react'
import { getOriginalCwd } from '../../../bootstrap/state.js'
import type { CommandResultDisplay } from '../../../commands.js'
import { Select } from '../../../components/CustomSelect/select.js'
import { Box, Text } from '@anthropic/ink'
import { Box, Text, useTabHeaderFocus } from '@anthropic/ink'
import type { ToolPermissionContext } from '../../../Tool.js'
type Props = {

View File

@ -17,7 +17,7 @@ export function logUnaryPermissionEvent(
event,
metadata: {
language_name: 'none',
message_id,
message_id: message_id!,
platform: getHostPlatformForAnalytics(),
hasFeedback: hasFeedback ?? false,
},

View File

@ -1,5 +1,6 @@
import { feature } from 'bun:bundle'
import figures from 'figures'
import type { AgentId } from '../../types/ids.js'
import React, {
type ReactNode,
useEffect,
@ -535,12 +536,12 @@ export function BackgroundTasksDialog({
}
onSkipAgent={
task.status === 'running' && skipWorkflowAgent
? agentId => skipWorkflowAgent(task.id, agentId, setAppState)
? (agentId: AgentId) => skipWorkflowAgent(task.id, agentId, setAppState)
: undefined
}
onRetryAgent={
task.status === 'running' && retryWorkflowAgent
? agentId => retryWorkflowAgent(task.id, agentId, setAppState)
? (agentId: AgentId) => retryWorkflowAgent(task.id, agentId, setAppState)
: undefined
}
onBack={goBackToList}

View File

@ -23,7 +23,7 @@ export function WizardProvider<T extends Record<string, unknown>>({
initialData?: T;
onComplete: (data: T) => void;
onCancel: () => void;
children: ReactNode;
children?: ReactNode;
title: string;
showStepCounter?: boolean;
}): ReactNode {

View File

@ -87,7 +87,7 @@ export async function launchAssistantSessionChooser(
return showSetupDialog<string | null>(root, done => (
<AssistantSessionChooser
sessions={props.sessions}
onSelect={id => done(id)}
onSelect={(id: string) => done(id)}
onCancel={() => done(null)}
/>
))

View File

@ -336,14 +336,22 @@ export const PermissionResultSchema = lazySchema(() =>
export const PermissionModeSchema = lazySchema(() =>
z
.enum(['default', 'acceptEdits', 'bypassPermissions', 'plan', 'dontAsk'])
.enum([
'default',
'acceptEdits',
'bypassPermissions',
'plan',
'dontAsk',
'auto',
])
.describe(
'Permission mode for controlling how tool executions are handled. ' +
"'default' - Standard behavior, prompts for dangerous operations. " +
"'acceptEdits' - Auto-accept file edit operations. " +
"'bypassPermissions' - Bypass all permission checks (requires allowDangerouslySkipPermissions). " +
"'plan' - Planning mode, no actual tool execution. " +
"'dontAsk' - Don't prompt for permissions, deny if not pre-approved.",
"'dontAsk' - Don't prompt for permissions, deny if not pre-approved. " +
"'auto' - Automatic mode (transcript classifier).",
),
)

View File

@ -102,7 +102,7 @@ function createPermissionContext(
setToolPermissionContext: (context: ToolPermissionContext) => void,
queueOps?: PermissionQueueOps,
) {
const messageId = assistantMessage.message.id
const messageId = assistantMessage.message.id!
const ctx = {
tool,
input,
@ -234,7 +234,7 @@ function createPermissionContext(
const finalInput = decision.updatedInput ?? updatedInput ?? input
return await this.handleHookAllow(
finalInput,
decision.updatedPermissions ?? [],
(decision.updatedPermissions ?? []) as unknown as import('../../types/permissions.js').PermissionUpdate[],
permissionPromptStartTimeMs,
)
} else if (decision.behavior === 'deny') {

View File

@ -152,7 +152,7 @@ function useCanUseTool(
tool,
input,
toolUseContext,
messageId: ctx.messageId,
messageId: ctx.messageId!,
toolUseID,
},
{ decision: 'reject', source: 'config' },

View File

@ -47,7 +47,7 @@ export function isSessionContainerCompatible(messages: Message[]): boolean {
if (msg.type !== 'assistant') {
continue
}
const content = msg.message.content
const content = msg.message!.content
if (!Array.isArray(content)) {
continue
}

View File

@ -69,7 +69,7 @@ function countHunkLines(hunks: StructuredPatchHunk[]): {
function getUserPromptPreview(message: Message): string {
if (message.type !== 'user') return ''
const content = message.message.content
const content = message.message!.content
const text = typeof content === 'string' ? content : ''
// Truncate to ~30 chars
if (text.length <= 30) return text
@ -124,8 +124,8 @@ export function useTurnDiffs(messages: Message[]): TurnDiff[] {
// Check if this is a user prompt (not a tool result)
const isToolResult =
message.toolUseResult ||
(Array.isArray(message.message.content) &&
message.message.content[0]?.type === 'tool_result')
(Array.isArray(message.message!.content) &&
message.message!.content[0]?.type === 'tool_result')
if (!isToolResult && !message.isMeta) {
// Start a new turn on user prompt

View File

@ -4460,7 +4460,7 @@ async function run(): Promise<CommanderCommand> {
...(uploaderReady && {
onTurnComplete: (messages: MessageType[]) => {
void uploaderReady.then((uploader) =>
uploader?.(messages),
(uploader as ((msgs: MessageType[]) => void) | null)?.(messages),
);
},
}),
@ -4618,13 +4618,13 @@ async function run(): Promise<CommanderCommand> {
createLocalSSHSession,
SSHSessionError,
} = await import("./ssh/createSSHSession.js");
let sshSession;
let sshSession: import('./ssh/createSSHSession.js').SSHSession | undefined;
try {
if (_pendingSSH.local) {
process.stderr.write(
"Starting local ssh-proxy test session...\n",
);
sshSession = createLocalSSHSession({
sshSession = await createLocalSSHSession({
cwd: _pendingSSH.cwd,
permissionMode: _pendingSSH.permissionMode,
dangerouslySkipPermissions:
@ -4651,7 +4651,7 @@ async function run(): Promise<CommanderCommand> {
},
isTTY
? {
onProgress: (msg) => {
onProgress: (msg: string) => {
hadProgress = true;
process.stderr.write(
`\r ${msg}\x1b[K`,

View File

@ -803,7 +803,7 @@ async function* queryLoop(
if (
contextCollapse?.isWithheldPromptTooLong(
message as Message,
isPromptTooLongMessage,
isPromptTooLongMessage as (msg: Message) => boolean,
querySource,
)
) {
@ -1084,7 +1084,7 @@ async function* queryLoop(
// prevents a spiral and the error surfaces.
const isWithheldMedia =
mediaRecoveryEnabled &&
reactiveCompact?.isWithheldMediaSizeError(lastMessage)
reactiveCompact?.isWithheldMediaSizeError(lastMessage as Message)
if (isWithheld413) {
// First: drain all staged context-collapses. Gated on the PREVIOUS
// transition not being collapse_drain_retry — if we already drained
@ -1173,8 +1173,8 @@ async function* queryLoop(
// so hooks have nothing meaningful to evaluate. Running stop hooks
// on prompt-too-long creates a death spiral: error → hook blocking
// → retry → error → … (the hook injects more tokens each cycle).
yield lastMessage
void executeStopFailureHooks(lastMessage, toolUseContext)
yield lastMessage!
void executeStopFailureHooks(lastMessage!, toolUseContext)
return { reason: isWithheldMedia ? 'image_error' : 'prompt_too_long' }
} else if (feature('CONTEXT_COLLAPSE') && isWithheld413) {
// reactiveCompact compiled out but contextCollapse withheld and
@ -1390,7 +1390,7 @@ async function* queryLoop(
if (
update.message.type === 'attachment' &&
update.message.attachment.type === 'hook_stopped_continuation'
update.message.attachment!.type === 'hook_stopped_continuation'
) {
shouldPreventContinuation = true
}

View File

@ -148,7 +148,7 @@ export async function* handleStopHooks(
// but before gracefulShutdownSync (see drainPendingExtraction).
void extractMemoriesModule!.executeExtractMemories(
stopHookContext,
toolUseContext.appendSystemMessage,
toolUseContext.appendSystemMessage as ((msg: import('../types/message.js').SystemMessage) => void) | undefined,
)
}
if (!toolUseContext.agentId) {
@ -215,7 +215,7 @@ export async function* handleStopHooks(
}
// Track errors and output from attachments
if (result.message.type === 'attachment') {
const attachment = result.message.attachment
const attachment = result.message.attachment!
if (
'hookEvent' in attachment &&
(attachment.hookEvent === 'Stop' ||

View File

@ -32,8 +32,8 @@ import { createUserMessage } from '../utils/messages.js'
function convertAssistantMessage(msg: SDKAssistantMessage): AssistantMessage {
return {
type: 'assistant',
message: msg.message,
uuid: msg.uuid,
message: msg.message!,
uuid: msg.uuid!,
requestId: undefined,
timestamp: new Date().toISOString(),
error: msg.error,
@ -64,7 +64,7 @@ function convertResultMessage(msg: SDKResultMessage): SystemMessage {
subtype: 'informational',
content,
level: isError ? 'warning' : 'info',
uuid: msg.uuid,
uuid: msg.uuid!,
timestamp: new Date().toISOString(),
}
}
@ -78,7 +78,7 @@ function convertInitMessage(msg: SDKSystemMessage): SystemMessage {
subtype: 'informational',
content: `Remote session initialized (model: ${msg.model})`,
level: 'info',
uuid: msg.uuid,
uuid: msg.uuid!,
timestamp: new Date().toISOString(),
}
}
@ -99,7 +99,7 @@ function convertStatusMessage(msg: SDKStatusMessage): SystemMessage | null {
? 'Compacting conversation…'
: `Status: ${msg.status}`,
level: 'info',
uuid: msg.uuid,
uuid: msg.uuid!,
timestamp: new Date().toISOString(),
}
}
@ -117,7 +117,7 @@ function convertToolProgressMessage(
subtype: 'informational',
content: `Tool ${msg.tool_name} running for ${msg.elapsed_time_seconds}s…`,
level: 'info',
uuid: msg.uuid,
uuid: msg.uuid!,
timestamp: new Date().toISOString(),
toolUseID: msg.tool_use_id,
}
@ -134,7 +134,7 @@ function convertCompactBoundaryMessage(
subtype: 'compact_boundary',
content: 'Conversation compacted',
level: 'info',
uuid: msg.uuid,
uuid: msg.uuid!,
timestamp: new Date().toISOString(),
compactMetadata: fromSDKCompactMetadata(msg.compact_metadata),
}

View File

@ -2978,11 +2978,11 @@ export function REPL({
for (const m of messagesRef.current) {
if (
m.type === 'attachment' &&
m.attachment.type === 'queued_command' &&
m.attachment.commandMode === 'task-notification' &&
typeof m.attachment.prompt === 'string'
m.attachment!.type === 'queued_command' &&
m.attachment!.commandMode === 'task-notification' &&
typeof m.attachment!.prompt === 'string'
) {
existingPrompts.add(m.attachment.prompt);
existingPrompts.add(m.attachment!.prompt);
}
}
const uniqueNotifications = notificationMessages.filter(
@ -3160,7 +3160,7 @@ export function REPL({
// title silently fell through to the "Claude Code" default.
if (!titleDisabled && !sessionTitle && !agentTitle && !haikuTitleAttemptedRef.current) {
const firstUserMessage = newMessages.find(m => m.type === 'user' && !m.isMeta);
const text = firstUserMessage?.type === 'user' ? getContentText(firstUserMessage.message.content) : null;
const text = firstUserMessage?.type === 'user' ? getContentText(firstUserMessage.message!.content as string | ContentBlockParam[]) : null;
// Skip synthetic breadcrumbs — slash-command output, prompt-skill
// expansions (/commit → <command-message>), local-command headers
// (/help → <command-name>), and bash-mode (!cmd → <bash-input>).
@ -3423,7 +3423,7 @@ export function REPL({
// replayed as user-visible text.
newMessages
.filter((m): m is UserMessage => m.type === 'user' && !m.isMeta)
.map(_ => getContentText(_.message.content))
.map(_ => getContentText(_.message.content as string | ContentBlockParam[]))
.filter(_ => _ !== null)
.forEach((msg, i) => {
enqueue({ value: msg, mode: 'prompt' });
@ -3662,13 +3662,13 @@ export function REPL({
...prev,
initialMessage: null,
toolPermissionContext: updatedToolPermissionContext,
...(shouldStorePlanForVerification && {
...(shouldStorePlanForVerification ? {
pendingPlanVerification: {
plan: initialMsg.message.planContent as string,
verificationStarted: false,
verificationCompleted: false,
},
}),
} : {}),
};
});
@ -4385,7 +4385,7 @@ export function REPL({
const newPastedContents: Record<number, PastedContent> = {};
imageBlocks.forEach((block, index) => {
if (block.source.type === 'base64') {
const id = message.imagePasteIds?.[index] ?? index + 1;
const id = (message.imagePasteIds as number[] | undefined)?.[index] ?? index + 1;
newPastedContents[id] = {
id,
type: 'image',
@ -4884,7 +4884,7 @@ export function REPL({
// Count completed hooks
const completedCount = count(messages, m => {
if (m.type !== 'attachment') return false;
const attachment = m.attachment;
const attachment = m.attachment!;
return (
'hookEvent' in attachment &&
(attachment.hookEvent === 'Stop' || attachment.hookEvent === 'SubagentStop') &&

View File

@ -130,7 +130,7 @@ export function startAgentSummarization(
)
continue
}
const contentArr = Array.isArray(msg.message.content) ? msg.message.content : []
const contentArr = Array.isArray(msg.message!.content) ? msg.message!.content : []
const textBlock = contentArr.find(b => b.type === 'text')
if (textBlock?.type === 'text' && textBlock.text.trim()) {
const summaryText = textBlock.text.trim()

View File

@ -243,11 +243,11 @@ export function getParentCacheSuppressReason(
): string | null {
if (!lastAssistantMessage) return null
const usage = lastAssistantMessage.message.usage
const inputTokens = usage.input_tokens ?? 0
const cacheWriteTokens = usage.cache_creation_input_tokens ?? 0
const usage = lastAssistantMessage.message!.usage
const inputTokens = usage!.input_tokens ?? 0
const cacheWriteTokens = usage!.cache_creation_input_tokens ?? 0
// The fork re-processes the parent's output (never cached) plus its own prompt.
const outputTokens = usage.output_tokens ?? 0
const outputTokens = usage!.output_tokens ?? 0
return (inputTokens as number) + (cacheWriteTokens as number) + (outputTokens as number) >
MAX_PARENT_UNCACHED_TOKENS
@ -339,7 +339,7 @@ export async function generateSuggestion(
for (const msg of result.messages) {
if (msg.type !== 'assistant') continue
const contentArr = Array.isArray(msg.message.content) ? msg.message.content as Array<{ type: string; text?: string }> : []
const contentArr = Array.isArray(msg.message!.content) ? msg.message!.content as Array<{ type: string; text?: string }> : []
const textBlock = contentArr.find(b => b.type === 'text')
if (textBlock?.type === 'text' && typeof textBlock.text === 'string') {
const suggestion = textBlock.text.trim()

View File

@ -197,7 +197,7 @@ function getBoundaryDetail(
function isUserMessageWithArrayContent(
m: Message,
): m is Message & { message: { content: unknown[] } } {
return m.type === 'user' && 'message' in m && Array.isArray(m.message.content)
return m.type === 'user' && 'message' in m && Array.isArray(m.message?.content)
}
export function prepareMessagesForInjection(messages: Message[]): Message[] {
@ -254,9 +254,9 @@ export function prepareMessagesForInjection(messages: Message[]): Message[] {
return messages
.map(msg => {
if (!('message' in msg) || !Array.isArray(msg.message.content)) return msg
const content = msg.message.content.filter(keep)
if (content.length === msg.message.content.length) return msg
if (!('message' in msg) || !Array.isArray(msg.message?.content)) return msg
const content = msg.message!.content.filter(keep)
if (content.length === msg.message!.content.length) return msg
if (content.length === 0) return null
// Drop messages where all remaining blocks are whitespace-only text
// (API rejects these with 400: "text content blocks must contain non-whitespace text")

View File

@ -121,7 +121,7 @@ function countToolCallsSince(
}
if (message.type === 'assistant') {
const content = message.message.content
const content = message.message!.content
if (Array.isArray(content)) {
toolCallCount += count(content, block => block.type === 'tool_use')
}

View File

@ -599,13 +599,13 @@ export function userMessageToMessageParam(
querySource?: QuerySource,
): MessageParam {
if (addCache) {
if (typeof message.message.content === 'string') {
if (typeof message.message!.content === 'string') {
return {
role: 'user',
content: [
{
type: 'text',
text: message.message.content,
text: message.message!.content,
...(enablePromptCaching && {
cache_control: getCacheControl({ querySource }),
}),
@ -615,9 +615,9 @@ export function userMessageToMessageParam(
} else {
return {
role: 'user',
content: message.message.content.map((_, i) => ({
content: message.message!.content!.map((_, i) => ({
..._,
...(i === message.message.content.length - 1
...(i === message.message!.content!.length - 1
? enablePromptCaching
? { cache_control: getCacheControl({ querySource }) }
: {}
@ -631,9 +631,9 @@ export function userMessageToMessageParam(
// to addCacheBreakpoints share the same array and each splices in duplicate cache_edits.
return {
role: 'user',
content: Array.isArray(message.message.content)
? [...message.message.content]
: message.message.content,
content: (Array.isArray(message.message!.content)
? [...message.message!.content]
: message.message!.content) as import('@anthropic-ai/sdk/resources/beta/messages/messages.js').BetaContentBlockParam[],
}
}
@ -644,13 +644,13 @@ export function assistantMessageToMessageParam(
querySource?: QuerySource,
): MessageParam {
if (addCache) {
if (typeof message.message.content === 'string') {
if (typeof message.message!.content === 'string') {
return {
role: 'assistant',
content: [
{
type: 'text',
text: message.message.content,
text: message.message!.content,
...(enablePromptCaching && {
cache_control: getCacheControl({ querySource }),
}),
@ -660,11 +660,11 @@ export function assistantMessageToMessageParam(
} else {
return {
role: 'assistant',
content: message.message.content.map((_, i) => {
content: message.message!.content!.map((_, i) => {
const contentBlock = stripGeminiProviderMetadata(_)
return {
...contentBlock,
...(i === message.message.content.length - 1 &&
...(i === message.message!.content!.length - 1 &&
contentBlock.type !== 'thinking' &&
contentBlock.type !== 'redacted_thinking' &&
(feature('CONNECTOR_TEXT')
@ -682,9 +682,9 @@ export function assistantMessageToMessageParam(
return {
role: 'assistant',
content:
typeof message.message.content === 'string'
? message.message.content
: message.message.content.map(stripGeminiProviderMetadata) as BetaContentBlockParam[],
typeof message.message!.content === 'string'
? message.message!.content
: message.message!.content!.map(stripGeminiProviderMetadata) as BetaContentBlockParam[],
}
}
@ -992,8 +992,8 @@ export function stripExcessMediaItems(
): (UserMessage | AssistantMessage)[] {
let toRemove = 0
for (const msg of messages) {
if (!Array.isArray(msg.message.content)) continue
for (const block of msg.message.content) {
if (!Array.isArray(msg.message!.content)) continue
for (const block of msg.message!.content) {
if (isMedia(block)) toRemove++
if (isToolResult(block) && Array.isArray(block.content)) {
for (const nested of block.content) {
@ -1007,7 +1007,7 @@ export function stripExcessMediaItems(
return messages.map(msg => {
if (toRemove <= 0) return msg
const content = msg.message.content
const content = msg.message!.content
if (!Array.isArray(content)) return msg
const before = toRemove

View File

@ -65,7 +65,7 @@ export function isPromptTooLongMessage(msg: AssistantMessage): boolean {
if (!msg.isApiErrorMessage) {
return false
}
const content = msg.message.content
const content = msg.message!.content
if (!Array.isArray(content)) {
return false
}
@ -230,7 +230,7 @@ function logToolUseToolResultMismatch(
for (let i = 0; i < messagesForAPI.length; i++) {
const msg = messagesForAPI[i]
if (!msg) continue
const content = msg.message.content
const content = msg.message!.content
if (Array.isArray(content)) {
for (const block of content) {
if (
@ -252,7 +252,7 @@ function logToolUseToolResultMismatch(
const msg = messages[i]
if (!msg) continue
if (msg.type === 'assistant' && 'message' in msg) {
const content = msg.message.content
const content = msg.message!.content
if (Array.isArray(content)) {
for (const block of content) {
if (
@ -274,10 +274,10 @@ function logToolUseToolResultMismatch(
for (let i = normalizedIndex + 1; i < messagesForAPI.length; i++) {
const msg = messagesForAPI[i]
if (!msg) continue
const content = msg.message.content
const content = msg.message!.content
if (Array.isArray(content)) {
for (const block of content) {
const role = msg.message.role
const role = msg.message!.role
if (block.type === 'tool_use' && 'id' in block) {
normalizedSeq.push(`${role}:tool_use:${block.id}`)
} else if (block.type === 'tool_result' && 'tool_use_id' in block) {
@ -307,10 +307,10 @@ function logToolUseToolResultMismatch(
case 'user':
case 'assistant': {
if ('message' in msg) {
const content = msg.message.content
const content = msg.message!.content
if (Array.isArray(content)) {
for (const block of content) {
const role = msg.message.role
const role = msg.message!.role
if (block.type === 'tool_use' && 'id' in block) {
preNormalizedSeq.push(`${role}:tool_use:${block.id}`)
} else if (
@ -331,14 +331,14 @@ function logToolUseToolResultMismatch(
}
}
} else if (typeof content === 'string') {
preNormalizedSeq.push(`${msg.message.role}:string_content`)
preNormalizedSeq.push(`${msg.message!.role}:string_content`)
}
}
break
}
case 'attachment':
if ('attachment' in msg) {
preNormalizedSeq.push(`attachment:${msg.attachment.type}`)
preNormalizedSeq.push(`attachment:${msg.attachment!.type}`)
}
break
case 'system':

View File

@ -84,7 +84,7 @@ function convertInternalUserMessage(
return {
role: 'user',
parts: content.flatMap(block =>
convertUserContentBlockToGeminiParts(block, toolNamesById),
convertUserContentBlockToGeminiParts(block as unknown as string | Record<string, unknown>, toolNamesById),
),
}
}

View File

@ -45,9 +45,8 @@ export async function* adaptGeminiStreamToAnthropic(
cache_read_input_tokens: 0,
},
},
} as BetaRawMessageStreamEvent
} as unknown as BetaRawMessageStreamEvent
}
const candidate = chunk.candidates?.[0]
const parts = candidate?.content?.parts ?? []

View File

@ -168,7 +168,7 @@ describe('buildOpenAIRequestBody — thinking params', () => {
const body = buildOpenAIRequestBody({ ...baseParams, enableThinking: true })
expect(body.thinking).toEqual({ type: 'enabled' })
expect(body.enable_thinking).toBe(true)
expect(body.chat_template_kwargs.thinking).toBe(true)
expect(body.chat_template_kwargs!.thinking).toBe(true)
})
test('does NOT include thinking params when disabled', () => {

View File

@ -6,6 +6,7 @@ import type {
SystemAPIErrorMessage,
AssistantMessage,
} from '../../../types/message.js'
import type { AgentId } from '../../../types/ids.js'
import type { Tools } from '../../../Tool.js'
import type { Stream } from 'openai/streaming.mjs'
import type {
@ -362,4 +363,4 @@ export async function* queryModelOpenAI(
error: (error instanceof Error ? error : new Error(String(error))) as unknown as SDKAssistantMessageError,
})
}
}
}

View File

@ -97,9 +97,10 @@ export async function* adaptOpenAIStreamToAnthropic(
cache_read_input_tokens: cachedTokens,
},
},
} as BetaRawMessageStreamEvent
} as unknown as BetaRawMessageStreamEvent
}
// Skip chunks that carry only usage data (no delta content)
if (!delta) continue
// Handle reasoning_content → Anthropic thinking block.

View File

@ -288,7 +288,7 @@ function makeDreamProgressWatcher(
let text = ''
let toolUseCount = 0
const touchedPaths: string[] = []
const contentBlocks = msg.message.content as ContentBlockParam[]
const contentBlocks = msg.message!.content as ContentBlockParam[]
for (const block of contentBlocks) {
if (block.type === 'text') {
text += block.text

View File

@ -93,8 +93,8 @@ describe("groupMessagesByApiRound", () => {
test("preserves message order within groups", () => {
const messages = [makeMsg("assistant", "a1"), makeMsg("user", "u2")];
const groups = groupMessagesByApiRound(messages);
expect(groups[0][0].message.id).toBe("a1");
expect(groups[0][1].message.id).toBe("u2");
expect(groups[0]![0]!.message!.id).toBe("a1");
expect(groups[0]![1]!.message!.id).toBe("u2");
});
test("handles system messages", () => {

View File

@ -39,6 +39,7 @@ import {
getAgentListingDeltaAttachment,
getDeferredToolsDeltaAttachment,
getMcpInstructionsDeltaAttachment,
type Attachment,
} from '../../utils/attachments.js'
import { getMemoryPath } from '../../utils/config.js'
import { COMPACT_MAX_OUTPUT_TOKENS } from '../../utils/context.js'
@ -114,6 +115,7 @@ import {
roughTokenCountEstimation,
roughTokenCountEstimationForMessages,
} from '../tokenEstimation.js'
import type { SDKStatus } from '../../entrypoints/agentSdkTypes.js'
import { groupMessagesByApiRound } from './grouping.js'
import {
getCompactPrompt,
@ -150,7 +152,7 @@ export function stripImagesFromMessages(messages: Message[]): Message[] {
return message
}
const content = message.message.content
const content = message.message!.content
if (!Array.isArray(content)) {
return message
}
@ -216,8 +218,8 @@ export function stripReinjectedAttachments(messages: Message[]): Message[] {
m =>
!(
m.type === 'attachment' &&
(m.attachment.type === 'skill_discovery' ||
m.attachment.type === 'skill_listing')
(m.attachment!.type === 'skill_discovery' ||
m.attachment!.type === 'skill_listing')
),
)
}
@ -251,8 +253,8 @@ export function truncateHeadForPTLRetry(
// (drops only the marker, re-adds it, zero progress on retry 2+).
const input =
messages[0]?.type === 'user' &&
messages[0].isMeta &&
messages[0].message.content === PTL_RETRY_MARKER
messages[0]?.isMeta &&
messages[0]?.message?.content === PTL_RETRY_MARKER
? messages.slice(1)
: messages
@ -760,7 +762,7 @@ export async function compactConversation(
context.setStreamMode?.('requesting')
context.setResponseLength?.(() => 0)
context.onCompactProgress?.({ type: 'compact_end' })
context.setSDKStatus?.(null)
context.setSDKStatus?.("" as SDKStatus)
}
}
@ -1103,7 +1105,7 @@ export async function partialCompactConversation(
context.setStreamMode?.('requesting')
context.setResponseLength?.(() => 0)
context.onCompactProgress?.({ type: 'compact_end' })
context.setSDKStatus?.(null)
context.setSDKStatus?.("" as SDKStatus)
}
}
@ -1453,7 +1455,7 @@ export async function createPostCompactFileAttachments(
)
let usedTokens = 0
return results.filter((result): result is AttachmentMessage => {
return results.filter((result): result is AttachmentMessage<Attachment> => {
if (result === null) {
return false
}
@ -1613,10 +1615,10 @@ export async function createAsyncAgentAttachmentsIfNeeded(
function collectReadToolFilePaths(messages: Message[]): Set<string> {
const stubIds = new Set<string>()
for (const message of messages) {
if (message.type !== 'user' || !Array.isArray(message.message.content)) {
if (message.type !== 'user' || !Array.isArray(message.message!.content)) {
continue
}
for (const block of message.message.content) {
for (const block of message.message!.content) {
if (
block.type === 'tool_result' &&
typeof block.content === 'string' &&
@ -1631,11 +1633,11 @@ function collectReadToolFilePaths(messages: Message[]): Set<string> {
for (const message of messages) {
if (
message.type !== 'assistant' ||
!Array.isArray(message.message.content)
!Array.isArray(message.message!.content)
) {
continue
}
for (const block of message.message.content) {
for (const block of message.message!.content) {
if (
block.type !== 'tool_use' ||
block.name !== FILE_READ_TOOL_NAME ||

View File

@ -43,7 +43,7 @@ export function groupMessagesByApiRound(messages: Message[]): Message[][] {
for (const msg of messages) {
if (
msg.type === 'assistant' &&
msg.message.id !== lastAssistantId &&
msg.message!.id !== lastAssistantId &&
current.length > 0
) {
groups.push(current)
@ -52,7 +52,7 @@ export function groupMessagesByApiRound(messages: Message[]): Message[][] {
current.push(msg)
}
if (msg.type === 'assistant') {
lastAssistantId = msg.message.id
lastAssistantId = msg.message!.id
}
}

View File

@ -169,11 +169,11 @@ export function estimateMessageTokens(messages: Message[]): number {
continue
}
if (!Array.isArray(message.message.content)) {
if (!Array.isArray(message.message!.content)) {
continue
}
for (const block of message.message.content) {
for (const block of message.message!.content) {
if (block.type === 'text') {
totalTokens += roughTokenCountEstimation(block.text)
} else if (block.type === 'tool_result') {
@ -228,9 +228,9 @@ function collectCompactableToolIds(messages: Message[]): string[] {
for (const message of messages) {
if (
message.type === 'assistant' &&
Array.isArray(message.message.content)
Array.isArray(message.message!.content)
) {
for (const block of message.message.content) {
for (const block of message.message!.content) {
if (block.type === 'tool_use' && COMPACTABLE_TOOLS.has(block.name)) {
ids.push(block.id)
}
@ -313,9 +313,9 @@ async function cachedMicrocompactPath(
const compactableToolIds = new Set(collectCompactableToolIds(messages))
// Second pass: register tool results grouped by user message
for (const message of messages) {
if (message.type === 'user' && Array.isArray(message.message.content)) {
if (message.type === 'user' && Array.isArray(message.message!.content)) {
const groupIds: string[] = []
for (const block of message.message.content) {
for (const block of message.message!.content) {
if (
block.type === 'tool_result' &&
compactableToolIds.has(block.tool_use_id) &&
@ -375,7 +375,7 @@ async function cachedMicrocompactPath(
const baseline =
lastAsst?.type === 'assistant'
? ((
lastAsst.message.usage as unknown as Record<
lastAsst.message!.usage as unknown as Record<
string,
number | undefined
>
@ -468,11 +468,11 @@ function maybeTimeBasedMicrocompact(
let tokensSaved = 0
const result: Message[] = messages.map(message => {
if (message.type !== 'user' || !Array.isArray(message.message.content)) {
if (message.type !== 'user' || !Array.isArray(message.message!.content)) {
return message
}
let touched = false
const newContent = message.message.content.map(block => {
const newContent = message.message!.content.map(block => {
if (
block.type === 'tool_result' &&
clearSet.has(block.tool_use_id) &&

View File

@ -7,6 +7,7 @@ import { clearClassifierApprovals } from '../../utils/classifierApprovals.js'
import { resetGetMemoryFilesCache } from '../../utils/claudemd.js'
import { clearSessionMessagesCache } from '../../utils/sessionStorage.js'
import { clearBetaTracingState } from '../../utils/telemetry/betaSessionTracing.js'
import { logError } from '../../utils/log.js'
import { resetMicrocompactState } from './microCompact.js'
/**

View File

@ -134,11 +134,11 @@ async function initSessionMemoryCompactConfig(): Promise<void> {
*/
export function hasTextBlocks(message: Message): boolean {
if (message.type === 'assistant') {
const content = message.message.content
const content = message.message!.content
return Array.isArray(content) && content.some(block => block.type === 'text')
}
if (message.type === 'user') {
const content = message.message.content
const content = message.message!.content
if (typeof content === 'string') {
return content.length > 0
}
@ -156,7 +156,7 @@ function getToolResultIds(message: Message): string[] {
if (message.type !== 'user') {
return []
}
const content = message.message.content
const content = message.message!.content
if (!Array.isArray(content)) {
return []
}
@ -176,7 +176,7 @@ function hasToolUseWithIds(message: Message, toolUseIds: Set<string>): boolean {
if (message.type !== 'assistant') {
return false
}
const content = message.message.content
const content = message.message!.content
if (!Array.isArray(content)) {
return false
}
@ -251,8 +251,8 @@ export function adjustIndexToPreserveAPIInvariants(
const toolUseIdsInKeptRange = new Set<string>()
for (let i = adjustedIndex; i < messages.length; i++) {
const msg = messages[i]!
if (msg.type === 'assistant' && Array.isArray(msg.message.content)) {
for (const block of msg.message.content) {
if (msg.type === 'assistant' && Array.isArray(msg.message!.content)) {
for (const block of msg.message!.content) {
if (block.type === 'tool_use') {
toolUseIdsInKeptRange.add(block.id)
}
@ -273,9 +273,9 @@ export function adjustIndexToPreserveAPIInvariants(
// Remove found tool_use_ids from the set
if (
message.type === 'assistant' &&
Array.isArray(message.message.content)
Array.isArray(message.message!.content)
) {
for (const block of message.message.content) {
for (const block of message.message!.content) {
if (block.type === 'tool_use' && neededToolUseIds.has(block.id)) {
neededToolUseIds.delete(block.id)
}
@ -290,8 +290,8 @@ export function adjustIndexToPreserveAPIInvariants(
const messageIdsInKeptRange = new Set<string>()
for (let i = adjustedIndex; i < messages.length; i++) {
const msg = messages[i]!
if (msg.type === 'assistant' && msg.message.id) {
messageIdsInKeptRange.add(msg.message.id)
if (msg.type === 'assistant' && msg.message!.id) {
messageIdsInKeptRange.add(msg.message!.id)
}
}
@ -301,8 +301,8 @@ export function adjustIndexToPreserveAPIInvariants(
const message = messages[i]!
if (
message.type === 'assistant' &&
message.message.id &&
messageIdsInKeptRange.has(message.message.id)
message.message!.id &&
messageIdsInKeptRange.has(message.message!.id)
) {
// This message has the same message.id as one in the kept range
// Include it so thinking blocks can be properly merged

View File

@ -346,8 +346,8 @@ export class StreamingToolExecutor {
const isErrorResult =
update.message.type === 'user' &&
Array.isArray(update.message.message.content) &&
update.message.message.content.some(
Array.isArray(update.message.message!.content) &&
update.message.message!.content.some(
_ => _.type === 'tool_result' && _.is_error === true,
)

View File

@ -815,7 +815,7 @@ async function checkPermissionsAndCallTool(
tool,
processedInput,
toolUseID,
assistantMessage.message.id,
assistantMessage.message.id!,
requestId,
mcpServerType,
mcpServerBaseUrl,
@ -1497,7 +1497,7 @@ async function checkPermissionsAndCallTool(
toolUseContext,
tool,
toolUseID,
assistantMessage.message.id,
assistantMessage.message.id!,
processedInput,
toolOutput,
requestId,

View File

@ -67,7 +67,7 @@ export async function* runPostToolUseHooks<Input extends AnyObject, Output>(
// IMPORTANT: We emit a cancelled event per hook
if (
result.message?.type === 'attachment' &&
result.message.attachment.type === 'hook_cancelled'
result.message.attachment!.type === 'hook_cancelled'
) {
logEvent('tengu_post_tool_hooks_cancelled', {
toolName: sanitizeToolNameForAnalytics(tool.name),
@ -96,7 +96,7 @@ export async function* runPostToolUseHooks<Input extends AnyObject, Output>(
result.message &&
!(
result.message.type === 'attachment' &&
result.message.attachment.type === 'hook_blocking_error'
result.message.attachment!.type === 'hook_blocking_error'
)
) {
yield { message: result.message as AttachmentMessage | ProgressMessage<HookProgress> }
@ -223,7 +223,7 @@ export async function* runPostToolUseFailureHooks<Input extends AnyObject>(
// Check if we were aborted during hook execution
if (
result.message?.type === 'attachment' &&
result.message.attachment.type === 'hook_cancelled'
result.message.attachment!.type === 'hook_cancelled'
) {
logEvent('tengu_post_tool_failure_hooks_cancelled', {
toolName: sanitizeToolNameForAnalytics(tool.name),
@ -248,7 +248,7 @@ export async function* runPostToolUseFailureHooks<Input extends AnyObject>(
result.message &&
!(
result.message.type === 'attachment' &&
result.message.attachment.type === 'hook_blocking_error'
result.message.attachment!.type === 'hook_blocking_error'
)
) {
yield { message: result.message as AttachmentMessage | ProgressMessage<HookProgress> }

View File

@ -180,7 +180,7 @@ function mapMessages(
if (typeof _ === 'string') {
return f(_)
}
return _.map(_ => {
return _!.map(_ => {
switch (_.type) {
case 'tool_result':
if (typeof _.content === 'string') {

View File

@ -106,14 +106,14 @@ export function updateProgressFromMessage(
if (message.type !== 'assistant') {
return
}
const usage = message.message.usage as BetaUsage
const usage = message.message!.usage as BetaUsage
// Keep latest input (it's cumulative in the API), sum outputs
tracker.latestInputTokens =
(usage.input_tokens as number) +
(usage.cache_creation_input_tokens ?? 0) +
(usage.cache_read_input_tokens ?? 0)
tracker.cumulativeOutputTokens += usage.output_tokens as number
for (const content of (message.message.content ?? []) as Array<{ type: string; name?: string; input?: unknown }>) {
for (const content of (message.message!.content ?? []) as Array<{ type: string; name?: string; input?: unknown }>) {
if (content.type === 'tool_use') {
tracker.toolUseCount++
// Omit StructuredOutput from preview - it's an internal tool

View File

@ -423,11 +423,11 @@ export function startBackgroundSession({
const contentBlocks = (msg.message?.content ?? []) as Array<{ type: string; text?: string; name?: string; input?: unknown }>
for (const block of contentBlocks) {
if (block.type === 'text') {
tokenCount += roughTokenCountEstimation(block.text)
tokenCount += roughTokenCountEstimation(block.text ?? '')
} else if (block.type === 'tool_use') {
toolCount++
const activity: ToolActivity = {
toolName: block.name,
toolName: block.name ?? '',
input: block.input as Record<string, unknown>,
}
recentActivities.push(activity)

View File

@ -29,6 +29,7 @@ import {
type BackgroundRemoteSessionPrecondition,
checkBackgroundRemoteSessionEligibility,
} from '../../utils/background/remote/remoteSession.js'
export type { BackgroundRemoteSessionPrecondition }
import { logForDebugging } from '../../utils/debug.js'
import { logError } from '../../utils/log.js'
import { enqueuePendingNotification } from '../../utils/messageQueueManager.js'

View File

@ -45,6 +45,7 @@ import {
formatPreconditionError,
getRemoteTaskSessionUrl,
registerRemoteAgentTask,
type BackgroundRemoteSessionPrecondition,
} from '../../tasks/RemoteAgentTask/RemoteAgentTask.js'
import { assembleToolPool } from '../../tools.js'
import { asAgentId } from '../../types/ids.js'
@ -668,7 +669,7 @@ export const AgentTool = buildTool({
if (process.env.USER_TYPE === 'ant' && effectiveIsolation === 'remote') {
const eligibility = await checkRemoteAgentEligibility()
if (!eligibility.eligible) {
const reasons = (eligibility as { eligible: false; errors: Array<{ type: string; message?: string }> }).errors
const reasons = (eligibility as { eligible: false; errors: BackgroundRemoteSessionPrecondition[] }).errors
.map(formatPreconditionError)
.join('\n')
throw new Error(`Cannot launch remote agent:\n${reasons}`)

View File

@ -1,7 +1,9 @@
import type {
ContentBlock,
ToolResultBlockParam,
ToolUseBlockParam,
} from '@anthropic-ai/sdk/resources/index.mjs'
type BetaContentBlock = ContentBlock | ToolResultBlockParam
import * as React from 'react'
import { ConfigurableShortcutHint } from 'src/components/ConfigurableShortcutHint.js'
import {
@ -555,7 +557,7 @@ export function renderToolUseProgressMessage(
}
const message = msg.data.message
return message.message.content.some(
content => content.type === 'tool_use',
(content: BetaContentBlock) => content.type === 'tool_use',
)
})
@ -630,7 +632,7 @@ export function renderToolUseProgressMessage(
return false
}
return data.message.message.content.some(
content => content.type === 'tool_use',
(content: BetaContentBlock) => content.type === 'tool_use',
)
})
@ -799,7 +801,7 @@ function calculateAgentStats(progressMessages: ProgressMessage<Progress>[]): {
const message = msg.data.message
return (
message.type === 'user' &&
message.message.content.some(content => content.type === 'tool_result')
message.message.content.some((content: BetaContentBlock) => content.type === 'tool_result')
)
})
@ -1078,14 +1080,14 @@ export function extractLastToolInfo(
const message = msg.data.message
return (
message.type === 'user' &&
message.message.content.some(c => c.type === 'tool_result')
message.message.content.some((c: BetaContentBlock) => c.type === 'tool_result')
)
},
)
if (lastToolResult?.data.message.type === 'user') {
const toolResultBlock = lastToolResult.data.message.message.content.find(
c => c.type === 'tool_result',
(c: BetaContentBlock) => c.type === 'tool_result',
)
if (toolResultBlock?.type === 'tool_result') {

View File

@ -78,7 +78,7 @@ export const FORK_AGENT = {
export function isInForkChild(messages: MessageType[]): boolean {
return messages.some(m => {
if (m.type !== 'user') return false
const content = m.message.content
const content = m.message!.content
if (!Array.isArray(content)) return false
return content.some(
block =>

View File

@ -267,7 +267,7 @@ export const NotebookEditTool = buildTool({
}
} else {
// First try to find the cell by its actual ID
const cellIndex = notebook.cells.findIndex(cell => cell.id === cell_id)
const cellIndex = notebook.cells.findIndex((cell: NotebookCell) => cell.id === cell_id)
if (cellIndex === -1) {
// If not found, try to parse as a numeric index (cell-N format)
@ -352,7 +352,7 @@ export const NotebookEditTool = buildTool({
cellIndex = 0 // Default to inserting at the beginning if no cell_id is provided
} else {
// First try to find the cell by its actual ID
cellIndex = notebook.cells.findIndex(cell => cell.id === cell_id)
cellIndex = notebook.cells.findIndex((cell: NotebookCell) => cell.id === cell_id)
// If not found, try to parse as a numeric index (cell-N format)
if (cellIndex === -1) {

View File

@ -430,7 +430,13 @@ export async function getURLMarkdownContent(
// This lets GC reclaim up to MAX_HTTP_CONTENT_LENGTH (10MB) before Turndown
// builds its DOM tree (which can be 3-5x the HTML size).
;(response as { data: unknown }).data = null
const contentType = response.headers['content-type'] ?? ''
const contentTypeHeader = response.headers['content-type']
const contentType =
typeof contentTypeHeader === 'string'
? contentTypeHeader
: contentTypeHeader === undefined || contentTypeHeader === null
? ''
: String(contentTypeHeader)
// Binary content: save raw bytes to disk with a proper extension so Claude
// can inspect the file later. We still fall through to the utf-8 decode +
@ -519,9 +525,9 @@ export async function applyPromptToMarkdown(
throw new AbortError()
}
const { content } = assistantMessage.message
if (content.length > 0) {
const contentBlock = content[0]
const { content } = assistantMessage.message!
if (content!.length > 0) {
const contentBlock = content![0]
if (contentBlock && typeof contentBlock === 'object' && 'text' in contentBlock) {
return (contentBlock as { text: string }).text
}

Some files were not shown because too many files have changed in this diff Show More