mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-18 22:35:51 +00:00
feat: 全部类型问题解决
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ?? []
|
||||
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) &&
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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> }
|
||||
|
||||
@@ -180,7 +180,7 @@ function mapMessages(
|
||||
if (typeof _ === 'string') {
|
||||
return f(_)
|
||||
}
|
||||
return _.map(_ => {
|
||||
return _!.map(_ => {
|
||||
switch (_.type) {
|
||||
case 'tool_result':
|
||||
if (typeof _.content === 'string') {
|
||||
|
||||
Reference in New Issue
Block a user