feat: 完成第二版类型清理

This commit is contained in:
claude-code-best
2026-03-31 23:03:47 +08:00
parent 4c0a655a1c
commit d7a729ca68
604 changed files with 595 additions and 953 deletions

View File

@@ -130,7 +130,8 @@ export function startAgentSummarization(
)
continue
}
const textBlock = msg.message.content.find(b => b.type === 'text')
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()
logForDebugging(

View File

@@ -339,7 +339,8 @@ export async function generateSuggestion(
for (const msg of result.messages) {
if (msg.type !== 'assistant') continue
const textBlock = msg.message.content.find(b => b.type === 'text')
const contentArr = Array.isArray(msg.message.content) ? msg.message.content : []
const textBlock = contentArr.find(b => b.type === 'text')
if (textBlock?.type === 'text') {
const suggestion = textBlock.text.trim()
if (suggestion) {

View File

@@ -10,6 +10,7 @@
// State is closure-scoped inside initAutoDream() rather than module-level
// (tests call initAutoDream() in beforeEach for a fresh closure).
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
import type { REPLHookContext } from '../../utils/hooks/postSamplingHooks.js'
import {
createCacheSafeParams,
@@ -241,7 +242,7 @@ ${sessionIds.map(id => `- ${id}`).join('\n')}`
isDreamTask(dreamState) &&
dreamState.filesTouched.length > 0
) {
appendSystemMessage({
;(appendSystemMessage as (msg: Message) => void)({
...createMemorySavedMessage(dreamState.filesTouched),
verb: 'Improved',
})
@@ -287,7 +288,8 @@ function makeDreamProgressWatcher(
let text = ''
let toolUseCount = 0
const touchedPaths: string[] = []
for (const block of msg.message.content) {
const contentBlocks = msg.message.content as ContentBlockParam[]
for (const block of contentBlocks) {
if (block.type === 'text') {
text += block.text
} else if (block.type === 'tool_use') {

View File

@@ -1,12 +1,37 @@
// Auto-generated stub — replace with real implementation
export {};
export const isCachedMicrocompactEnabled: any = (() => {}) as any;
export const isModelSupportedForCacheEditing: any = (() => {}) as any;
export const getCachedMCConfig: any = (() => {}) as any;
export const createCachedMCState: any = (() => {}) as any;
export const markToolsSentToAPI: any = (() => {}) as any;
export const resetCachedMCState: any = (() => {}) as any;
export const registerToolResult: any = (() => {}) as any;
export const registerToolMessage: any = (() => {}) as any;
export const getToolResultsToDelete: any = (() => {}) as any;
export const createCacheEditsBlock: any = (() => {}) as any;
export type CachedMCState = {
registeredTools: Set<string>
toolOrder: string[]
deletedRefs: Set<string>
pinnedEdits: PinnedCacheEdits[]
toolsSentToAPI: boolean
}
export type CacheEditsBlock = {
type: 'cache_edits'
edits: Array<{ type: string; tool_use_id: string }>
}
export type PinnedCacheEdits = {
userMessageIndex: number
block: CacheEditsBlock
}
export const isCachedMicrocompactEnabled: () => boolean = () => false;
export const isModelSupportedForCacheEditing: (model: string) => boolean = () => false;
export const getCachedMCConfig: () => { triggerThreshold: number; keepRecent: number } = () => ({ triggerThreshold: 0, keepRecent: 0 });
export const createCachedMCState: () => CachedMCState = () => ({
registeredTools: new Set(),
toolOrder: [],
deletedRefs: new Set(),
pinnedEdits: [],
toolsSentToAPI: false,
});
export const markToolsSentToAPI: (state: CachedMCState) => void = () => {};
export const resetCachedMCState: (state: CachedMCState) => void = () => {};
export const registerToolResult: (state: CachedMCState, toolId: string) => void = () => {};
export const registerToolMessage: (state: CachedMCState, groupIds: string[]) => void = () => {};
export const getToolResultsToDelete: (state: CachedMCState) => string[] = () => [];
export const createCacheEditsBlock: (state: CachedMCState, toolIds: string[]) => CacheEditsBlock | null = () => null;

View File

@@ -436,7 +436,7 @@ export function evaluateTimeBasedTrigger(
return null
}
const gapMinutes =
(Date.now() - new Date(lastAssistant.timestamp).getTime()) / 60_000
(Date.now() - new Date(lastAssistant.timestamp as string | number).getTime()) / 60_000
if (!Number.isFinite(gapMinutes) || gapMinutes < config.gapThresholdMinutes) {
return null
}

View File

@@ -135,7 +135,7 @@ async function initSessionMemoryCompactConfig(): Promise<void> {
export function hasTextBlocks(message: Message): boolean {
if (message.type === 'assistant') {
const content = message.message.content
return content.some(block => block.type === 'text')
return Array.isArray(content) && content.some(block => block.type === 'text')
}
if (message.type === 'user') {
const content = message.message.content

File diff suppressed because one or more lines are too long

View File

@@ -139,6 +139,7 @@ import type {
ConnectedMCPServer,
MCPServerConnection,
McpSdkServerConfig,
McpStdioServerConfig,
ScopedMcpServerConfig,
ServerResource,
} from './types.js'
@@ -903,7 +904,7 @@ export const connectToServer = memoize(
)
logMCPDebug(name, `claude.ai proxy transport created successfully`)
} else if (
(serverRef.type === 'stdio' || !serverRef.type) &&
((serverRef as ScopedMcpServerConfig).type === 'stdio' || !(serverRef as ScopedMcpServerConfig).type) &&
isClaudeInChromeMCPServer(name)
) {
// Run the Chrome MCP server in-process to avoid spawning a ~325 MB subprocess
@@ -916,7 +917,7 @@ export const connectToServer = memoize(
const { createLinkedTransportPair } = await import(
'./InProcessTransport.js'
)
const context = createChromeContext(serverRef.env)
const context = createChromeContext((serverRef as McpStdioServerConfig).env)
inProcessServer = createClaudeForChromeMcpServer(context)
const [clientTransport, serverTransport] = createLinkedTransportPair()
await inProcessServer.connect(serverTransport)
@@ -924,7 +925,7 @@ export const connectToServer = memoize(
logMCPDebug(name, `In-process Chrome MCP server started`)
} else if (
feature('CHICAGO_MCP') &&
(serverRef.type === 'stdio' || !serverRef.type) &&
((serverRef as ScopedMcpServerConfig).type === 'stdio' || !(serverRef as ScopedMcpServerConfig).type) &&
isComputerUseMCPServer!(name)
) {
// Run the Computer Use MCP server in-process — same rationale as
@@ -941,23 +942,24 @@ export const connectToServer = memoize(
await inProcessServer.connect(serverTransport)
transport = clientTransport
logMCPDebug(name, `In-process Computer Use MCP server started`)
} else if (serverRef.type === 'stdio' || !serverRef.type) {
} else if ((serverRef as ScopedMcpServerConfig).type === 'stdio' || !(serverRef as ScopedMcpServerConfig).type) {
const stdioRef = serverRef as McpStdioServerConfig
const finalCommand =
process.env.CLAUDE_CODE_SHELL_PREFIX || serverRef.command
process.env.CLAUDE_CODE_SHELL_PREFIX || stdioRef.command
const finalArgs = process.env.CLAUDE_CODE_SHELL_PREFIX
? [[serverRef.command, ...serverRef.args].join(' ')]
: serverRef.args
? [[stdioRef.command, ...stdioRef.args].join(' ')]
: stdioRef.args
transport = new StdioClientTransport({
command: finalCommand,
args: finalArgs,
env: {
...subprocessEnv(),
...serverRef.env,
...stdioRef.env,
} as Record<string, string>,
stderr: 'pipe', // prevents error output from the MCP server from printing to the UI
})
} else {
throw new Error(`Unsupported server type: ${serverRef.type}`)
throw new Error(`Unsupported server type: ${(serverRef as ScopedMcpServerConfig).type}`)
}
// Set up stderr logging for stdio transport before connecting in case there are any stderr
@@ -3245,10 +3247,11 @@ async function callMCPTool({
}
function extractToolUseId(message: AssistantMessage): string | undefined {
if (message.message.content[0]?.type !== 'tool_use') {
const firstBlock = (message.message.content as ContentBlockParam[] | undefined)?.[0]
if (!firstBlock || typeof firstBlock === 'string' || firstBlock.type !== 'tool_use') {
return undefined
}
return message.message.content[0].id
return firstBlock.id
}
/**

View File

@@ -38,4 +38,3 @@ export async function handleMcpjsonServerApprovals(root: Root): Promise<void> {
}
});
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIk1DUFNlcnZlckFwcHJvdmFsRGlhbG9nIiwiTUNQU2VydmVyTXVsdGlzZWxlY3REaWFsb2ciLCJSb290IiwiS2V5YmluZGluZ1NldHVwIiwiQXBwU3RhdGVQcm92aWRlciIsImdldE1jcENvbmZpZ3NCeVNjb3BlIiwiZ2V0UHJvamVjdE1jcFNlcnZlclN0YXR1cyIsImhhbmRsZU1jcGpzb25TZXJ2ZXJBcHByb3ZhbHMiLCJyb290IiwiUHJvbWlzZSIsInNlcnZlcnMiLCJwcm9qZWN0U2VydmVycyIsInBlbmRpbmdTZXJ2ZXJzIiwiT2JqZWN0Iiwia2V5cyIsImZpbHRlciIsInNlcnZlck5hbWUiLCJsZW5ndGgiLCJyZXNvbHZlIiwiZG9uZSIsInVuZGVmaW5lZCIsInJlbmRlciJdLCJzb3VyY2VzIjpbIm1jcFNlcnZlckFwcHJvdmFsLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBNQ1BTZXJ2ZXJBcHByb3ZhbERpYWxvZyB9IGZyb20gJy4uL2NvbXBvbmVudHMvTUNQU2VydmVyQXBwcm92YWxEaWFsb2cuanMnXG5pbXBvcnQgeyBNQ1BTZXJ2ZXJNdWx0aXNlbGVjdERpYWxvZyB9IGZyb20gJy4uL2NvbXBvbmVudHMvTUNQU2VydmVyTXVsdGlzZWxlY3REaWFsb2cuanMnXG5pbXBvcnQgdHlwZSB7IFJvb3QgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyBLZXliaW5kaW5nU2V0dXAgfSBmcm9tICcuLi9rZXliaW5kaW5ncy9LZXliaW5kaW5nUHJvdmlkZXJTZXR1cC5qcydcbmltcG9ydCB7IEFwcFN0YXRlUHJvdmlkZXIgfSBmcm9tICcuLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7IGdldE1jcENvbmZpZ3NCeVNjb3BlIH0gZnJvbSAnLi9tY3AvY29uZmlnLmpzJ1xuaW1wb3J0IHsgZ2V0UHJvamVjdE1jcFNlcnZlclN0YXR1cyB9IGZyb20gJy4vbWNwL3V0aWxzLmpzJ1xuXG4vKipcbiAqIFNob3cgTUNQIHNlcnZlciBhcHByb3ZhbCBkaWFsb2dzIGZvciBwZW5kaW5nIHByb2plY3Qgc2VydmVycy5cbiAqIFVzZXMgdGhlIHByb3ZpZGVkIEluayByb290IHRvIHJlbmRlciAocmV1c2luZyB0aGUgZXhpc3RpbmcgaW5zdGFuY2VcbiAqIGZyb20gbWFpbi50c3ggaW5zdGVhZCBvZiBjcmVhdGluZyBhIHNlcGFyYXRlIG9uZSkuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVNY3Bqc29uU2VydmVyQXBwcm92YWxzKHJvb3Q6IFJvb3QpOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgeyBzZXJ2ZXJzOiBwcm9qZWN0U2VydmVycyB9ID0gZ2V0TWNwQ29uZmlnc0J5U2NvcGUoJ3Byb2plY3QnKVxuICBjb25zdCBwZW5kaW5nU2VydmVycyA9IE9iamVjdC5rZXlzKHByb2plY3RTZXJ2ZXJzKS5maWx0ZXIoXG4gICAgc2VydmVyTmFtZSA9PiBnZXRQcm9qZWN0TWNwU2VydmVyU3RhdHVzKHNlcnZlck5hbWUpID09PSAncGVuZGluZycsXG4gIClcblxuICBpZiAocGVuZGluZ1NlcnZlcnMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuXG4gIH1cblxuICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPihyZXNvbHZlID0+IHtcbiAgICBjb25zdCBkb25lID0gKCk6IHZvaWQgPT4gdm9pZCByZXNvbHZlKClcbiAgICBpZiAocGVuZGluZ1NlcnZlcnMubGVuZ3RoID09PSAxICYmIHBlbmRpbmdTZXJ2ZXJzWzBdICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IHNlcnZlck5hbWUgPSBwZW5kaW5nU2VydmVyc1swXVxuICAgICAgcm9vdC5yZW5kZXIoXG4gICAgICAgIDxBcHBTdGF0ZVByb3ZpZGVyPlxuICAgICAgICAgIDxLZXliaW5kaW5nU2V0dXA+XG4gICAgICAgICAgICA8TUNQU2VydmVyQXBwcm92YWxEaWFsb2cgc2VydmVyTmFtZT17c2VydmVyTmFtZX0gb25Eb25lPXtkb25lfSAvPlxuICAgICAgICAgIDwvS2V5YmluZGluZ1NldHVwPlxuICAgICAgICA8L0FwcFN0YXRlUHJvdmlkZXI+LFxuICAgICAgKVxuICAgIH0gZWxzZSB7XG4gICAgICByb290LnJlbmRlcihcbiAgICAgICAgPEFwcFN0YXRlUHJvdmlkZXI+XG4gICAgICAgICAgPEtleWJpbmRpbmdTZXR1cD5cbiAgICAgICAgICAgIDxNQ1BTZXJ2ZXJNdWx0aXNlbGVjdERpYWxvZ1xuICAgICAgICAgICAgICBzZXJ2ZXJOYW1lcz17cGVuZGluZ1NlcnZlcnN9XG4gICAgICAgICAgICAgIG9uRG9uZT17ZG9uZX1cbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgPC9LZXliaW5kaW5nU2V0dXA+XG4gICAgICAgIDwvQXBwU3RhdGVQcm92aWRlcj4sXG4gICAgICApXG4gICAgfVxuICB9KVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixTQUFTQyx1QkFBdUIsUUFBUSwwQ0FBMEM7QUFDbEYsU0FBU0MsMEJBQTBCLFFBQVEsNkNBQTZDO0FBQ3hGLGNBQWNDLElBQUksUUFBUSxXQUFXO0FBQ3JDLFNBQVNDLGVBQWUsUUFBUSwyQ0FBMkM7QUFDM0UsU0FBU0MsZ0JBQWdCLFFBQVEsc0JBQXNCO0FBQ3ZELFNBQVNDLG9CQUFvQixRQUFRLGlCQUFpQjtBQUN0RCxTQUFTQyx5QkFBeUIsUUFBUSxnQkFBZ0I7O0FBRTFEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLGVBQWVDLDRCQUE0QkEsQ0FBQ0MsSUFBSSxFQUFFTixJQUFJLENBQUMsRUFBRU8sT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0VBQzVFLE1BQU07SUFBRUMsT0FBTyxFQUFFQztFQUFlLENBQUMsR0FBR04sb0JBQW9CLENBQUMsU0FBUyxDQUFDO0VBQ25FLE1BQU1PLGNBQWMsR0FBR0MsTUFBTSxDQUFDQyxJQUFJLENBQUNILGNBQWMsQ0FBQyxDQUFDSSxNQUFNLENBQ3ZEQyxVQUFVLElBQUlWLHlCQUF5QixDQUFDVSxVQUFVLENBQUMsS0FBSyxTQUMxRCxDQUFDO0VBRUQsSUFBSUosY0FBYyxDQUFDSyxNQUFNLEtBQUssQ0FBQyxFQUFFO0lBQy9CO0VBQ0Y7RUFFQSxNQUFNLElBQUlSLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQ1MsT0FBTyxJQUFJO0lBQ2pDLE1BQU1DLElBQUksR0FBR0EsQ0FBQSxDQUFFLEVBQUUsSUFBSSxJQUFJLEtBQUtELE9BQU8sQ0FBQyxDQUFDO0lBQ3ZDLElBQUlOLGNBQWMsQ0FBQ0ssTUFBTSxLQUFLLENBQUMsSUFBSUwsY0FBYyxDQUFDLENBQUMsQ0FBQyxLQUFLUSxTQUFTLEVBQUU7TUFDbEUsTUFBTUosVUFBVSxHQUFHSixjQUFjLENBQUMsQ0FBQyxDQUFDO01BQ3BDSixJQUFJLENBQUNhLE1BQU0sQ0FDVCxDQUFDLGdCQUFnQjtBQUN6QixVQUFVLENBQUMsZUFBZTtBQUMxQixZQUFZLENBQUMsdUJBQXVCLENBQUMsVUFBVSxDQUFDLENBQUNMLFVBQVUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDRyxJQUFJLENBQUM7QUFDMUUsVUFBVSxFQUFFLGVBQWU7QUFDM0IsUUFBUSxFQUFFLGdCQUFnQixDQUNwQixDQUFDO0lBQ0gsQ0FBQyxNQUFNO01BQ0xYLElBQUksQ0FBQ2EsTUFBTSxDQUNULENBQUMsZ0JBQWdCO0FBQ3pCLFVBQVUsQ0FBQyxlQUFlO0FBQzFCLFlBQVksQ0FBQywwQkFBMEIsQ0FDekIsV0FBVyxDQUFDLENBQUNULGNBQWMsQ0FBQyxDQUM1QixNQUFNLENBQUMsQ0FBQ08sSUFBSSxDQUFDO0FBRTNCLFVBQVUsRUFBRSxlQUFlO0FBQzNCLFFBQVEsRUFBRSxnQkFBZ0IsQ0FDcEIsQ0FBQztJQUNIO0VBQ0YsQ0FBQyxDQUFDO0FBQ0oiLCJpZ25vcmVMaXN0IjpbXX0=

View File

@@ -379,38 +379,39 @@ export async function installPluginOp(
})
if (!result.ok) {
switch (result.reason) {
const failResult = result as Extract<typeof result, { ok: false }>
switch (failResult.reason) {
case 'local-source-no-location':
return {
success: false,
message: `Cannot install local plugin "${result.pluginName}" without marketplace install location`,
message: `Cannot install local plugin "${failResult.pluginName}" without marketplace install location`,
}
case 'settings-write-failed':
return {
success: false,
message: `Failed to update settings: ${result.message}`,
message: `Failed to update settings: ${failResult.message}`,
}
case 'resolution-failed':
return {
success: false,
message: formatResolutionError(result.resolution),
message: formatResolutionError(failResult.resolution),
}
case 'blocked-by-policy':
return {
success: false,
message: `Plugin "${result.pluginName}" is blocked by your organization's policy and cannot be installed`,
message: `Plugin "${failResult.pluginName}" is blocked by your organization's policy and cannot be installed`,
}
case 'dependency-blocked-by-policy':
return {
success: false,
message: `Plugin "${result.pluginName}" depends on "${result.blockedDependency}", which is blocked by your organization's policy`,
message: `Plugin "${failResult.pluginName}" depends on "${failResult.blockedDependency}", which is blocked by your organization's policy`,
}
}
}
return {
success: true,
message: `Successfully installed plugin: ${pluginId} (scope: ${scope})${result.depNote}`,
message: `Successfully installed plugin: ${pluginId} (scope: ${scope})${(result as Extract<typeof result, { ok: true }>).depNote}`,
pluginId,
pluginName: entry.name,
scope,

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,17 @@
// Auto-generated stub — replace with real implementation
export {};
export function loadRemoteSkill(_slug: string, _url: string): Promise<{
cacheHit: boolean;
latencyMs: number;
skillPath: string;
content: string;
fileCount?: number;
totalBytes?: number;
fetchMethod?: string;
}> {
return Promise.resolve({
cacheHit: false,
latencyMs: 0,
skillPath: '',
content: '',
});
}

View File

@@ -1,2 +1,3 @@
// Auto-generated stub — replace with real implementation
export {};
export function stripCanonicalPrefix(_name: string): string | null { return null; }
export function getDiscoveredRemoteSkill(_slug: string): { url: string } | undefined { return undefined; }

View File

@@ -1,2 +1,11 @@
// Auto-generated stub — replace with real implementation
export {};
export function logRemoteSkillLoaded(_data: {
slug: string;
cacheHit: boolean;
latencyMs: number;
urlScheme: string;
error?: string;
fileCount?: number;
totalBytes?: number;
fetchMethod?: string;
}): void {}

View File

@@ -80,7 +80,7 @@ export async function generateToolUseSummary({
},
})
const summary = response.message.content
const summary = (Array.isArray(response.message.content) ? response.message.content : [])
.filter(block => block.type === 'text')
.map(block => (block.type === 'text' ? block.text : ''))
.join('')

View File

@@ -253,7 +253,7 @@ function getNextImagePasteId(messages: Message[]): number {
let maxId = 0
for (const message of messages) {
if (message.type === 'user' && message.imagePasteIds) {
for (const id of message.imagePasteIds) {
for (const id of message.imagePasteIds as number[]) {
if (id > maxId) maxId = id
}
}
@@ -354,8 +354,8 @@ export async function* runToolUse(
tool = fallbackTool
}
}
const messageId = assistantMessage.message.id
const requestId = assistantMessage.requestId
const messageId = assistantMessage.message.id as string
const requestId = assistantMessage.requestId as string | undefined
const mcpServerType = getMcpServerType(
toolName,
toolUseContext.options.mcpClients,
@@ -548,7 +548,7 @@ function streamedCheckPermissionsAndCallTool(
})
stream.enqueue({
message: createProgressMessage({
toolUseID: progress.toolUseID,
toolUseID: progress.toolUseID as string,
parentToolUseID: toolUseID,
data: progress.data,
}),
@@ -822,8 +822,8 @@ async function checkPermissionsAndCallTool(
att.durationMs !== undefined
) {
preToolHookInfos.push({
command: att.command,
durationMs: att.durationMs,
command: att.command as string,
durationMs: att.durationMs as number,
})
}
}
@@ -1506,8 +1506,8 @@ async function checkPermissionsAndCallTool(
att.durationMs !== undefined
) {
postToolHookInfos.push({
command: att.command,
durationMs: att.durationMs,
command: att.command as string,
durationMs: att.durationMs as number,
})
}
}
@@ -1522,8 +1522,8 @@ async function checkPermissionsAndCallTool(
att.durationMs !== undefined
) {
postToolHookInfos.push({
command: att.command,
durationMs: att.durationMs,
command: att.command as string,
durationMs: att.durationMs as number,
})
}
}

View File

@@ -130,7 +130,7 @@ async function* runToolsSerially(
for await (const update of runToolUse(
toolUse,
assistantMessages.find(_ =>
_.message.content.some(
Array.isArray(_.message.content) && _.message.content.some(
_ => _.type === 'tool_use' && _.id === toolUse.id,
),
)!,
@@ -163,7 +163,7 @@ async function* runToolsConcurrently(
yield* runToolUse(
toolUse,
assistantMessages.find(_ =>
_.message.content.some(
Array.isArray(_.message.content) && _.message.content.some(
_ => _.type === 'tool_use' && _.id === toolUse.id,
),
)!,