diff --git a/README.md b/README.md index c2a4721..8596c31 100644 --- a/README.md +++ b/README.md @@ -2,27 +2,254 @@ Anthropic 官方 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI 工具的源码反编译/逆向还原项目。目标是将 Claude Code 核心功能跑通,必要时删减次级能力。 -## 核心能力 +## 能力清单 -- API 通信(Anthropic SDK / Bedrock / Vertex) -- Bash / FileRead / FileWrite / FileEdit 等核心工具 -- REPL 交互界面(ink 终端渲染) -- 对话历史与会话管理 -- 权限系统 -- Agent / 子代理系统 +> ✅ = 已实现 ⚠️ = 部分实现 / 条件启用 ❌ = stub / 移除 / feature flag 关闭 -## 已删减模块 +### 核心系统 -| 模块 | 处理方式 | -|------|----------| -| Computer Use (`@ant/computer-use-*`) | stub | -| Claude for Chrome MCP | stub | -| Magic Docs / Voice Mode / LSP Server | 移除 | -| Analytics / GrowthBook / Sentry | 空实现 | -| Plugins / Marketplace / Desktop Upsell | 移除 | -| Ultraplan / Tungsten / Auto Dream | 移除 | -| MCP OAuth/IDP | 简化 | -| DAEMON / BRIDGE / BG_SESSIONS / TEMPLATES 等 | feature flag 关闭 | +| 能力 | 状态 | 说明 | +|------|------|------| +| REPL 交互界面(Ink 终端渲染) | ✅ | 主屏幕 5000+ 行,完整交互 | +| API 通信 — Anthropic Direct | ✅ | 支持 API Key + OAuth | +| API 通信 — AWS Bedrock | ✅ | 支持凭据刷新、Bearer Token | +| API 通信 — Google Vertex | ✅ | 支持 GCP 凭据刷新 | +| API 通信 — Azure Foundry | ✅ | 支持 API Key + Azure AD | +| 流式对话与工具调用循环 (`query.ts`) | ✅ | 1700+ 行,含自动压缩、token 追踪 | +| 会话引擎 (`QueryEngine.ts`) | ✅ | 1300+ 行,管理对话状态与归因 | +| 上下文构建(git status / CLAUDE.md / memory) | ✅ | `context.ts` 完整实现 | +| 权限系统(plan/auto/manual 模式) | ✅ | 6300+ 行,含 YOLO 分类器、路径验证、规则匹配 | +| Hook 系统(pre/post tool use) | ✅ | 支持 settings.json 配置 | +| 会话恢复 (`/resume`) | ✅ | 独立 ResumeConversation 屏幕 | +| Doctor 诊断 (`/doctor`) | ✅ | 版本、API、插件、沙箱检查 | +| 自动压缩 (compaction) | ✅ | auto-compact / micro-compact / API compact | + +### 工具 — 始终可用 + +| 工具 | 状态 | 说明 | +|------|------|------| +| BashTool | ✅ | Shell 执行,沙箱,权限检查 | +| FileReadTool | ✅ | 文件 / PDF / 图片 / Notebook 读取 | +| FileEditTool | ✅ | 字符串替换式编辑 + diff 追踪 | +| FileWriteTool | ✅ | 文件创建 / 覆写 + diff 生成 | +| NotebookEditTool | ✅ | Jupyter Notebook 单元格编辑 | +| AgentTool | ✅ | 子代理派生(fork / async / background / remote) | +| WebFetchTool | ✅ | URL 抓取 → Markdown → AI 摘要 | +| WebSearchTool | ✅ | 网页搜索 + 域名过滤 | +| AskUserQuestionTool | ✅ | 多问题交互提示 + 预览 | +| SendMessageTool | ✅ | 消息发送(peers / teammates / mailbox) | +| SkillTool | ✅ | 斜杠命令 / Skill 调用 | +| EnterPlanModeTool | ✅ | 进入计划模式 | +| ExitPlanModeTool (V2) | ✅ | 退出计划模式 | +| TodoWriteTool | ✅ | Todo 列表 v1 | +| BriefTool | ✅ | 简短消息 + 附件发送 | +| TaskOutputTool | ✅ | 后台任务输出读取 | +| TaskStopTool | ✅ | 后台任务停止 | +| ListMcpResourcesTool | ✅ | MCP 资源列表 | +| ReadMcpResourceTool | ✅ | MCP 资源读取 | +| SyntheticOutputTool | ✅ | 非交互会话结构化输出 | + +### 工具 — 条件启用 + +| 工具 | 状态 | 启用条件 | +|------|------|----------| +| GlobTool | ✅ | 未嵌入 bfs/ugrep 时启用(默认启用) | +| GrepTool | ✅ | 同上 | +| TaskCreateTool | ⚠️ | `isTodoV2Enabled()` 为 true 时 | +| TaskGetTool | ⚠️ | 同上 | +| TaskUpdateTool | ⚠️ | 同上 | +| TaskListTool | ⚠️ | 同上 | +| EnterWorktreeTool | ⚠️ | `isWorktreeModeEnabled()` | +| ExitWorktreeTool | ⚠️ | 同上 | +| TeamCreateTool | ⚠️ | `isAgentSwarmsEnabled()` | +| TeamDeleteTool | ⚠️ | 同上 | +| ToolSearchTool | ⚠️ | `isToolSearchEnabledOptimistic()` | +| PowerShellTool | ⚠️ | Windows 平台检测 | +| LSPTool | ⚠️ | `ENABLE_LSP_TOOL` 环境变量 | +| ConfigTool | ❌ | `USER_TYPE === 'ant'`(永远为 false) | + +### 工具 — Feature Flag 关闭(全部不可用) + +| 工具 | Feature Flag | +|------|-------------| +| SleepTool | `PROACTIVE` / `KAIROS` | +| CronCreate/Delete/ListTool | `AGENT_TRIGGERS` | +| RemoteTriggerTool | `AGENT_TRIGGERS_REMOTE` | +| MonitorTool | `MONITOR_TOOL` | +| SendUserFileTool | `KAIROS` | +| OverflowTestTool | `OVERFLOW_TEST_TOOL` | +| TerminalCaptureTool | `TERMINAL_PANEL` | +| WebBrowserTool | `WEB_BROWSER_TOOL` | +| SnipTool | `HISTORY_SNIP` | +| WorkflowTool | `WORKFLOW_SCRIPTS` | +| PushNotificationTool | `KAIROS` | +| SubscribePRTool | `KAIROS_GITHUB_WEBHOOKS` | +| ListPeersTool | `UDS_INBOX` | +| CtxInspectTool | `CONTEXT_COLLAPSE` | + +### 工具 — Stub / 不可用 + +| 工具 | 说明 | +|------|------| +| TungstenTool | ANT-ONLY stub | +| REPLTool | ANT-ONLY,`isEnabled: () => false` | +| SuggestBackgroundPRTool | ANT-ONLY,`isEnabled: () => false` | +| VerifyPlanExecutionTool | 需 `CLAUDE_CODE_VERIFY_PLAN=true` 环境变量,且为 stub | +| ReviewArtifactTool | stub,未注册到 tools.ts | +| DiscoverSkillsTool | stub,未注册到 tools.ts | + +### 斜杠命令 — 可用 + +| 命令 | 状态 | 说明 | +|------|------|------| +| `/add-dir` | ✅ | 添加目录 | +| `/advisor` | ✅ | Advisor 配置 | +| `/agents` | ✅ | 代理列表/管理 | +| `/branch` | ✅ | 分支管理 | +| `/btw` | ✅ | 快速备注 | +| `/chrome` | ✅ | Chrome 集成 | +| `/clear` | ✅ | 清屏 | +| `/color` | ✅ | Agent 颜色 | +| `/compact` | ✅ | 压缩对话 | +| `/config` (`/settings`) | ✅ | 配置管理 | +| `/context` | ✅ | 上下文信息 | +| `/copy` | ✅ | 复制最后消息 | +| `/cost` | ✅ | 会话费用 | +| `/desktop` | ✅ | Claude Desktop 集成 | +| `/diff` | ✅ | 显示 diff | +| `/doctor` | ✅ | 健康检查 | +| `/effort` | ✅ | 设置 effort 等级 | +| `/exit` | ✅ | 退出 | +| `/export` | ✅ | 导出对话 | +| `/extra-usage` | ✅ | 额外用量信息 | +| `/fast` | ✅ | 切换 fast 模式 | +| `/feedback` | ✅ | 反馈 | +| `/files` | ✅ | 已跟踪文件 | +| `/heapdump` | ✅ | Heap dump(调试) | +| `/help` | ✅ | 帮助 | +| `/hooks` | ✅ | Hook 管理 | +| `/ide` | ✅ | IDE 连接 | +| `/init` | ✅ | 初始化项目 | +| `/install-github-app` | ✅ | 安装 GitHub App | +| `/install-slack-app` | ✅ | 安装 Slack App | +| `/keybindings` | ✅ | 快捷键管理 | +| `/login` / `/logout` | ✅ | 登录 / 登出 | +| `/mcp` | ✅ | MCP 服务管理 | +| `/memory` | ✅ | Memory / CLAUDE.md 管理 | +| `/mobile` | ✅ | 移动端 QR 码 | +| `/model` | ✅ | 模型选择 | +| `/output-style` | ✅ | 输出风格 | +| `/passes` | ✅ | 推荐码 | +| `/permissions` | ✅ | 权限管理 | +| `/plan` | ✅ | 计划模式 | +| `/plugin` | ✅ | 插件管理 | +| `/pr-comments` | ✅ | PR 评论 | +| `/privacy-settings` | ✅ | 隐私设置 | +| `/rate-limit-options` | ✅ | 限速选项 | +| `/release-notes` | ✅ | 更新日志 | +| `/reload-plugins` | ✅ | 重载插件 | +| `/remote-env` | ✅ | 远程环境配置 | +| `/rename` | ✅ | 重命名会话 | +| `/resume` | ✅ | 恢复会话 | +| `/review` | ✅ | 代码审查(本地) | +| `/ultrareview` | ✅ | 云端审查 | +| `/rewind` | ✅ | 回退对话 | +| `/sandbox-toggle` | ✅ | 切换沙箱 | +| `/security-review` | ✅ | 安全审查 | +| `/session` | ✅ | 会话信息 | +| `/skills` | ✅ | Skill 管理 | +| `/stats` | ✅ | 会话统计 | +| `/status` | ✅ | 状态信息 | +| `/statusline` | ✅ | 状态栏 UI | +| `/stickers` | ✅ | 贴纸 | +| `/tasks` | ✅ | 任务管理 | +| `/theme` | ✅ | 终端主题 | +| `/think-back` | ✅ | 年度回顾 | +| `/upgrade` | ✅ | 升级 CLI | +| `/usage` | ✅ | 用量信息 | +| `/insights` | ✅ | 使用分析报告 | +| `/vim` | ✅ | Vim 模式 | + +### 斜杠命令 — Feature Flag 关闭 + +| 命令 | Feature Flag | +|------|-------------| +| `/voice` | `VOICE_MODE` | +| `/proactive` | `PROACTIVE` / `KAIROS` | +| `/brief` | `KAIROS` / `KAIROS_BRIEF` | +| `/assistant` | `KAIROS` | +| `/bridge` | `BRIDGE_MODE` | +| `/remote-control-server` | `DAEMON` + `BRIDGE_MODE` | +| `/force-snip` | `HISTORY_SNIP` | +| `/workflows` | `WORKFLOW_SCRIPTS` | +| `/web-setup` | `CCR_REMOTE_SETUP` | +| `/subscribe-pr` | `KAIROS_GITHUB_WEBHOOKS` | +| `/ultraplan` | `ULTRAPLAN` | +| `/torch` | `TORCH` | +| `/peers` | `UDS_INBOX` | +| `/fork` | `FORK_SUBAGENT` | +| `/buddy` | `BUDDY` | + +### 斜杠命令 — ANT-ONLY(不可用) + +`/tag` `/backfill-sessions` `/break-cache` `/bughunter` `/commit` `/commit-push-pr` `/ctx_viz` `/good-claude` `/issue` `/init-verifiers` `/mock-limits` `/bridge-kick` `/version` `/reset-limits` `/onboarding` `/share` `/summary` `/teleport` `/ant-trace` `/perf-issue` `/env` `/oauth-refresh` `/debug-tool-call` `/agents-platform` `/autofix-pr` + +### CLI 子命令 + +| 子命令 | 状态 | 说明 | +|--------|------|------| +| `claude`(默认) | ✅ | 主 REPL / 交互 / print 模式 | +| `claude mcp serve/add/remove/list/get/...` | ✅ | MCP 服务管理(7 个子命令) | +| `claude auth login/status/logout` | ✅ | 认证管理 | +| `claude plugin validate/list/install/...` | ✅ | 插件管理(7 个子命令) | +| `claude setup-token` | ✅ | 长效 Token 配置 | +| `claude agents` | ✅ | 代理列表 | +| `claude doctor` | ✅ | 健康检查 | +| `claude update` / `upgrade` | ✅ | 自动更新 | +| `claude install [target]` | ✅ | Native 安装 | +| `claude server` | ❌ | `DIRECT_CONNECT` flag | +| `claude ssh ` | ❌ | `SSH_REMOTE` flag | +| `claude open ` | ❌ | `DIRECT_CONNECT` flag | +| `claude auto-mode` | ❌ | `TRANSCRIPT_CLASSIFIER` flag | +| `claude remote-control` | ❌ | `BRIDGE_MODE` + `DAEMON` flag | +| `claude assistant` | ❌ | `KAIROS` flag | +| `claude up/rollback/log/error/export/task/completion` | ❌ | ANT-ONLY | + +### 服务层 + +| 服务 | 状态 | 说明 | +|------|------|------| +| API 客户端 (`services/api/`) | ✅ | 3400+ 行,4 个 provider | +| MCP (`services/mcp/`) | ✅ | 24 个文件,12000+ 行 | +| OAuth (`services/oauth/`) | ✅ | 完整 OAuth 流程 | +| 插件 (`services/plugins/`) | ✅ | 基础设施完整,无内置插件 | +| LSP (`services/lsp/`) | ⚠️ | 实现存在,默认关闭 | +| 压缩 (`services/compact/`) | ✅ | auto / micro / API 压缩 | +| Hook 系统 (`services/tools/toolHooks.ts`) | ✅ | pre/post tool use hooks | +| 会话记忆 (`services/SessionMemory/`) | ✅ | 会话记忆管理 | +| 记忆提取 (`services/extractMemories/`) | ✅ | 自动记忆提取 | +| Skill 搜索 (`services/skillSearch/`) | ✅ | 本地/远程 skill 搜索 | +| 策略限制 (`services/policyLimits/`) | ✅ | 策略限制执行 | +| 分析 / GrowthBook / Sentry | ⚠️ | 框架存在,实际 sink 为空 | +| Voice (`services/voice.ts`) | ❌ | `VOICE_MODE` flag 关闭 | + +### 内部包 (`packages/`) + +| 包 | 状态 | 说明 | +|------|------|------| +| `color-diff-napi` | ✅ | 997 行完整 TypeScript 实现(语法高亮 diff) | +| `audio-capture-napi` | ❌ | stub,`isNativeAudioAvailable()` 返回 false | +| `image-processor-napi` | ❌ | stub,`getNativeModule()` 返回 null | +| `modifiers-napi` | ❌ | stub,`isModifierPressed()` 返回 false | +| `url-handler-napi` | ❌ | stub,`waitForUrlEvent()` 返回 null | +| `@ant/claude-for-chrome-mcp` | ❌ | stub,`createServer()` 返回 null | +| `@ant/computer-use-mcp` | ❌ | stub,`buildTools()` 返回 [] | +| `@ant/computer-use-input` | ❌ | stub,仅类型声明 | +| `@ant/computer-use-swift` | ❌ | stub,仅类型声明 | + +### Feature Flags(30 个,全部返回 `false`) + +`ABLATION_BASELINE` `AGENT_MEMORY_SNAPSHOT` `BG_SESSIONS` `BRIDGE_MODE` `BUDDY` `CCR_MIRROR` `CCR_REMOTE_SETUP` `CHICAGO_MCP` `COORDINATOR_MODE` `DAEMON` `DIRECT_CONNECT` `EXPERIMENTAL_SKILL_SEARCH` `FORK_SUBAGENT` `HARD_FAIL` `HISTORY_SNIP` `KAIROS` `KAIROS_BRIEF` `KAIROS_CHANNELS` `KAIROS_GITHUB_WEBHOOKS` `LODESTONE` `MCP_SKILLS` `PROACTIVE` `SSH_REMOTE` `TORCH` `TRANSCRIPT_CLASSIFIER` `UDS_INBOX` `ULTRAPLAN` `UPLOAD_USER_SETTINGS` `VOICE_MODE` `WEB_BROWSER_TOOL` `WORKFLOW_SCRIPTS` ## 快速开始 diff --git a/package.json b/package.json index d8124f6..2a3dfdf 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,18 @@ { - "name": "claude-code", + "name": "claude-js", "version": "1.0.0", - "private": true, "type": "module", "workspaces": [ "packages/*", "packages/@ant/*" ], + "files": [ + "dist" + ], "scripts": { "build": "bun build src/entrypoints/cli.tsx --outdir dist --target bun", - "dev": "bun run src/entrypoints/cli.tsx" + "dev": "bun run src/entrypoints/cli.tsx", + "prepublishOnly": "bun run build" }, "dependencies": { "@alcalzone/ansi-tokenize": "^0.3.0", diff --git a/packages/color-diff-napi/src/index.ts b/packages/color-diff-napi/src/index.ts index 0f9a808..779e23b 100644 --- a/packages/color-diff-napi/src/index.ts +++ b/packages/color-diff-napi/src/index.ts @@ -30,7 +30,7 @@ import { basename, extname } from 'path' // pushed later tests in the same shard into GC-pause territory and a // beforeEach/afterEach hook timeout (officialRegistry.test.ts, PR #24150). // Same lazy pattern the NAPI wrapper used for dlopen. -type HLJSApi = typeof hljsNamespace +type HLJSApi = typeof hljsNamespace.default let cachedHljs: HLJSApi | null = null function hljs(): HLJSApi { if (cachedHljs) return cachedHljs diff --git a/src/bootstrap/state.ts b/src/bootstrap/state.ts index 31a3e1c..e160e16 100644 --- a/src/bootstrap/state.ts +++ b/src/bootstrap/state.ts @@ -1755,4 +1755,4 @@ export function getPromptId(): string | null { export function setPromptId(id: string | null): void { STATE.promptId = id } -export type isReplBridgeActive = any; +export function isReplBridgeActive(): boolean { return false; } diff --git a/src/cli/handlers/auth.ts b/src/cli/handlers/auth.ts index c4cba5d..8b92c7d 100644 --- a/src/cli/handlers/auth.ts +++ b/src/cli/handlers/auth.ts @@ -159,7 +159,7 @@ export async function authLogin({ const orgResult = await validateForceLoginOrg() if (!orgResult.valid) { - process.stderr.write(orgResult.message + '\n') + process.stderr.write((orgResult as { valid: false; message: string }).message + '\n') process.exit(1) } @@ -209,7 +209,7 @@ export async function authLogin({ const orgResult = await validateForceLoginOrg() if (!orgResult.valid) { - process.stderr.write(orgResult.message + '\n') + process.stderr.write((orgResult as { valid: false; message: string }).message + '\n') process.exit(1) } diff --git a/src/cli/handlers/mcp.tsx b/src/cli/handlers/mcp.tsx index fef01d9..c144d04 100644 --- a/src/cli/handlers/mcp.tsx +++ b/src/cli/handlers/mcp.tsx @@ -178,9 +178,9 @@ export async function mcpListHandler(): Promise { // biome-ignore lint/suspicious/noConsole:: intentional console output console.log(`${name}: ${server.url} - ${status}`); } else if (!server.type || server.type === 'stdio') { - const args = Array.isArray(server.args) ? server.args : []; + const args = Array.isArray((server as any).args) ? (server as any).args : []; // biome-ignore lint/suspicious/noConsole:: intentional console output - console.log(`${name}: ${server.command} ${args.join(' ')} - ${status}`); + console.log(`${name}: ${(server as any).command} ${args.join(' ')} - ${status}`); } } } diff --git a/src/cli/print.ts b/src/cli/print.ts index 12d9809..d70d5cd 100644 --- a/src/cli/print.ts +++ b/src/cli/print.ts @@ -1243,7 +1243,7 @@ function runHeadlessStreaming( uuid: crumb.uuid, timestamp: crumb.timestamp, isReplay: true, - } satisfies SDKUserMessageReplay) + } as SDKUserMessageReplay) } } } @@ -1980,7 +1980,7 @@ function runHeadlessStreaming( parent_tool_use_id: null, uuid: c.uuid as string, isReplay: true, - } satisfies SDKUserMessageReplay) + } as SDKUserMessageReplay) } } } @@ -2265,8 +2265,8 @@ function runHeadlessStreaming( output.enqueue({ type: 'system' as const, subtype: 'files_persisted' as const, - files: result.persistedFiles, - failed: result.failedFiles, + files: (result as any).persistedFiles, + failed: (result as any).failedFiles, processed_at: new Date().toISOString(), uuid: randomUUID(), session_id: getSessionId(), diff --git a/src/cli/transports/ccrClient.ts b/src/cli/transports/ccrClient.ts index da3dc2e..3e40ae1 100644 --- a/src/cli/transports/ccrClient.ts +++ b/src/cli/transports/ccrClient.ts @@ -375,7 +375,7 @@ export class CCRClient { if (!result.ok) { throw new RetryableError( 'client event POST failed', - result.retryAfterMs, + (result as any).retryAfterMs, ) } }, @@ -398,7 +398,7 @@ export class CCRClient { if (!result.ok) { throw new RetryableError( 'internal event POST failed', - result.retryAfterMs, + (result as any).retryAfterMs, ) } }, @@ -427,7 +427,7 @@ export class CCRClient { 'delivery batch', ) if (!result.ok) { - throw new RetryableError('delivery POST failed', result.retryAfterMs) + throw new RetryableError('delivery POST failed', (result as any).retryAfterMs) } }, baseDelayMs: 500, diff --git a/src/commands/branch/branch.ts b/src/commands/branch/branch.ts index 4a7c277..a4742bf 100644 --- a/src/commands/branch/branch.ts +++ b/src/commands/branch/branch.ts @@ -38,7 +38,7 @@ type TranscriptEntry = TranscriptMessage & { export function deriveFirstPrompt( firstUserMessage: Extract | undefined, ): string { - const content = firstUserMessage?.message?.content + const content = (firstUserMessage as any)?.message?.content if (!content) return 'Branched conversation' const raw = typeof content === 'string' @@ -240,7 +240,7 @@ export async function call( // Build LogOption for resume const now = new Date() const firstPrompt = deriveFirstPrompt( - serializedMessages.find(m => m.type === 'user'), + serializedMessages.find(m => m.type === 'user') as Extract | undefined, ) // Save custom title - use provided title or firstPrompt as default diff --git a/src/commands/plugin/BrowseMarketplace.tsx b/src/commands/plugin/BrowseMarketplace.tsx index fe270d1..2f1d7f1 100644 --- a/src/commands/plugin/BrowseMarketplace.tsx +++ b/src/commands/plugin/BrowseMarketplace.tsx @@ -140,7 +140,7 @@ export function BrowseMarketplace({ } of marketplaces_0) { if (marketplace) { // Count how many plugins from this marketplace are installed - const installedFromThisMarketplace = count(marketplace.plugins, plugin => isPluginInstalled(createPluginId(plugin.name, name))); + const installedFromThisMarketplace = count(marketplace.plugins, (plugin: any) => isPluginInstalled(createPluginId(plugin.name, name))); marketplaceInfos.push({ name, totalPlugins: marketplace.plugins.length, @@ -334,7 +334,7 @@ export function BrowseMarketplace({ failureCount++; newFailedPlugins.push({ name: plugin_1.entry.name, - reason: result.error + reason: (result as { success: false; error: string }).error }); } } @@ -397,7 +397,7 @@ export function BrowseMarketplace({ }); } else { setIsInstalling(false); - setInstallError(result_0.error); + setInstallError((result_0 as { success: false; error: string }).error); } }; diff --git a/src/commands/plugin/DiscoverPlugins.tsx b/src/commands/plugin/DiscoverPlugins.tsx index 540ed97..f8378b8 100644 --- a/src/commands/plugin/DiscoverPlugins.tsx +++ b/src/commands/plugin/DiscoverPlugins.tsx @@ -248,7 +248,7 @@ export function DiscoverPlugins({ failureCount++; newFailedPlugins.push({ name: plugin_0.entry.name, - reason: result.error + reason: (result as { success: false; error: string }).error }); } } @@ -306,7 +306,7 @@ export function DiscoverPlugins({ }); } else { setIsInstalling(false); - setInstallError(result_0.error); + setInstallError((result_0 as { success: false; error: string }).error); } }; diff --git a/src/commands/remote-setup/remote-setup.tsx b/src/commands/remote-setup/remote-setup.tsx index 8061ce8..e51f2e8 100644 --- a/src/commands/remote-setup/remote-setup.tsx +++ b/src/commands/remote-setup/remote-setup.tsx @@ -130,11 +130,12 @@ function Web({ }); const result = await importGithubToken(token); if (!result.ok) { + const importErr = (result as { ok: false; error: ImportTokenError }).error; logEvent('tengu_remote_setup_result', { result: 'import_failed' as SafeString, - error_kind: result.error.kind as SafeString + error_kind: importErr.kind as SafeString }); - onDone(errorMessage(result.error, getCodeWebUrl())); + onDone(errorMessage(importErr, getCodeWebUrl())); return; } diff --git a/src/commands/reset-limits/index.ts b/src/commands/reset-limits/index.ts index 8122d9a..1b03426 100644 --- a/src/commands/reset-limits/index.ts +++ b/src/commands/reset-limits/index.ts @@ -1,3 +1,4 @@ -// Auto-generated type stub — replace with real implementation -export type resetLimits = any; -export type resetLimitsNonInteractive = any; +// Auto-generated stub — replace with real implementation +const stub = { isEnabled: () => false, isHidden: true, name: 'stub' }; +export const resetLimits = stub; +export const resetLimitsNonInteractive = stub; diff --git a/src/commands/resume/resume.tsx b/src/commands/resume/resume.tsx index a3dcb80..4764089 100644 --- a/src/commands/resume/resume.tsx +++ b/src/commands/resume/resume.tsx @@ -154,11 +154,12 @@ function ResumeCommand({ } // Different project - show command instead of resuming - const raw = await setClipboard(crossProjectCheck.command); + const crossCmd = (crossProjectCheck as { isCrossProject: true; isSameRepoWorktree: false; command: string }).command; + const raw = await setClipboard(crossCmd); if (raw) process.stdout.write(raw); // Format the output message - const message = ['', 'This conversation is from a different directory.', '', 'To resume, run:', ` ${crossProjectCheck.command}`, '', '(Command copied to clipboard)', ''].join('\n'); + const message = ['', 'This conversation is from a different directory.', '', 'To resume, run:', ` ${crossCmd}`, '', '(Command copied to clipboard)', ''].join('\n'); onDone(message, { display: 'user' }); diff --git a/src/commands/review/reviewRemote.ts b/src/commands/review/reviewRemote.ts index 0b80e33..24b0f94 100644 --- a/src/commands/review/reviewRemote.ts +++ b/src/commands/review/reviewRemote.ts @@ -136,7 +136,7 @@ export async function launchRemoteReview( // consume at session creation routes billing: first N zero-rate, then // anthropic:cccr org-service-key (overage-only). if (!eligibility.eligible) { - const blockers = eligibility.errors.filter( + const blockers = (eligibility as { eligible: false; errors: Array<{ type: string }> }).errors.filter( e => e.type !== 'no_remote_environment', ) if (blockers.length > 0) { diff --git a/src/commands/ultraplan.tsx b/src/commands/ultraplan.tsx index 051b923..0d52baa 100644 --- a/src/commands/ultraplan.tsx +++ b/src/commands/ultraplan.tsx @@ -314,11 +314,12 @@ async function launchDetached(opts: { const model = getUltraplanModel(); const eligibility = await checkRemoteAgentEligibility(); if (!eligibility.eligible) { + const eligErrors = (eligibility as { eligible: false; errors: Array<{ type: string }> }).errors; logEvent('tengu_ultraplan_create_failed', { reason: 'precondition' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, - precondition_errors: eligibility.errors.map(e => e.type).join(',') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + precondition_errors: eligErrors.map(e => e.type).join(',') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS }); - const reasons = eligibility.errors.map(formatPreconditionError).join('\n'); + const reasons = eligErrors.map(formatPreconditionError).join('\n'); enqueuePendingNotification({ value: `ultraplan: cannot launch remote session —\n${reasons}`, mode: 'task-notification' diff --git a/src/components/ConsoleOAuthFlow.tsx b/src/components/ConsoleOAuthFlow.tsx index 14f1875..8d5fc12 100644 --- a/src/components/ConsoleOAuthFlow.tsx +++ b/src/components/ConsoleOAuthFlow.tsx @@ -235,7 +235,7 @@ export function ConsoleOAuthFlow({ await installOAuthTokens(result); const orgResult = await validateForceLoginOrg(); if (!orgResult.valid) { - throw new Error(orgResult.message); + throw new Error((orgResult as { valid: false; message: string }).message); } setOAuthStatus({ state: 'success' diff --git a/src/components/Feedback.tsx b/src/components/Feedback.tsx index a82d92c..d03bdbb 100644 --- a/src/components/Feedback.tsx +++ b/src/components/Feedback.tsx @@ -59,8 +59,11 @@ type FeedbackData = { description: string; platform: string; gitRepo: boolean; + terminal: string; version: string | null; transcript: Message[]; + errors: unknown; + lastApiRequest: unknown; subagentTranscripts?: { [agentId: string]: Message[]; }; @@ -203,8 +206,8 @@ export function Feedback({ ...diskTranscripts, ...teammateTranscripts }; - const reportData = { - latestAssistantMessageId: lastAssistantMessageId, + const reportData: FeedbackData = { + latestAssistantMessageId: lastAssistantMessageId as string | null, message_count: messages.length, datetime: new Date().toISOString(), description, diff --git a/src/components/FileEditToolDiff.tsx b/src/components/FileEditToolDiff.tsx index 7a3bf9d..7b32195 100644 --- a/src/components/FileEditToolDiff.tsx +++ b/src/components/FileEditToolDiff.tsx @@ -50,7 +50,7 @@ export function FileEditToolDiff(props) { } return t2; } -function DiffBody(t0) { +function DiffBody(t0: { promise: Promise; file_path: string }) { const $ = _c(6); const { promise, diff --git a/src/components/HighlightedCode/Fallback.tsx b/src/components/HighlightedCode/Fallback.tsx index 23e16f4..2a20c9a 100644 --- a/src/components/HighlightedCode/Fallback.tsx +++ b/src/components/HighlightedCode/Fallback.tsx @@ -134,7 +134,7 @@ function Highlighted(t0) { } else { t1 = $[0]; } - const hl = use(t1); + const hl = use(t1) as NonNullable>> | null; let t2; if ($[1] !== codeWithSpaces || $[2] !== hl || $[3] !== language) { bb0: { diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx index 51b11a4..dc089df 100644 --- a/src/components/Markdown.tsx +++ b/src/components/Markdown.tsx @@ -129,7 +129,7 @@ function MarkdownBody(t0) { } = t0; const [theme] = useTheme(); configureMarked(); - let elements; + let elements: React.ReactNode[]; if ($[0] !== children || $[1] !== dimColor || $[2] !== highlight || $[3] !== theme) { const tokens = cachedLexer(stripPromptXMLTags(children)); elements = []; @@ -156,7 +156,7 @@ function MarkdownBody(t0) { $[3] = theme; $[4] = elements; } else { - elements = $[4]; + elements = $[4] as React.ReactNode[]; } const elements_0 = elements; let t1; diff --git a/src/components/Message.tsx b/src/components/Message.tsx index 5d316a4..02f5fb2 100644 --- a/src/components/Message.tsx +++ b/src/components/Message.tsx @@ -606,7 +606,7 @@ export function areMessagePropsEqual(prev: Props, next: Props): boolean { // Only re-render on lastThinkingBlockId change if this message actually // has thinking content — otherwise every message in scrollback re-renders // whenever streaming thinking starts/stops (CC-941). - if (prev.lastThinkingBlockId !== next.lastThinkingBlockId && hasThinkingContent(next.message)) { + if (prev.lastThinkingBlockId !== next.lastThinkingBlockId && hasThinkingContent(next.message as any)) { return false; } // Verbose toggle changes thinking block visibility/expansion diff --git a/src/components/MessageSelector.tsx b/src/components/MessageSelector.tsx index cf7ff4e..888ec93 100644 --- a/src/components/MessageSelector.tsx +++ b/src/components/MessageSelector.tsx @@ -334,7 +334,7 @@ export function MessageSelector({ - ({formatRelativeTimeAgo(new Date(messageToRestore.timestamp))}) + ({formatRelativeTimeAgo(new Date(messageToRestore.timestamp as number))}) diff --git a/src/components/Settings/Settings.tsx b/src/components/Settings/Settings.tsx index 1e77af3..e4cb31c 100644 --- a/src/components/Settings/Settings.tsx +++ b/src/components/Settings/Settings.tsx @@ -95,7 +95,8 @@ export function Settings(t0) { } let t7; if ($[13] !== contentHeight) { - t7 = false ? [] : []; + const GatesComponent = Gates as any; + t7 = false ? [] : []; $[13] = contentHeight; $[14] = t7; } else { diff --git a/src/components/Settings/Status.tsx b/src/components/Settings/Status.tsx index c5fc835..e6d116d 100644 --- a/src/components/Settings/Status.tsx +++ b/src/components/Settings/Status.tsx @@ -206,7 +206,7 @@ function Diagnostics(t0) { const { promise } = t0; - const diagnostics = use(promise); + const diagnostics = use(promise) as any[]; if (diagnostics.length === 0) { return null; } diff --git a/src/components/Spinner/GlimmerMessage.tsx b/src/components/Spinner/GlimmerMessage.tsx index 02b8200..4b8020a 100644 --- a/src/components/Spinner/GlimmerMessage.tsx +++ b/src/components/Spinner/GlimmerMessage.tsx @@ -272,10 +272,10 @@ export function GlimmerMessage(t0) { $[57] = shim; $[58] = colPos; } else { - before = $[55]; - after = $[56]; - shim = $[57]; - colPos = $[58]; + before = $[55] as string; + after = $[56] as string; + shim = $[57] as string; + colPos = $[58] as number; } let t3; if ($[59] !== before || $[60] !== messageColor) { diff --git a/src/components/StatusNotices.tsx b/src/components/StatusNotices.tsx index 4408ca9..9bd9ef5 100644 --- a/src/components/StatusNotices.tsx +++ b/src/components/StatusNotices.tsx @@ -31,7 +31,7 @@ export function StatusNotices(t0) { const context = { config: t1, agentDefinitions, - memoryFiles: use(t2) + memoryFiles: use(t2) as any }; const activeNotices = getActiveNotices(context); if (activeNotices.length === 0) { diff --git a/src/components/TextInput.tsx b/src/components/TextInput.tsx index f81c053..a705256 100644 --- a/src/components/TextInput.tsx +++ b/src/components/TextInput.tsx @@ -45,9 +45,9 @@ export default function TextInput(props: Props): React.ReactNode { // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant useVoiceState(s => s.voiceState) : 'idle' as const; const isVoiceRecording = voiceState === 'recording'; - const audioLevels = feature('VOICE_MODE') ? + const audioLevels = (feature('VOICE_MODE') ? // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant - useVoiceState(s_0 => s_0.voiceAudioLevels) : []; + useVoiceState(s_0 => s_0.voiceAudioLevels) : []) as number[]; const smoothedRef = useRef(new Array(CURSOR_WAVEFORM_WIDTH).fill(0)); const needsAnimation = isVoiceRecording && !reducedMotion; const [animRef, animTime] = feature('VOICE_MODE') ? diff --git a/src/components/ThemePicker.tsx b/src/components/ThemePicker.tsx index cca39ec..338ce23 100644 --- a/src/components/ThemePicker.tsx +++ b/src/components/ThemePicker.tsx @@ -72,7 +72,7 @@ export function ThemePicker(t0) { } = usePreviewTheme(); const syntaxHighlightingDisabled = useAppState(_temp) ?? false; const setAppState = useSetAppState(); - useRegisterKeybindingContext("ThemePicker"); + useRegisterKeybindingContext("ThemePicker", undefined); const syntaxToggleShortcut = useShortcutDisplay("theme:toggleSyntaxHighlighting", "ThemePicker", "ctrl+t"); let t8; if ($[3] !== setAppState || $[4] !== syntaxHighlightingDisabled) { diff --git a/src/components/VirtualMessageList.tsx b/src/components/VirtualMessageList.tsx index 5d54202..8c7e2f3 100644 --- a/src/components/VirtualMessageList.tsx +++ b/src/components/VirtualMessageList.tsx @@ -16,7 +16,7 @@ const HEADROOM = 3; import { logForDebugging } from '../utils/debug.js'; import { sleep } from '../utils/sleep.js'; import { renderableSearchText } from '../utils/transcriptSearch.js'; -import { isNavigableMessage, type MessageActionsNav, type MessageActionsState, type NavigableMessage, stripSystemReminders, toolCallOf } from './messageActions.js'; +import { isNavigableMessage, type MessageActionsNav, type MessageActionsState, type NavigableMessage, type NavigableType, stripSystemReminders, toolCallOf } from './messageActions.js'; // Fallback extractor: lower + cache here for callers without the // Messages.tsx tool-lookup path (tests, static contexts). Messages.tsx @@ -151,7 +151,7 @@ function computeStickyPromptText(msg: RenderableMessage): string | null { raw = block.text; } else if (msg.type === 'attachment' && msg.attachment.type === 'queued_command' && msg.attachment.commandMode !== 'task-notification' && !msg.attachment.isMeta) { const p = msg.attachment.prompt; - raw = typeof p === 'string' ? p : p.flatMap(b => b.type === 'text' ? [b.text] : []).join('\n'); + raw = typeof p === 'string' ? p : (p as any[]).flatMap(b => b.type === 'text' ? [b.text] : []).join('\n'); } if (raw === null) return null; const t = stripSystemReminders(raw); @@ -345,7 +345,7 @@ export function VirtualMessageList({ useImperativeHandle(cursorNavRef, (): MessageActionsNav => { const select = (m: NavigableMessage) => setCursor?.({ uuid: m.uuid, - msgType: m.type, + msgType: m.type as NavigableType, expanded: false, toolName: toolCallOf(m)?.name }); diff --git a/src/components/agents/ToolSelector.tsx b/src/components/agents/ToolSelector.tsx index 2dc942c..046f41b 100644 --- a/src/components/agents/ToolSelector.tsx +++ b/src/components/agents/ToolSelector.tsx @@ -251,7 +251,7 @@ export function ToolSelector(t0) { let t9; if ($[22] !== selectedSet) { t9 = bucketTools => { - const selected = count(bucketTools, t_5 => selectedSet.has(t_5.name)); + const selected = count(bucketTools, (t_5: any) => selectedSet.has(t_5.name)); const needsSelection = selected < bucketTools.length; return () => { const toolNames_1 = bucketTools.map(_temp4); @@ -321,7 +321,7 @@ export function ToolSelector(t0) { if (bucketTools_0.length === 0) { return; } - const selected_0 = count(bucketTools_0, t_8 => selectedSet.has(t_8.name)); + const selected_0 = count(bucketTools_0, (t_8: any) => selectedSet.has(t_8.name)); const isFullySelected = selected_0 === bucketTools_0.length; navigableItems.push({ id, diff --git a/src/components/memory/MemoryFileSelector.tsx b/src/components/memory/MemoryFileSelector.tsx index 9987aa9..5fd03a8 100644 --- a/src/components/memory/MemoryFileSelector.tsx +++ b/src/components/memory/MemoryFileSelector.tsx @@ -47,7 +47,7 @@ export function MemoryFileSelector(t0) { onSelect, onCancel } = t0; - const existingMemoryFiles = use(getMemoryFiles()); + const existingMemoryFiles = use(getMemoryFiles()) as MemoryFileInfo[]; const userMemoryPath = join(getClaudeConfigHomeDir(), "CLAUDE.md"); const projectMemoryPath = join(getOriginalCwd(), "CLAUDE.md"); const hasUserMemory = existingMemoryFiles.some(f => f.path === userMemoryPath); diff --git a/src/components/messages/AssistantToolUseMessage.tsx b/src/components/messages/AssistantToolUseMessage.tsx index ada997a..f0c8f02 100644 --- a/src/components/messages/AssistantToolUseMessage.tsx +++ b/src/components/messages/AssistantToolUseMessage.tsx @@ -337,7 +337,7 @@ function renderToolUseProgressMessage(tool: Tool, tools: Tools, lookups: ReturnT columns: number; rows: number; }): React.ReactNode { - const toolProgressMessages = progressMessagesForMessage.filter((msg): msg is ProgressMessage => msg.data.type !== 'hook_progress'); + const toolProgressMessages = progressMessagesForMessage.filter((msg): msg is ProgressMessage => (msg.data as { type?: string }).type !== 'hook_progress'); try { const toolMessages = tool.renderToolUseProgressMessage?.(toolProgressMessages, { tools, diff --git a/src/components/messages/nullRenderingAttachments.ts b/src/components/messages/nullRenderingAttachments.ts index 933fea0..6cb3a79 100644 --- a/src/components/messages/nullRenderingAttachments.ts +++ b/src/components/messages/nullRenderingAttachments.ts @@ -40,8 +40,6 @@ const NULL_RENDERING_TYPES = [ 'auto_mode', 'auto_mode_exit', 'output_token_usage', - 'pen_mode_enter', - 'pen_mode_exit', 'verify_plan_reminder', 'current_session_memory', 'compaction_reminder', @@ -65,6 +63,6 @@ export function isNullRenderingAttachment( ): boolean { return ( msg.type === 'attachment' && - NULL_RENDERING_ATTACHMENT_TYPES.has(msg.attachment.type) + NULL_RENDERING_ATTACHMENT_TYPES.has(msg.attachment.type as Attachment['type']) ) } diff --git a/src/components/permissions/AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.tsx b/src/components/permissions/AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.tsx index 9228821..8141153 100644 --- a/src/components/permissions/AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.tsx +++ b/src/components/permissions/AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.tsx @@ -140,7 +140,7 @@ function AskUserQuestionPermissionRequestBody(t0) { $[10] = theme; $[11] = maxHeight; } else { - maxHeight = $[11]; + maxHeight = $[11] as number; } const t3 = Math.min(Math.max(maxHeight, MIN_CONTENT_HEIGHT), maxAllowedHeight); const t4 = Math.max(maxWidth, MIN_CONTENT_WIDTH); @@ -177,7 +177,7 @@ function AskUserQuestionPermissionRequestBody(t0) { const pasteId = nextPasteIdRef.current; const newContent = { id: pasteId, - type: "image", + type: "image" as const, content: base64Image, mediaType: mediaType || "image/png", filename: filename || "Pasted image", diff --git a/src/components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.tsx b/src/components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.tsx index e1c0d9c..4741d0b 100644 --- a/src/components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.tsx +++ b/src/components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.tsx @@ -151,7 +151,7 @@ export function ExitPlanModePermissionRequest({ const options = useMemo(() => buildPlanApprovalOptions({ showClearContext, showUltraplan, - usedPercent: showClearContext ? getContextUsedPercent(usage, mode) : null, + usedPercent: showClearContext ? getContextUsedPercent(usage as any, mode) : null, isAutoModeAvailable, isBypassPermissionsModeAvailable, onFeedbackChange: setPlanFeedback diff --git a/src/components/permissions/PermissionDecisionDebugInfo.tsx b/src/components/permissions/PermissionDecisionDebugInfo.tsx index 6697e91..d877faa 100644 --- a/src/components/permissions/PermissionDecisionDebugInfo.tsx +++ b/src/components/permissions/PermissionDecisionDebugInfo.tsx @@ -62,7 +62,7 @@ function PermissionDecisionInfoItem(t0) { return {Array.from(decisionReason.reasons.entries()).map(t2 => { const [subcommand, result] = t2 as [string, { behavior: string; decisionReason?: { type: string }; suggestions?: unknown }]; const icon = result.behavior === "allow" ? color("success", theme)(figures.tick) : color("error", theme)(figures.cross); - return {icon} {subcommand}{result.decisionReason !== undefined && result.decisionReason.type !== "subcommandResults" && {" "}⎿{" "}{decisionReasonDisplayString(result.decisionReason)}}{result.behavior === "ask" && }; + return {icon} {subcommand}{result.decisionReason !== undefined && result.decisionReason.type !== "subcommandResults" && {" "}⎿{" "}{decisionReasonDisplayString(result.decisionReason as any)}}{result.behavior === "ask" && }; })}; } default: diff --git a/src/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx b/src/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx index d0ee51b..999a63f 100644 --- a/src/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx +++ b/src/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx @@ -102,7 +102,7 @@ function SedEditPermissionRequestInner(t0) { const { oldContent, fileExists - } = use(contentPromise); + } = use(contentPromise) as any; let t1; if ($[4] !== oldContent || $[5] !== sedInfo) { t1 = applySedSubstitution(oldContent, sedInfo); diff --git a/src/components/tasks/ShellDetailDialog.tsx b/src/components/tasks/ShellDetailDialog.tsx index 3d84ea2..d42472c 100644 --- a/src/components/tasks/ShellDetailDialog.tsx +++ b/src/components/tasks/ShellDetailDialog.tsx @@ -310,7 +310,7 @@ function ShellOutputContent(t0) { const { content, bytesTotal - } = use(outputPromise); + } = use(outputPromise) as any; if (!content) { let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { diff --git a/src/hooks/useCanUseTool.tsx b/src/hooks/useCanUseTool.tsx index 3676135..1ce77d8 100644 --- a/src/hooks/useCanUseTool.tsx +++ b/src/hooks/useCanUseTool.tsx @@ -132,11 +132,11 @@ function useCanUseTool(setToolUseConfirmQueue, setToolPermissionContext) { if (ctx.resolveIfAborted(resolve)) { return; } - if (raceResult.type === "result" && raceResult.result.matches && raceResult.result.confidence === "high" && feature("BASH_CLASSIFIER")) { + if ((raceResult as any).type === "result" && (raceResult as any).result.matches && (raceResult as any).result.confidence === "high" && feature("BASH_CLASSIFIER")) { consumeSpeculativeClassifierCheck((input as { command: string; }).command); - const matchedRule = raceResult.result.matchedDescription ?? undefined; + const matchedRule = (raceResult as any).result.matchedDescription ?? undefined; if (matchedRule) { setClassifierApproval(toolUseID, matchedRule); } @@ -150,7 +150,7 @@ function useCanUseTool(setToolUseConfirmQueue, setToolPermissionContext) { decisionReason: { type: "classifier" as const, classifier: "bash_allow" as const, - reason: `Allowed by prompt rule: "${raceResult.result.matchedDescription}"` + reason: `Allowed by prompt rule: "${(raceResult as any).result.matchedDescription}"` } })); return; diff --git a/src/hooks/useReplBridge.tsx b/src/hooks/useReplBridge.tsx index 86cad57..91fe582 100644 --- a/src/hooks/useReplBridge.tsx +++ b/src/hooks/useReplBridge.tsx @@ -370,7 +370,7 @@ export function useReplBridge(messages: Message[], setMessages: (action: React.S // Dispatch incoming control_response messages to registered handlers function handlePermissionResponse(msg_0: SDKControlResponse): void { - const requestId = msg_0.response?.request_id; + const requestId = (msg_0 as any).response?.request_id; if (!requestId) return; const handler = pendingPermissionHandlers.get(requestId); if (!handler) { @@ -379,7 +379,7 @@ export function useReplBridge(messages: Message[], setMessages: (action: React.S } pendingPermissionHandlers.delete(requestId); // Extract the permission decision from the control_response payload - const inner = msg_0.response; + const inner = (msg_0 as any).response; if (inner.subtype === 'success' && inner.response && isBridgePermissionResponse(inner.response)) { handler(inner.response); } diff --git a/src/hooks/useVoiceIntegration.tsx b/src/hooks/useVoiceIntegration.tsx index b402189..47de37c 100644 --- a/src/hooks/useVoiceIntegration.tsx +++ b/src/hooks/useVoiceIntegration.tsx @@ -225,9 +225,9 @@ export function useVoiceIntegration({ const voiceState = feature('VOICE_MODE') ? // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant useVoiceState(s => s.voiceState) : 'idle' as const; - const voiceInterimTranscript = feature('VOICE_MODE') ? + const voiceInterimTranscript: string = feature('VOICE_MODE') ? // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant - useVoiceState(s_0 => s_0.voiceInterimTranscript) : ''; + useVoiceState(s_0 => s_0.voiceInterimTranscript) as string : ''; // Set the voice anchor for focus mode (where recording starts via terminal // focus, not key hold). Key-hold sets the anchor in stripTrailing. diff --git a/src/ink/Ansi.tsx b/src/ink/Ansi.tsx index 10c38e0..5e51a7c 100644 --- a/src/ink/Ansi.tsx +++ b/src/ink/Ansi.tsx @@ -29,7 +29,7 @@ type SpanProps = { * * Memoized to prevent re-renders when parent changes but children string is the same. */ -export const Ansi = React.memo(function Ansi(t0) { +export const Ansi = React.memo(function Ansi(t0: { children: React.ReactNode; dimColor?: boolean }) { const $ = _c(12); const { children, diff --git a/src/services/api/client.ts b/src/services/api/client.ts index 8c1feb6..ccc9672 100644 --- a/src/services/api/client.ts +++ b/src/services/api/client.ts @@ -159,7 +159,7 @@ export async function getAnthropicClient({ ? process.env.ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION : getAWSRegion() - const bedrockArgs: ConstructorParameters[0] = { + const bedrockArgs: any = { ...ARGS, awsRegion, ...(isEnvTruthy(process.env.CLAUDE_CODE_SKIP_BEDROCK_AUTH) && { @@ -290,7 +290,7 @@ export async function getAnthropicClient({ const vertexArgs: ConstructorParameters[0] = { ...ARGS, region: getVertexRegionForModel(model), - googleAuth, + googleAuth: googleAuth as any, ...(isDebugToStdErr() && { logger: createStderrLogger() }), } // we have always been lying about the return type - this doesn't support batching or models diff --git a/src/services/api/errors.ts b/src/services/api/errors.ts index 1a7edc5..6ab10fc 100644 --- a/src/services/api/errors.ts +++ b/src/services/api/errors.ts @@ -108,7 +108,7 @@ export function getPromptTooLongTokenGap( return undefined } const { actualTokens, limitTokens } = parsePromptTooLongTokenCounts( - msg.errorDetails, + msg.errorDetails as string, ) if (actualTokens === undefined || limitTokens === undefined) { return undefined @@ -148,7 +148,7 @@ export function isMediaSizeErrorMessage(msg: AssistantMessage): boolean { return ( msg.isApiErrorMessage === true && msg.errorDetails !== undefined && - isMediaSizeError(msg.errorDetails) + isMediaSizeError(msg.errorDetails as string) ) } export const CREDIT_BALANCE_TOO_LOW_ERROR_MESSAGE = 'Credit balance is too low' diff --git a/src/services/mcp/config.ts b/src/services/mcp/config.ts index c5cee9c..288f9e0 100644 --- a/src/services/mcp/config.ts +++ b/src/services/mcp/config.ts @@ -1279,7 +1279,7 @@ export async function getAllMcpConfigs(): Promise<{ // Keys never collide (`slack` vs `claude.ai Slack`) so the merge below // won't catch this — need content-based dedup by URL signature. const { servers: dedupedClaudeAi } = dedupClaudeAiMcpServers( - claudeaiMcpServers, + claudeaiMcpServers as Record, claudeCodeServers, ) @@ -1351,6 +1351,7 @@ export function parseMcpConfig(params: { if ( getPlatform() === 'windows' && (!configToCheck.type || configToCheck.type === 'stdio') && + ('command' in configToCheck) && (configToCheck.command === 'npx' || configToCheck.command.endsWith('\\npx') || configToCheck.command.endsWith('/npx')) diff --git a/src/services/tools/toolHooks.ts b/src/services/tools/toolHooks.ts index cb2ef8f..cf6370a 100644 --- a/src/services/tools/toolHooks.ts +++ b/src/services/tools/toolHooks.ts @@ -99,7 +99,7 @@ export async function* runPostToolUseHooks( result.message.attachment.type === 'hook_blocking_error' ) ) { - yield { message: result.message } + yield { message: result.message as AttachmentMessage | ProgressMessage } } if (result.blockingError) { @@ -251,7 +251,7 @@ export async function* runPostToolUseFailureHooks( result.message.attachment.type === 'hook_blocking_error' ) ) { - yield { message: result.message } + yield { message: result.message as AttachmentMessage | ProgressMessage } } if (result.blockingError) { @@ -476,7 +476,7 @@ export async function* runPreToolUseHooks( )) { try { if (result.message) { - yield { type: 'message', message: { message: result.message } } + yield { type: 'message', message: { message: result.message as AttachmentMessage | ProgressMessage } } } if (result.blockingError) { const denialMessage = getPreToolHookBlockingMessage( diff --git a/src/services/vcr.ts b/src/services/vcr.ts index a57a34c..8c3ce6c 100644 --- a/src/services/vcr.ts +++ b/src/services/vcr.ts @@ -1,4 +1,4 @@ -import type { BetaContentBlock } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs' +import type { BetaContentBlock, BetaUsage } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs' import { createHash, randomUUID, type UUID } from 'crypto' import { mkdir, readFile, writeFile } from 'fs/promises' import isPlainObject from 'lodash-es/isPlainObject.js' @@ -166,8 +166,8 @@ function addCachedCostToTotalSessionCost( if (message.type === 'stream_event') { return } - const model = message.message.model - const usage = message.message.usage + const model = (message as AssistantMessage).message.model as string + const usage = (message as AssistantMessage).message.usage as BetaUsage const costUSD = calculateUSDCost(model, usage) addToTotalSessionCost(costUSD, usage, model) } @@ -251,7 +251,7 @@ function mapAssistantMessage( timestamp: message.timestamp, message: { ...message.message, - content: message.message.content + content: (message.message.content as BetaContentBlock[]) .map(_ => { switch (_.type) { case 'text': @@ -269,7 +269,7 @@ function mapAssistantMessage( return _ // Handle other block types unchanged } }) - .filter(Boolean) as BetaContentBlock[], + .filter(Boolean) as any, }, type: 'assistant', } @@ -282,7 +282,7 @@ function mapMessage( uuid?: UUID, ): AssistantMessage | SystemAPIErrorMessage | StreamEvent { if (message.type === 'assistant') { - return mapAssistantMessage(message, f, index, uuid) + return mapAssistantMessage(message as AssistantMessage, f, index, uuid) } else { return message } diff --git a/src/tasks/LocalShellTask/LocalShellTask.tsx b/src/tasks/LocalShellTask/LocalShellTask.tsx index 4cad71c..5955182 100644 --- a/src/tasks/LocalShellTask/LocalShellTask.tsx +++ b/src/tasks/LocalShellTask/LocalShellTask.tsx @@ -107,7 +107,7 @@ function enqueueShellNotification(taskId: string, description: string, status: ' // If the task was already marked as notified (e.g., by TaskStopTool), skip // enqueueing to avoid sending redundant messages to the model. let shouldEnqueue = false; - updateTaskState(taskId, setAppState, task => { + updateTaskState(taskId, setAppState, task => { if (task.notified) { return task; } @@ -479,7 +479,7 @@ export function backgroundExistingForegroundTask(taskId: string, shellCommand: S * carries the full output, so the would be redundant. */ export function markTaskNotified(taskId: string, setAppState: SetAppState): void { - updateTaskState(taskId, setAppState, t => t.notified ? t : { + updateTaskState(taskId, setAppState, t => t.notified ? t : { ...t, notified: true }); diff --git a/src/tools/AgentTool/runAgent.ts b/src/tools/AgentTool/runAgent.ts index e2cb942..8d2b0cf 100644 --- a/src/tools/AgentTool/runAgent.ts +++ b/src/tools/AgentTool/runAgent.ts @@ -760,17 +760,17 @@ export async function* runAgent({ // so TTFT/OTPS update during subagent execution. if ( message.type === 'stream_event' && - message.event.type === 'message_start' && - message.ttftMs != null + (message as any).event.type === 'message_start' && + (message as any).ttftMs != null ) { - toolUseContext.pushApiMetricsEntry?.(message.ttftMs) + toolUseContext.pushApiMetricsEntry?.((message as any).ttftMs) continue } // Yield attachment messages (e.g., structured_output) without recording them if (message.type === 'attachment') { // Handle max turns reached signal from query.ts - if (message.attachment.type === 'max_turns_reached') { + if ((message as any).attachment.type === 'max_turns_reached') { logForDebugging( `[Agent : $ @@ -779,13 +779,13 @@ export async function* runAgent({ } ] Reached max turns limit ($ { - message.attachment.maxTurns + (message as any).attachment.maxTurns } )`, ) break } - yield message + yield message as Message continue } diff --git a/src/tools/FileEditTool/UI.tsx b/src/tools/FileEditTool/UI.tsx index 860577e..fef8a99 100644 --- a/src/tools/FileEditTool/UI.tsx +++ b/src/tools/FileEditTool/UI.tsx @@ -222,7 +222,7 @@ function EditRejectionBody(t0) { patch, firstLine, fileContent - } = use(promise); + } = use(promise) as any; let t1; if ($[0] !== fileContent || $[1] !== filePath || $[2] !== firstLine || $[3] !== patch || $[4] !== style || $[5] !== verbose) { t1 = ; diff --git a/src/tools/FileReadTool/FileReadTool.ts b/src/tools/FileReadTool/FileReadTool.ts index 1ceed46..45c676e 100644 --- a/src/tools/FileReadTool/FileReadTool.ts +++ b/src/tools/FileReadTool/FileReadTool.ts @@ -899,11 +899,11 @@ async function callInner( parsedRange ?? undefined, ) if (!extractResult.success) { - throw new Error(extractResult.error.message) + throw new Error((extractResult as any).error.message) } logEvent('tengu_pdf_page_extraction', { success: true, - pageCount: extractResult.data.file.count, + pageCount: (extractResult as any).data.file.count, fileSize: extractResult.data.file.originalSize, hasPageRange: true, }) @@ -970,7 +970,7 @@ async function callInner( } else { logEvent('tengu_pdf_page_extraction', { success: false, - available: extractResult.error.reason !== 'unavailable', + available: (extractResult as any).error.reason !== 'unavailable', fileSize: stats.size, }) } @@ -986,7 +986,7 @@ async function callInner( const readResult = await readPDF(resolvedFilePath) if (!readResult.success) { - throw new Error(readResult.error.message) + throw new Error((readResult as any).error.message) } const pdfData = readResult.data logFileOperation({ @@ -1158,12 +1158,12 @@ export async function readImageWithTokenBudget( const sharpModule = await import('sharp') const sharp = ( - sharpModule as { + sharpModule as unknown as { default?: typeof sharpModule } & typeof sharpModule ).default || sharpModule - const fallbackBuffer = await sharp(imageBuffer) + const fallbackBuffer = await (sharp as any)(imageBuffer) .resize(400, 400, { fit: 'inside', withoutEnlargement: true, diff --git a/src/tools/SendMessageTool/SendMessageTool.ts b/src/tools/SendMessageTool/SendMessageTool.ts index 289f6a1..ebf4237 100644 --- a/src/tools/SendMessageTool/SendMessageTool.ts +++ b/src/tools/SendMessageTool/SendMessageTool.ts @@ -826,7 +826,7 @@ export const SendMessageTool: Tool = prompt: input.message, toolUseContext: context, canUseTool, - invokingRequestId: assistantMessage?.requestId, + invokingRequestId: assistantMessage?.requestId as string | undefined, }) return { data: { @@ -853,7 +853,7 @@ export const SendMessageTool: Tool = prompt: input.message, toolUseContext: context, canUseTool, - invokingRequestId: assistantMessage?.requestId, + invokingRequestId: assistantMessage?.requestId as string | undefined, }) return { data: { diff --git a/src/types/global.d.ts b/src/types/global.d.ts index c6778ba..cc5d367 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -67,6 +67,17 @@ declare const BUILD_TARGET: string declare const BUILD_ENV: string declare const INTERFACE_TYPE: string +// ============================================================================ +// Ink custom JSX intrinsic elements — used by the internal Ink framework +declare namespace JSX { + interface IntrinsicElements { + 'ink-box': any; + 'ink-text': any; + 'ink-link': any; + 'ink-raw-ansi': any; + } +} + // ============================================================================ // Bun text/file loaders — allow importing non-TS assets as strings declare module '*.md' { diff --git a/src/upstreamproxy/relay.ts b/src/upstreamproxy/relay.ts index 747c242..fa9b211 100644 --- a/src/upstreamproxy/relay.ts +++ b/src/upstreamproxy/relay.ts @@ -262,7 +262,7 @@ export async function startNodeRelay( }, end: () => sock.end(), } - sock.on('data', data => + sock.on('data', (data: Buffer) => handleData(adapter, st, data, wsUrl, authHeader, wsAuthHeader), ) sock.on('close', () => cleanupConn(states.get(sock))) @@ -381,7 +381,7 @@ function openTunnel( // responds with its own "HTTP/1.1 200" over the tunnel; we just pipe it. const head = `${connectLine}\r\n` + `Proxy-Authorization: ${authHeader}\r\n` + `\r\n` - ws.send(encodeChunk(Buffer.from(head, 'utf8'))) + ws.send(encodeChunk(new Uint8Array(Buffer.from(head, 'utf8'))) as any) // Flush anything that arrived while the WS handshake was in flight — // trailing bytes from the CONNECT packet and any data() callbacks that // fired before onopen. @@ -429,15 +429,15 @@ function openTunnel( function sendKeepalive(ws: WebSocketLike): void { if (ws.readyState === WebSocket.OPEN) { - ws.send(encodeChunk(new Uint8Array(0))) + ws.send(encodeChunk(new Uint8Array(0)) as any) } } function forwardToWs(ws: WebSocketLike, data: Buffer): void { if (ws.readyState !== WebSocket.OPEN) return for (let off = 0; off < data.length; off += MAX_CHUNK_BYTES) { - const slice = data.subarray(off, off + MAX_CHUNK_BYTES) - ws.send(encodeChunk(slice)) + const slice = new Uint8Array(data.subarray(off, off + MAX_CHUNK_BYTES)) + ws.send(encodeChunk(slice) as any) } } diff --git a/src/utils/cronScheduler.ts b/src/utils/cronScheduler.ts index 56b3627..7cae923 100644 --- a/src/utils/cronScheduler.ts +++ b/src/utils/cronScheduler.ts @@ -553,8 +553,8 @@ export function buildMissedTaskNotification(missed: CronTask[]): string { // Use a fence one longer than any backtick run in the prompt so a // prompt containing ``` cannot close the fence early and un-wrap the // trailing text (CommonMark fence-matching rule). - const longestRun = (t.prompt.match(/`+/g) ?? []).reduce( - (max, run) => Math.max(max, run.length), + const longestRun = (t.prompt.match(/`+/g) ?? ([] as string[])).reduce( + (max: number, run: string) => Math.max(max, run.length), 0, ) const fence = '`'.repeat(Math.max(3, longestRun + 1)) diff --git a/src/utils/dxt/helpers.ts b/src/utils/dxt/helpers.ts index ae1d671..85414ed 100644 --- a/src/utils/dxt/helpers.ts +++ b/src/utils/dxt/helpers.ts @@ -1,4 +1,4 @@ -import type { McpbManifest } from '@anthropic-ai/mcpb' +import type { McpbManifestAny } from '@anthropic-ai/mcpb' import { errorMessage } from '../errors.js' import { jsonParse } from '../slowOperations.js' @@ -12,15 +12,15 @@ import { jsonParse } from '../slowOperations.js' */ export async function validateManifest( manifestJson: unknown, -): Promise { - const { McpbManifestSchema } = await import('@anthropic-ai/mcpb') - const parseResult = McpbManifestSchema.safeParse(manifestJson) +): Promise { + const { vAny } = await import('@anthropic-ai/mcpb') + const parseResult = vAny.McpbManifestSchema.safeParse(manifestJson) if (!parseResult.success) { const errors = parseResult.error.flatten() const errorMessages = [ ...Object.entries(errors.fieldErrors).map( - ([field, errs]) => `${field}: ${errs?.join(', ')}`, + ([field, errs]) => `${field}: ${(errs as any)?.join(', ')}`, ), ...(errors.formErrors || []), ] @@ -38,7 +38,7 @@ export async function validateManifest( */ export async function parseAndValidateManifestFromText( manifestText: string, -): Promise { +): Promise { let manifestJson: unknown try { @@ -55,7 +55,7 @@ export async function parseAndValidateManifestFromText( */ export async function parseAndValidateManifestFromBytes( manifestData: Uint8Array, -): Promise { +): Promise { const manifestText = new TextDecoder().decode(manifestData) return parseAndValidateManifestFromText(manifestText) } @@ -65,7 +65,7 @@ export async function parseAndValidateManifestFromBytes( * Uses the same algorithm as the directory backend for consistency. */ export function generateExtensionId( - manifest: McpbManifest, + manifest: McpbManifestAny, prefix?: 'local.unpacked' | 'local.dxt', ): string { const sanitize = (str: string) => diff --git a/src/utils/filePersistence/outputsScanner.ts b/src/utils/filePersistence/outputsScanner.ts index ce845d2..f1ce651 100644 --- a/src/utils/filePersistence/outputsScanner.ts +++ b/src/utils/filePersistence/outputsScanner.ts @@ -64,12 +64,12 @@ export async function findModifiedFiles( outputsDir: string, ): Promise { // Use recursive flag to get all entries in one call - let entries: Awaited> + let entries: Awaited> | any[] try { entries = await fs.readdir(outputsDir, { withFileTypes: true, recursive: true, - }) + }) as any[] } catch { // Directory doesn't exist or is not accessible return [] @@ -113,7 +113,7 @@ export async function findModifiedFiles( // Filter to files modified since turn start const modifiedFiles: string[] = [] for (const result of statResults) { - if (result && result.mtimeMs >= turnStartTime) { + if (result && result.mtimeMs >= (turnStartTime as any as number)) { modifiedFiles.push(result.filePath) } } diff --git a/src/utils/forkedAgent.ts b/src/utils/forkedAgent.ts index 972361a..94c95b1 100644 --- a/src/utils/forkedAgent.ts +++ b/src/utils/forkedAgent.ts @@ -558,10 +558,10 @@ export async function runForkedAgent({ if (message.type === 'stream_event') { if ( 'event' in message && - message.event?.type === 'message_delta' && - message.event.usage + (message as any).event?.type === 'message_delta' && + (message as any).event.usage ) { - const turnUsage = updateUsage({ ...EMPTY_USAGE }, message.event.usage) + const turnUsage = updateUsage({ ...EMPTY_USAGE }, (message as any).event.usage) totalUsage = accumulateUsage(totalUsage, turnUsage) } continue diff --git a/src/utils/permissions/filesystem.ts b/src/utils/permissions/filesystem.ts index 320b623..9fd709e 100644 --- a/src/utils/permissions/filesystem.ts +++ b/src/utils/permissions/filesystem.ts @@ -1325,14 +1325,15 @@ export function checkWritePermissionForTool( }, ] : generateSuggestions(path, 'write', toolPermissionContext, pathsToCheck) + const failedCheck = safetyCheck as { safe: false; message: string; classifierApprovable: boolean } return { behavior: 'ask', - message: safetyCheck.message, + message: failedCheck.message, suggestions: safetySuggestions, decisionReason: { type: 'safetyCheck', - reason: safetyCheck.message, - classifierApprovable: safetyCheck.classifierApprovable, + reason: failedCheck.message, + classifierApprovable: failedCheck.classifierApprovable, }, } } diff --git a/src/utils/permissions/pathValidation.ts b/src/utils/permissions/pathValidation.ts index 40814f7..01326c9 100644 --- a/src/utils/permissions/pathValidation.ts +++ b/src/utils/permissions/pathValidation.ts @@ -112,8 +112,8 @@ export function isPathInSandboxWriteAllowlist(resolvedPath: string): boolean { // their resolution to avoid N × config.length redundant syscalls per // command with N write targets (matching getResolvedWorkingDirPaths). const pathsToCheck = getPathsForPermissionCheck(resolvedPath) - const resolvedAllow = allowOnly.flatMap(getResolvedSandboxConfigPath) - const resolvedDeny = denyWithinAllow.flatMap(getResolvedSandboxConfigPath) + const resolvedAllow = allowOnly.flatMap(getResolvedSandboxConfigPath) as string[] + const resolvedDeny = denyWithinAllow.flatMap(getResolvedSandboxConfigPath) as string[] return pathsToCheck.every(p => { for (const denyPath of resolvedDeny) { if (pathInWorkingPath(p, denyPath)) return false @@ -184,12 +184,13 @@ export function isPathAllowed( precomputedPathsToCheck, ) if (!safetyCheck.safe) { + const failedCheck = safetyCheck as { safe: false; message: string; classifierApprovable: boolean } return { allowed: false, decisionReason: { type: 'safetyCheck', - reason: safetyCheck.message, - classifierApprovable: safetyCheck.classifierApprovable, + reason: failedCheck.message, + classifierApprovable: failedCheck.classifierApprovable, }, } } diff --git a/src/utils/permissions/permissions.ts b/src/utils/permissions/permissions.ts index 99db042..e7dbc5c 100644 --- a/src/utils/permissions/permissions.ts +++ b/src/utils/permissions/permissions.ts @@ -412,7 +412,7 @@ async function runPermissionRequestHooksForHeadlessAgent( input, context, permissionMode, - suggestions, + suggestions as any, context.abortController.signal, )) { if (!hookResult.permissionRequestResult) { @@ -423,12 +423,12 @@ async function runPermissionRequestHooksForHeadlessAgent( const finalInput = decision.updatedInput ?? input // Persist permission updates if provided if (decision.updatedPermissions?.length) { - persistPermissionUpdates(decision.updatedPermissions) + persistPermissionUpdates(decision.updatedPermissions as any) context.setAppState(prev => ({ ...prev, toolPermissionContext: applyPermissionUpdates( prev.toolPermissionContext, - decision.updatedPermissions!, + decision.updatedPermissions as any, ), })) } diff --git a/src/utils/processUserInput/processUserInput.ts b/src/utils/processUserInput/processUserInput.ts index d5a68c4..f33acc9 100644 --- a/src/utils/processUserInput/processUserInput.ts +++ b/src/utils/processUserInput/processUserInput.ts @@ -251,12 +251,12 @@ export async function processUserInput({ ...hookResult.message, attachment: { ...hookResult.message.attachment, - content: applyTruncation(hookResult.message.attachment.content), + content: applyTruncation(hookResult.message.attachment.content as string), }, - }) + } as AttachmentMessage) break default: - result.messages.push(hookResult.message) + result.messages.push(hookResult.message as AttachmentMessage) break } } diff --git a/src/utils/sideQuestion.ts b/src/utils/sideQuestion.ts index ab6af37..860d421 100644 --- a/src/utils/sideQuestion.ts +++ b/src/utils/sideQuestion.ts @@ -125,7 +125,7 @@ ${question}` function extractSideQuestionResponse(messages: Message[]): string | null { // Flatten all assistant content blocks across the per-block messages. const assistantBlocks = messages.flatMap(m => - m.type === 'assistant' ? m.message.content : [], + m.type === 'assistant' ? (m.message.content as unknown as Array<{ type: string; [key: string]: unknown }>) : [], ) if (assistantBlocks.length > 0) { @@ -136,7 +136,7 @@ function extractSideQuestionResponse(messages: Message[]): string | null { // No text — check if the model tried to call a tool despite instructions. const toolUse = assistantBlocks.find(b => b.type === 'tool_use') if (toolUse) { - const toolName = 'name' in toolUse ? toolUse.name : 'a tool' + const toolName = 'name' in toolUse ? (toolUse as any).name : 'a tool' return `(The model tried to call ${toolName} instead of answering directly. Try rephrasing or ask in the main conversation.)` } } @@ -148,7 +148,7 @@ function extractSideQuestionResponse(messages: Message[]): string | null { m.type === 'system' && 'subtype' in m && m.subtype === 'api_error', ) if (apiErr) { - return `(API error: ${formatAPIError(apiErr.error)})` + return `(API error: ${formatAPIError(apiErr.error as any)})` } return null diff --git a/src/utils/sliceAnsi.ts b/src/utils/sliceAnsi.ts index 4cd9107..8e1a0a0 100644 --- a/src/utils/sliceAnsi.ts +++ b/src/utils/sliceAnsi.ts @@ -43,7 +43,7 @@ export default function sliceAnsi( // pass start/end in display cells (via stringWidth), so position must // track the same units. const width = - token.type === 'ansi' ? 0 : token.fullWidth ? 2 : stringWidth(token.value) + token.type === 'ansi' ? 0 : token.type === 'char' ? (token.fullWidth ? 2 : stringWidth(token.value)) : 0 // Break AFTER trailing zero-width marks — a combining mark attaches to // the preceding base char, so "भा" (भ + ा, 1 display cell) sliced at @@ -77,7 +77,7 @@ export default function sliceAnsi( } if (include) { - result += token.value + result += (token as any).value } position += width diff --git a/src/utils/teleport/gitBundle.ts b/src/utils/teleport/gitBundle.ts index 0e53c5d..fa4f2d8 100644 --- a/src/utils/teleport/gitBundle.ts +++ b/src/utils/teleport/gitBundle.ts @@ -231,16 +231,17 @@ export async function createAndUploadGitBundle( ) if (!bundle.ok) { - logForDebugging(`[gitBundle] ${bundle.error}`) + const failedBundle = bundle as { ok: false; error: string; failReason: BundleFailReason } + logForDebugging(`[gitBundle] ${failedBundle.error}`) logEvent('tengu_ccr_bundle_upload', { outcome: - bundle.failReason as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + failedBundle.failReason as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, max_bytes: maxBytes, }) return { success: false, - error: bundle.error, - failReason: bundle.failReason, + error: failedBundle.error, + failReason: failedBundle.failReason, } } @@ -254,7 +255,7 @@ export async function createAndUploadGitBundle( outcome: 'failed' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, }) - return { success: false, error: upload.error } + return { success: false, error: (upload as { success: false; error: string }).error } } logForDebugging( diff --git a/src/utils/toolSearch.ts b/src/utils/toolSearch.ts index 5f9a001..4531470 100644 --- a/src/utils/toolSearch.ts +++ b/src/utils/toolSearch.ts @@ -551,7 +551,7 @@ export function extractDiscoveredToolNames(messages: Message[]): Set { // check rather than isCompactBoundaryMessage — utils/messages.ts imports // from this file, so importing back would be circular. if (msg.type === 'system' && msg.subtype === 'compact_boundary') { - const carried = msg.compactMetadata?.preCompactDiscoveredTools + const carried = (msg as any).compactMetadata?.preCompactDiscoveredTools as string[] | undefined if (carried) { for (const name of carried) discoveredTools.add(name) carriedFromBoundary += carried.length @@ -658,8 +658,8 @@ export function getDeferredToolsDelta( attachmentTypesSeen.add(msg.attachment.type) if (msg.attachment.type !== 'deferred_tools_delta') continue dtdCount++ - for (const n of msg.attachment.addedNames) announced.add(n) - for (const n of msg.attachment.removedNames) announced.delete(n) + for (const n of (msg.attachment as any).addedNames) announced.add(n) + for (const n of (msg.attachment as any).removedNames) announced.delete(n) } const deferred: Tool[] = tools.filter(isDeferredTool)