feat: 全部类型问题解决

This commit is contained in:
claude-code-best
2026-04-11 10:24:00 +08:00
parent 7088fe3c8b
commit 6a70056910
135 changed files with 671 additions and 503 deletions

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

@@ -579,13 +579,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 }),
}),
@@ -595,9 +595,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 }) }
: {}
@@ -611,9 +611,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[],
}
}
@@ -624,13 +624,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 }),
}),
@@ -640,11 +640,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')
@@ -662,9 +662,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[],
}
}
@@ -972,8 +972,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) {
@@ -987,7 +987,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 {
@@ -149,7 +150,7 @@ function assembleFinalAssistantOutputs(params: {
outputs.push({
message: {
...partialMessage,
content: normalizeContentFromAPI(allBlocks, tools, agentId),
content: normalizeContentFromAPI(allBlocks, tools, agentId as AgentId | undefined),
usage,
stop_reason: stopReason,
stop_sequence: null,

View File

@@ -111,9 +111,10 @@ export async function* adaptOpenAIStreamToAnthropic(
cache_read_input_tokens: cachedReadTokens,
},
},
} 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

@@ -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

@@ -443,7 +443,7 @@ const externalTips: Tip[] = [
},
{
id: 'desktop-shortcut',
content: async ctx => {
content: async (ctx: TipContext) => {
const blue = color('suggestion', ctx.theme)
return `Continue your session in Claude Code Desktop with ${blue('/desktop')}`
},
@@ -489,24 +489,24 @@ const externalTips: Tip[] = [
},
{
id: 'frontend-design-plugin',
content: async ctx => {
content: async (ctx: TipContext) => {
const blue = color('suggestion', ctx.theme)
return `Working with HTML/CSS? Install the frontend-design plugin:\n${blue(`/plugin install frontend-design@${OFFICIAL_MARKETPLACE_NAME}`)}`
},
cooldownSessions: 3,
isRelevant: async context =>
isRelevant: async (context: TipContext) =>
isMarketplacePluginRelevant('frontend-design', context, {
filePath: /\.(html|css|htm)$/i,
}),
},
{
id: 'vercel-plugin',
content: async ctx => {
content: async (ctx: TipContext) => {
const blue = color('suggestion', ctx.theme)
return `Working with Vercel? Install the vercel plugin:\n${blue(`/plugin install vercel@${OFFICIAL_MARKETPLACE_NAME}`)}`
},
cooldownSessions: 3,
isRelevant: async context =>
isRelevant: async (context: TipContext) =>
isMarketplacePluginRelevant('vercel', context, {
filePath: /(?:^|[/\\])vercel\.json$/i,
cli: ['vercel'],
@@ -514,7 +514,7 @@ const externalTips: Tip[] = [
},
{
id: 'effort-high-nudge',
content: async ctx => {
content: async (ctx: TipContext) => {
const blue = color('suggestion', ctx.theme)
const cmd = blue('/effort high')
const variant = getFeatureValue_CACHED_MAY_BE_STALE<
@@ -544,7 +544,7 @@ const externalTips: Tip[] = [
},
{
id: 'subagent-fanout-nudge',
content: async ctx => {
content: async (ctx: TipContext) => {
const blue = color('suggestion', ctx.theme)
const variant = getFeatureValue_CACHED_MAY_BE_STALE<
'off' | 'copy_a' | 'copy_b'
@@ -566,7 +566,7 @@ const externalTips: Tip[] = [
},
{
id: 'loop-command-nudge',
content: async ctx => {
content: async (ctx: TipContext) => {
const blue = color('suggestion', ctx.theme)
const variant = getFeatureValue_CACHED_MAY_BE_STALE<
'off' | 'copy_a' | 'copy_b'
@@ -589,7 +589,7 @@ const externalTips: Tip[] = [
},
{
id: 'guest-passes',
content: async ctx => {
content: async (ctx: TipContext) => {
const claude = color('claude', ctx.theme)
const reward = getCachedReferrerReward()
return reward
@@ -608,7 +608,7 @@ const externalTips: Tip[] = [
},
{
id: 'overage-credit',
content: async ctx => {
content: async (ctx: TipContext) => {
const claude = color('claude', ctx.theme)
const info = getCachedOverageCreditGrant()
const amount = info ? formatGrantAmount(info) : null

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') {