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

@@ -4,6 +4,9 @@
"workspaces": {
"": {
"name": "claude-code",
"dependencies": {
"@types/lodash-es": "^4.17.12",
},
"devDependencies": {
"@alcalzone/ansi-tokenize": "^0.3.0",
"@ant/claude-for-chrome-mcp": "workspace:*",
@@ -51,11 +54,18 @@
"@smithy/node-http-handler": "^4.5.1",
"@types/bun": "^1.3.11",
"@types/cacache": "^20.0.1",
"@types/picomatch": "^4.0.3",
"@types/plist": "^3.0.5",
"@types/proper-lockfile": "^4.1.4",
"@types/qrcode": "^1.5.6",
"@types/react": "^19.2.14",
"@types/react-reconciler": "^0.33.0",
"@types/semver": "^7.7.1",
"@types/sharp": "^0.32.0",
"@types/shell-quote": "^1.7.5",
"@types/stack-utils": "^2.0.3",
"@types/turndown": "^5.0.6",
"@types/ws": "^8.18.1",
"ajv": "^8.18.0",
"asciichart": "^1.5.25",
"audio-capture-napi": "workspace:*",
@@ -986,16 +996,30 @@
"@types/pg-pool": ["@types/pg-pool@2.0.7", "https://registry.npmmirror.com/@types/pg-pool/-/pg-pool-2.0.7.tgz", { "dependencies": { "@types/pg": "*" } }, "sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng=="],
"@types/picomatch": ["@types/picomatch@4.0.3", "https://registry.npmmirror.com/@types/picomatch/-/picomatch-4.0.3.tgz", {}, "sha512-iG0T6+nYJ9FAPmx9SsUlnwcq1ZVRuCXcVEvWnntoPlrOpwtSTKNDC9uVAxTsC3PUvJ+99n4RpAcNgBbHX3JSnQ=="],
"@types/plist": ["@types/plist@3.0.5", "https://registry.npmmirror.com/@types/plist/-/plist-3.0.5.tgz", { "dependencies": { "@types/node": "*", "xmlbuilder": ">=11.0.1" } }, "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA=="],
"@types/proper-lockfile": ["@types/proper-lockfile@4.1.4", "https://registry.npmmirror.com/@types/proper-lockfile/-/proper-lockfile-4.1.4.tgz", { "dependencies": { "@types/retry": "*" } }, "sha512-uo2ABllncSqg9F1D4nugVl9v93RmjxF6LJzQLMLDdPaXCUIDPeOJ21Gbqi43xNKzBi/WQ0Q0dICqufzQbMjipQ=="],
"@types/qrcode": ["@types/qrcode@1.5.6", "https://registry.npmmirror.com/@types/qrcode/-/qrcode-1.5.6.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw=="],
"@types/react": ["@types/react@19.2.14", "https://registry.npmmirror.com/@types/react/-/react-19.2.14.tgz", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
"@types/react-dom": ["@types/react-dom@19.2.3", "https://registry.npmmirror.com/@types/react-dom/-/react-dom-19.2.3.tgz", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
"@types/react-reconciler": ["@types/react-reconciler@0.33.0", "https://registry.npmmirror.com/@types/react-reconciler/-/react-reconciler-0.33.0.tgz", { "peerDependencies": { "@types/react": "*" } }, "sha512-HZOXsKT0tGI9LlUw2LuedXsVeB88wFa536vVL0M6vE8zN63nI+sSr1ByxmPToP5K5bukaVscyeCJcF9guVNJ1g=="],
"@types/retry": ["@types/retry@0.12.5", "https://registry.npmmirror.com/@types/retry/-/retry-0.12.5.tgz", {}, "sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw=="],
"@types/semver": ["@types/semver@7.7.1", "https://registry.npmmirror.com/@types/semver/-/semver-7.7.1.tgz", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="],
"@types/sharp": ["@types/sharp@0.32.0", "https://registry.npmmirror.com/@types/sharp/-/sharp-0.32.0.tgz", { "dependencies": { "sharp": "*" } }, "sha512-OOi3kL+FZDnPhVzsfD37J88FNeZh6gQsGcLc95NbeURRGvmSjeXiDcyWzF2o3yh/gQAUn2uhh/e+CPCa5nwAxw=="],
"@types/shell-quote": ["@types/shell-quote@1.7.5", "https://registry.npmmirror.com/@types/shell-quote/-/shell-quote-1.7.5.tgz", {}, "sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw=="],
"@types/stack-utils": ["@types/stack-utils@2.0.3", "https://registry.npmmirror.com/@types/stack-utils/-/stack-utils-2.0.3.tgz", {}, "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="],
"@types/tedious": ["@types/tedious@4.0.14", "https://registry.npmmirror.com/@types/tedious/-/tedious-4.0.14.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw=="],
"@types/turndown": ["@types/turndown@5.0.6", "https://registry.npmmirror.com/@types/turndown/-/turndown-5.0.6.tgz", {}, "sha512-ru00MoyeeouE5BX4gRL+6m/BsDfbRayOskWqUvh7CLGW+UXxHQItqALa38kKnOiZPqJrtzJUgAC2+F0rL1S4Pg=="],
@@ -1004,6 +1028,8 @@
"@types/wrap-ansi": ["@types/wrap-ansi@3.0.0", "https://registry.npmmirror.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", {}, "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g=="],
"@types/ws": ["@types/ws@8.18.1", "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
"@typespec/ts-http-runtime": ["@typespec/ts-http-runtime@0.3.4", "https://registry.npmmirror.com/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.4.tgz", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-CI0NhTrz4EBaa0U+HaaUZrJhPoso8sG7ZFya8uQoBA57fjzrjRSv87ekCjLZOFExN+gXE/z0xuN2QfH4H2HrLQ=="],
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],

View File

@@ -53,9 +53,10 @@
"docs:dev": "npx mintlify dev",
"rcs": "bun run scripts/rcs.ts"
},
"dependencies": {},
"dependencies": {
"@types/lodash-es": "^4.17.12"
},
"devDependencies": {
"openai": "^6.33.0",
"@alcalzone/ansi-tokenize": "^0.3.0",
"@ant/claude-for-chrome-mcp": "workspace:*",
"@ant/computer-use-input": "workspace:*",
@@ -68,6 +69,7 @@
"@anthropic-ai/sandbox-runtime": "^0.0.44",
"@anthropic-ai/sdk": "^0.80.0",
"@anthropic-ai/vertex-sdk": "^0.14.4",
"@anthropic/ink": "workspace:*",
"@aws-sdk/client-bedrock": "^3.1020.0",
"@aws-sdk/client-bedrock-runtime": "^3.1020.0",
"@aws-sdk/client-sts": "^3.1020.0",
@@ -101,11 +103,18 @@
"@smithy/node-http-handler": "^4.5.1",
"@types/bun": "^1.3.11",
"@types/cacache": "^20.0.1",
"@types/picomatch": "^4.0.3",
"@types/plist": "^3.0.5",
"@types/proper-lockfile": "^4.1.4",
"@types/qrcode": "^1.5.6",
"@types/react": "^19.2.14",
"@types/react-reconciler": "^0.33.0",
"@types/semver": "^7.7.1",
"@types/sharp": "^0.32.0",
"@types/shell-quote": "^1.7.5",
"@types/stack-utils": "^2.0.3",
"@types/turndown": "^5.0.6",
"@types/ws": "^8.18.1",
"ajv": "^8.18.0",
"asciichart": "^1.5.25",
"audio-capture-napi": "workspace:*",
@@ -132,7 +141,6 @@
"highlight.js": "^11.11.1",
"https-proxy-agent": "^8.0.0",
"ignore": "^7.0.5",
"@anthropic/ink": "workspace:*",
"image-processor-napi": "workspace:*",
"indent-string": "^5.0.0",
"jsonc-parser": "^3.3.1",
@@ -141,6 +149,7 @@
"lru-cache": "^11.2.7",
"marked": "^17.0.5",
"modifiers-napi": "workspace:*",
"openai": "^6.33.0",
"p-map": "^7.0.4",
"picomatch": "^4.0.4",
"plist": "^3.1.0",

View File

@@ -2148,7 +2148,7 @@ async function handleScreenshot(
const monitorNote = await buildMonitorNote(
adapter,
shot.displayId,
shot.displayId ?? 0,
overrides.lastScreenshot?.displayId,
overrides.onDisplayPinned !== undefined,
);
@@ -2217,7 +2217,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

@@ -563,16 +563,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,
@@ -1089,7 +1089,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.
@@ -1147,7 +1147,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

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

View File

@@ -55,7 +55,7 @@ export async function call(
AgentTool.call(
input,
context,
context.canUseTool,
context.canUseTool!,
lastAssistantMessage
).catch(error => {
logForDebugging(`Fork subagent async error: ${error}`, { level: 'error' })

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

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

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

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

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

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

@@ -4458,7 +4458,7 @@ async function run(): Promise<CommanderCommand> {
...(uploaderReady && {
onTurnComplete: (messages: MessageType[]) => {
void uploaderReady.then((uploader) =>
uploader?.(messages),
(uploader as ((msgs: MessageType[]) => void) | null)?.(messages),
);
},
}),
@@ -4616,13 +4616,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:
@@ -4649,7 +4649,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

@@ -2974,11 +2974,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(
@@ -3156,7 +3156,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>).
@@ -3419,7 +3419,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' });
@@ -3658,13 +3658,13 @@ export function REPL({
...prev,
initialMessage: null,
toolPermissionContext: updatedToolPermissionContext,
...(shouldStorePlanForVerification && {
...(shouldStorePlanForVerification ? {
pendingPlanVerification: {
plan: initialMsg.message.planContent as string,
verificationStarted: false,
verificationCompleted: false,
},
}),
} : {}),
};
});
@@ -4381,7 +4381,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',
@@ -4880,7 +4880,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

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

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

@@ -519,9 +519,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
}

View File

@@ -15,4 +15,17 @@ declare module "bun:ffi" {
export function dlopen<T extends Record<string, { args: readonly string[]; returns: string }>>(path: string, symbols: T): { symbols: { [K in keyof T]: (...args: unknown[]) => unknown }; close(): void };
}
//
// Third-party modules without @types packages
declare module 'bidi-js' {
function getEmbeddingLevels(text: string, defaultDirection?: string): { paragraphLevel: number; levels: Uint8Array }
function getReorderSegments(text: string, embeddingLevels: { paragraphLevel: number; levels: Uint8Array }, start?: number, end?: number): [number, number][]
function getVisualOrder(reorderSegments: [number, number][]): number[]
export { getEmbeddingLevels, getReorderSegments, getVisualOrder }
export default { getEmbeddingLevels, getReorderSegments, getVisualOrder }
}
declare module 'asciichart' {
function plot(series: number[] | number[][], config?: Record<string, unknown>): string
export { plot }
export default { plot }
}

View File

@@ -37,7 +37,7 @@ export type Message = {
isCompactSummary?: boolean
toolUseResult?: unknown
isVisibleInTranscriptOnly?: boolean
attachment?: { type: string; toolUseID?: string; [key: string]: unknown }
attachment?: { type: string; toolUseID?: string; [key: string]: unknown; addedNames: string[]; addedLines: string[]; removedNames: string[] }
message?: {
role?: string
id?: string
@@ -48,12 +48,19 @@ export type Message = {
[key: string]: unknown
}
export type AssistantMessage = Message & { type: 'assistant' }
export type AttachmentMessage<T = unknown> = Message & { type: 'attachment'; attachment: { type: string; [key: string]: unknown } }
export type AssistantMessage = Message & {
type: 'assistant'
message: NonNullable<Message['message']>
}
export type AttachmentMessage<T = { type: string; [key: string]: unknown }> = Message & { type: 'attachment'; attachment: T }
export type ProgressMessage<T = unknown> = Message & { type: 'progress'; data: T }
export type SystemLocalCommandMessage = Message & { type: 'system' }
export type SystemMessage = Message & { type: 'system' }
export type UserMessage = Message & { type: 'user' }
export type UserMessage = Message & {
type: 'user'
message: NonNullable<Message['message']>
imagePasteIds?: number[]
}
export type NormalizedUserMessage = UserMessage
export type RequestStartEvent = { type: string; [key: string]: unknown }
export type StreamEvent = { type: string; [key: string]: unknown }

View File

@@ -86,9 +86,9 @@ describe("createAssistantMessage", () => {
test("creates assistant message with string content", () => {
const msg = createAssistantMessage({ content: "hello" });
expect(msg.type).toBe("assistant");
expect(msg.message.role).toBe("assistant");
expect(msg.message.content).toHaveLength(1);
expect((msg.message.content[0] as any).text).toBe("hello");
expect(msg.message!.role).toBe("assistant");
expect(msg.message!.content![0] as any).toBeTruthy();
expect((msg.message!.content![0] as any).text).toBe("hello");
});
test("creates assistant message with content blocks", () => {
@@ -501,7 +501,7 @@ describe("normalizeMessagesForAPI", () => {
]);
const normalized = normalizeMessagesForAPI([assistant]);
const block = (normalized[0] as AssistantMessage).message.content[0] as any;
const block = (normalized[0] as AssistantMessage).message!.content![0] as any;
expect(block.type).toBe("tool_use");
expect(block._geminiThoughtSignature).toBe("sig-123");

View File

@@ -445,8 +445,8 @@ async function countBuiltInToolTokens(
if (messages) {
const deferredToolNameSet = new Set(deferredBuiltinTools.map(t => t.name))
for (const msg of messages) {
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 (
typeof block !== 'string' &&
'type' in block &&
@@ -683,8 +683,8 @@ export async function countMcpToolTokens(
if (isDeferred && messages) {
const mcpToolNameSet = new Set(mcpTools.map(t => t.name))
for (const msg of messages) {
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 (
typeof block !== 'string' &&
'type' in block &&
@@ -786,7 +786,7 @@ function processAssistantMessage(
breakdown: MessageBreakdown,
): void {
// Process each content block individually
const contentBlocks = Array.isArray(msg.message.content) ? msg.message.content : []
const contentBlocks = Array.isArray(msg.message!.content) ? msg.message!.content : []
for (const block of contentBlocks) {
const blockStr = jsonStringify(block)
const blockTokens = roughTokenCountEstimation(blockStr)
@@ -811,20 +811,19 @@ function processUserMessage(
toolUseIdToName: Map<string, string>,
): void {
// Handle both string and array content
if (typeof msg.message.content === 'string') {
if (typeof msg.message!.content === 'string') {
// Simple string content
const tokens = roughTokenCountEstimation(msg.message.content)
const tokens = roughTokenCountEstimation(msg.message!.content)
breakdown.userMessageTokens += tokens
return
}
// Process each content block individually
for (const block of msg.message.content) {
for (const block of (msg.message!.content ?? [])) {
const blockStr = jsonStringify(block)
const blockTokens = roughTokenCountEstimation(blockStr)
if ('type' in block && block.type === 'tool_result') {
breakdown.toolResultTokens += blockTokens
const toolUseId = 'tool_use_id' in block ? block.tool_use_id : undefined
const toolName =
(toolUseId ? toolUseIdToName.get(toolUseId) : undefined) || 'unknown'
@@ -874,8 +873,8 @@ async function approximateMessageTokens(
// Build a map of tool_use_id to tool_name for easier lookup
const toolUseIdToName = new Map<string, string>()
for (const msg of microcompactResult.messages) {
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 (typeof block !== 'string' && 'type' in block && block.type === 'tool_use') {
const toolUseId = 'id' in block ? (block.id as string) : undefined
const toolName =

View File

@@ -193,8 +193,8 @@ export function installAsciicastRecorder(): void {
) as typeof process.stdout.write
process.stdout.write = function (
chunk: string | Uint8Array,
encodingOrCb?: BufferEncoding | ((err?: Error) => void),
cb?: (err?: Error) => void,
encodingOrCb?: BufferEncoding | ((err?: Error | null) => void),
cb?: (err?: Error | null) => void,
): boolean {
// Record the output event
const elapsed = (performance.now() - startTime) / 1000

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