feat: 问就是封包
This commit is contained in:
parent
d7a729ca68
commit
dd9cd782a7
263
README.md
263
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 <host>` | ❌ | `SSH_REMOTE` flag |
|
||||
| `claude open <cc-url>` | ❌ | `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`
|
||||
|
||||
## 快速开始
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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; }
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
@ -178,9 +178,9 @@ export async function mcpListHandler(): Promise<void> {
|
||||
// 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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -38,7 +38,7 @@ type TranscriptEntry = TranscriptMessage & {
|
||||
export function deriveFirstPrompt(
|
||||
firstUserMessage: Extract<SerializedMessage, { type: 'user' }> | 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<SerializedMessage, { type: 'user' }> | undefined,
|
||||
)
|
||||
|
||||
// Save custom title - use provided title or firstPrompt as default
|
||||
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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'
|
||||
});
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -50,7 +50,7 @@ export function FileEditToolDiff(props) {
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
function DiffBody(t0) {
|
||||
function DiffBody(t0: { promise: Promise<DiffData>; file_path: string }) {
|
||||
const $ = _c(6);
|
||||
const {
|
||||
promise,
|
||||
|
||||
@ -134,7 +134,7 @@ function Highlighted(t0) {
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
const hl = use(t1);
|
||||
const hl = use(t1) as NonNullable<Awaited<ReturnType<typeof getCliHighlightPromise>>> | null;
|
||||
let t2;
|
||||
if ($[1] !== codeWithSpaces || $[2] !== hl || $[3] !== language) {
|
||||
bb0: {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -334,7 +334,7 @@ export function MessageSelector({
|
||||
<Box flexDirection="column" paddingLeft={1} borderStyle="single" borderRight={false} borderTop={false} borderBottom={false} borderLeft={true} borderLeftDimColor>
|
||||
<UserMessageOption userMessage={messageToRestore} color="text" isCurrent={false} />
|
||||
<Text dimColor>
|
||||
({formatRelativeTimeAgo(new Date(messageToRestore.timestamp))})
|
||||
({formatRelativeTimeAgo(new Date(messageToRestore.timestamp as number))})
|
||||
</Text>
|
||||
</Box>
|
||||
<RestoreOptionDescription selectedRestoreOption={selectedRestoreOption} canRestoreCode={!!canRestoreCode_0} diffStatsForRestore={diffStatsForRestore} />
|
||||
|
||||
@ -95,7 +95,8 @@ export function Settings(t0) {
|
||||
}
|
||||
let t7;
|
||||
if ($[13] !== contentHeight) {
|
||||
t7 = false ? [<Tab key="gates" title="Gates"><Gates onOwnsEscChange={setGatesOwnsEsc} contentHeight={contentHeight} /></Tab>] : [];
|
||||
const GatesComponent = Gates as any;
|
||||
t7 = false ? [<Tab key="gates" title="Gates"><GatesComponent onOwnsEscChange={setGatesOwnsEsc} contentHeight={contentHeight} /></Tab>] : [];
|
||||
$[13] = contentHeight;
|
||||
$[14] = t7;
|
||||
} else {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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<number[]>(new Array(CURSOR_WAVEFORM_WIDTH).fill(0));
|
||||
const needsAnimation = isVoiceRecording && !reducedMotion;
|
||||
const [animRef, animTime] = feature('VOICE_MODE') ?
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
});
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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<ToolProgressData> => msg.data.type !== 'hook_progress');
|
||||
const toolProgressMessages = progressMessagesForMessage.filter((msg): msg is ProgressMessage<ToolProgressData> => (msg.data as { type?: string }).type !== 'hook_progress');
|
||||
try {
|
||||
const toolMessages = tool.renderToolUseProgressMessage?.(toolProgressMessages, {
|
||||
tools,
|
||||
|
||||
@ -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'])
|
||||
)
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -62,7 +62,7 @@ function PermissionDecisionInfoItem(t0) {
|
||||
return <Box flexDirection="column">{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 <Box flexDirection="column" key={subcommand}><Text>{icon} {subcommand}</Text>{result.decisionReason !== undefined && result.decisionReason.type !== "subcommandResults" && <Text><Text dimColor={true}>{" "}⎿{" "}</Text><Ansi>{decisionReasonDisplayString(result.decisionReason)}</Ansi></Text>}{result.behavior === "ask" && <SuggestedRules suggestions={result.suggestions} />}</Box>;
|
||||
return <Box flexDirection="column" key={subcommand}><Text>{icon} {subcommand}</Text>{result.decisionReason !== undefined && result.decisionReason.type !== "subcommandResults" && <Text><Text dimColor={true}>{" "}⎿{" "}</Text><Ansi>{decisionReasonDisplayString(result.decisionReason as any)}</Ansi></Text>}{result.behavior === "ask" && <SuggestedRules suggestions={result.suggestions} />}</Box>;
|
||||
})}</Box>;
|
||||
}
|
||||
default:
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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")) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -159,7 +159,7 @@ export async function getAnthropicClient({
|
||||
? process.env.ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION
|
||||
: getAWSRegion()
|
||||
|
||||
const bedrockArgs: ConstructorParameters<typeof AnthropicBedrock>[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<typeof AnthropicVertex>[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
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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<string, ScopedMcpServerConfig>,
|
||||
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'))
|
||||
|
||||
@ -99,7 +99,7 @@ export async function* runPostToolUseHooks<Input extends AnyObject, Output>(
|
||||
result.message.attachment.type === 'hook_blocking_error'
|
||||
)
|
||||
) {
|
||||
yield { message: result.message }
|
||||
yield { message: result.message as AttachmentMessage | ProgressMessage<HookProgress> }
|
||||
}
|
||||
|
||||
if (result.blockingError) {
|
||||
@ -251,7 +251,7 @@ export async function* runPostToolUseFailureHooks<Input extends AnyObject>(
|
||||
result.message.attachment.type === 'hook_blocking_error'
|
||||
)
|
||||
) {
|
||||
yield { message: result.message }
|
||||
yield { message: result.message as AttachmentMessage | ProgressMessage<HookProgress> }
|
||||
}
|
||||
|
||||
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<HookProgress> } }
|
||||
}
|
||||
if (result.blockingError) {
|
||||
const denialMessage = getPreToolHookBlockingMessage(
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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<LocalShellTaskState>(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 <task_notification> would be redundant.
|
||||
*/
|
||||
export function markTaskNotified(taskId: string, setAppState: SetAppState): void {
|
||||
updateTaskState(taskId, setAppState, t => t.notified ? t : {
|
||||
updateTaskState<LocalShellTaskState>(taskId, setAppState, t => t.notified ? t : {
|
||||
...t,
|
||||
notified: true
|
||||
});
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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 = <FileEditToolUseRejectedMessage file_path={filePath} operation="update" patch={patch} firstLine={firstLine} fileContent={fileContent} style={style} verbose={verbose} />;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -826,7 +826,7 @@ export const SendMessageTool: Tool<InputSchema, SendMessageToolOutput> =
|
||||
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<InputSchema, SendMessageToolOutput> =
|
||||
prompt: input.message,
|
||||
toolUseContext: context,
|
||||
canUseTool,
|
||||
invokingRequestId: assistantMessage?.requestId,
|
||||
invokingRequestId: assistantMessage?.requestId as string | undefined,
|
||||
})
|
||||
return {
|
||||
data: {
|
||||
|
||||
11
src/types/global.d.ts
vendored
11
src/types/global.d.ts
vendored
@ -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' {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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<McpbManifest> {
|
||||
const { McpbManifestSchema } = await import('@anthropic-ai/mcpb')
|
||||
const parseResult = McpbManifestSchema.safeParse(manifestJson)
|
||||
): Promise<McpbManifestAny> {
|
||||
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<McpbManifest> {
|
||||
): Promise<McpbManifestAny> {
|
||||
let manifestJson: unknown
|
||||
|
||||
try {
|
||||
@ -55,7 +55,7 @@ export async function parseAndValidateManifestFromText(
|
||||
*/
|
||||
export async function parseAndValidateManifestFromBytes(
|
||||
manifestData: Uint8Array,
|
||||
): Promise<McpbManifest> {
|
||||
): Promise<McpbManifestAny> {
|
||||
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) =>
|
||||
|
||||
@ -64,12 +64,12 @@ export async function findModifiedFiles(
|
||||
outputsDir: string,
|
||||
): Promise<string[]> {
|
||||
// Use recursive flag to get all entries in one call
|
||||
let entries: Awaited<ReturnType<typeof fs.readdir>>
|
||||
let entries: Awaited<ReturnType<typeof fs.readdir>> | 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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1325,14 +1325,15 @@ export function checkWritePermissionForTool<Input extends AnyObject>(
|
||||
},
|
||||
]
|
||||
: 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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
),
|
||||
}))
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -551,7 +551,7 @@ export function extractDiscoveredToolNames(messages: Message[]): Set<string> {
|
||||
// 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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user