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 核心功能跑通,必要时删减次级能力。
|
Anthropic 官方 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI 工具的源码反编译/逆向还原项目。目标是将 Claude Code 核心功能跑通,必要时删减次级能力。
|
||||||
|
|
||||||
## 核心能力
|
## 能力清单
|
||||||
|
|
||||||
- API 通信(Anthropic SDK / Bedrock / Vertex)
|
> ✅ = 已实现 ⚠️ = 部分实现 / 条件启用 ❌ = stub / 移除 / feature flag 关闭
|
||||||
- Bash / FileRead / FileWrite / FileEdit 等核心工具
|
|
||||||
- REPL 交互界面(ink 终端渲染)
|
|
||||||
- 对话历史与会话管理
|
|
||||||
- 权限系统
|
|
||||||
- Agent / 子代理系统
|
|
||||||
|
|
||||||
## 已删减模块
|
### 核心系统
|
||||||
|
|
||||||
| 模块 | 处理方式 |
|
| 能力 | 状态 | 说明 |
|
||||||
|------|----------|
|
|------|------|------|
|
||||||
| Computer Use (`@ant/computer-use-*`) | stub |
|
| REPL 交互界面(Ink 终端渲染) | ✅ | 主屏幕 5000+ 行,完整交互 |
|
||||||
| Claude for Chrome MCP | stub |
|
| API 通信 — Anthropic Direct | ✅ | 支持 API Key + OAuth |
|
||||||
| Magic Docs / Voice Mode / LSP Server | 移除 |
|
| API 通信 — AWS Bedrock | ✅ | 支持凭据刷新、Bearer Token |
|
||||||
| Analytics / GrowthBook / Sentry | 空实现 |
|
| API 通信 — Google Vertex | ✅ | 支持 GCP 凭据刷新 |
|
||||||
| Plugins / Marketplace / Desktop Upsell | 移除 |
|
| API 通信 — Azure Foundry | ✅ | 支持 API Key + Azure AD |
|
||||||
| Ultraplan / Tungsten / Auto Dream | 移除 |
|
| 流式对话与工具调用循环 (`query.ts`) | ✅ | 1700+ 行,含自动压缩、token 追踪 |
|
||||||
| MCP OAuth/IDP | 简化 |
|
| 会话引擎 (`QueryEngine.ts`) | ✅ | 1300+ 行,管理对话状态与归因 |
|
||||||
| DAEMON / BRIDGE / BG_SESSIONS / TEMPLATES 等 | feature flag 关闭 |
|
| 上下文构建(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",
|
"version": "1.0.0",
|
||||||
"private": true,
|
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*",
|
"packages/*",
|
||||||
"packages/@ant/*"
|
"packages/@ant/*"
|
||||||
],
|
],
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "bun build src/entrypoints/cli.tsx --outdir dist --target bun",
|
"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": {
|
"dependencies": {
|
||||||
"@alcalzone/ansi-tokenize": "^0.3.0",
|
"@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
|
// pushed later tests in the same shard into GC-pause territory and a
|
||||||
// beforeEach/afterEach hook timeout (officialRegistry.test.ts, PR #24150).
|
// beforeEach/afterEach hook timeout (officialRegistry.test.ts, PR #24150).
|
||||||
// Same lazy pattern the NAPI wrapper used for dlopen.
|
// Same lazy pattern the NAPI wrapper used for dlopen.
|
||||||
type HLJSApi = typeof hljsNamespace
|
type HLJSApi = typeof hljsNamespace.default
|
||||||
let cachedHljs: HLJSApi | null = null
|
let cachedHljs: HLJSApi | null = null
|
||||||
function hljs(): HLJSApi {
|
function hljs(): HLJSApi {
|
||||||
if (cachedHljs) return cachedHljs
|
if (cachedHljs) return cachedHljs
|
||||||
|
|||||||
@ -1755,4 +1755,4 @@ export function getPromptId(): string | null {
|
|||||||
export function setPromptId(id: string | null): void {
|
export function setPromptId(id: string | null): void {
|
||||||
STATE.promptId = id
|
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()
|
const orgResult = await validateForceLoginOrg()
|
||||||
if (!orgResult.valid) {
|
if (!orgResult.valid) {
|
||||||
process.stderr.write(orgResult.message + '\n')
|
process.stderr.write((orgResult as { valid: false; message: string }).message + '\n')
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,7 +209,7 @@ export async function authLogin({
|
|||||||
|
|
||||||
const orgResult = await validateForceLoginOrg()
|
const orgResult = await validateForceLoginOrg()
|
||||||
if (!orgResult.valid) {
|
if (!orgResult.valid) {
|
||||||
process.stderr.write(orgResult.message + '\n')
|
process.stderr.write((orgResult as { valid: false; message: string }).message + '\n')
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -178,9 +178,9 @@ export async function mcpListHandler(): Promise<void> {
|
|||||||
// biome-ignore lint/suspicious/noConsole:: intentional console output
|
// biome-ignore lint/suspicious/noConsole:: intentional console output
|
||||||
console.log(`${name}: ${server.url} - ${status}`);
|
console.log(`${name}: ${server.url} - ${status}`);
|
||||||
} else if (!server.type || server.type === 'stdio') {
|
} 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
|
// 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,
|
uuid: crumb.uuid,
|
||||||
timestamp: crumb.timestamp,
|
timestamp: crumb.timestamp,
|
||||||
isReplay: true,
|
isReplay: true,
|
||||||
} satisfies SDKUserMessageReplay)
|
} as SDKUserMessageReplay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1980,7 +1980,7 @@ function runHeadlessStreaming(
|
|||||||
parent_tool_use_id: null,
|
parent_tool_use_id: null,
|
||||||
uuid: c.uuid as string,
|
uuid: c.uuid as string,
|
||||||
isReplay: true,
|
isReplay: true,
|
||||||
} satisfies SDKUserMessageReplay)
|
} as SDKUserMessageReplay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2265,8 +2265,8 @@ function runHeadlessStreaming(
|
|||||||
output.enqueue({
|
output.enqueue({
|
||||||
type: 'system' as const,
|
type: 'system' as const,
|
||||||
subtype: 'files_persisted' as const,
|
subtype: 'files_persisted' as const,
|
||||||
files: result.persistedFiles,
|
files: (result as any).persistedFiles,
|
||||||
failed: result.failedFiles,
|
failed: (result as any).failedFiles,
|
||||||
processed_at: new Date().toISOString(),
|
processed_at: new Date().toISOString(),
|
||||||
uuid: randomUUID(),
|
uuid: randomUUID(),
|
||||||
session_id: getSessionId(),
|
session_id: getSessionId(),
|
||||||
|
|||||||
@ -375,7 +375,7 @@ export class CCRClient {
|
|||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
throw new RetryableError(
|
throw new RetryableError(
|
||||||
'client event POST failed',
|
'client event POST failed',
|
||||||
result.retryAfterMs,
|
(result as any).retryAfterMs,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -398,7 +398,7 @@ export class CCRClient {
|
|||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
throw new RetryableError(
|
throw new RetryableError(
|
||||||
'internal event POST failed',
|
'internal event POST failed',
|
||||||
result.retryAfterMs,
|
(result as any).retryAfterMs,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -427,7 +427,7 @@ export class CCRClient {
|
|||||||
'delivery batch',
|
'delivery batch',
|
||||||
)
|
)
|
||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
throw new RetryableError('delivery POST failed', result.retryAfterMs)
|
throw new RetryableError('delivery POST failed', (result as any).retryAfterMs)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
baseDelayMs: 500,
|
baseDelayMs: 500,
|
||||||
|
|||||||
@ -38,7 +38,7 @@ type TranscriptEntry = TranscriptMessage & {
|
|||||||
export function deriveFirstPrompt(
|
export function deriveFirstPrompt(
|
||||||
firstUserMessage: Extract<SerializedMessage, { type: 'user' }> | undefined,
|
firstUserMessage: Extract<SerializedMessage, { type: 'user' }> | undefined,
|
||||||
): string {
|
): string {
|
||||||
const content = firstUserMessage?.message?.content
|
const content = (firstUserMessage as any)?.message?.content
|
||||||
if (!content) return 'Branched conversation'
|
if (!content) return 'Branched conversation'
|
||||||
const raw =
|
const raw =
|
||||||
typeof content === 'string'
|
typeof content === 'string'
|
||||||
@ -240,7 +240,7 @@ export async function call(
|
|||||||
// Build LogOption for resume
|
// Build LogOption for resume
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const firstPrompt = deriveFirstPrompt(
|
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
|
// Save custom title - use provided title or firstPrompt as default
|
||||||
|
|||||||
@ -140,7 +140,7 @@ export function BrowseMarketplace({
|
|||||||
} of marketplaces_0) {
|
} of marketplaces_0) {
|
||||||
if (marketplace) {
|
if (marketplace) {
|
||||||
// Count how many plugins from this marketplace are installed
|
// 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({
|
marketplaceInfos.push({
|
||||||
name,
|
name,
|
||||||
totalPlugins: marketplace.plugins.length,
|
totalPlugins: marketplace.plugins.length,
|
||||||
@ -334,7 +334,7 @@ export function BrowseMarketplace({
|
|||||||
failureCount++;
|
failureCount++;
|
||||||
newFailedPlugins.push({
|
newFailedPlugins.push({
|
||||||
name: plugin_1.entry.name,
|
name: plugin_1.entry.name,
|
||||||
reason: result.error
|
reason: (result as { success: false; error: string }).error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -397,7 +397,7 @@ export function BrowseMarketplace({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setIsInstalling(false);
|
setIsInstalling(false);
|
||||||
setInstallError(result_0.error);
|
setInstallError((result_0 as { success: false; error: string }).error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -248,7 +248,7 @@ export function DiscoverPlugins({
|
|||||||
failureCount++;
|
failureCount++;
|
||||||
newFailedPlugins.push({
|
newFailedPlugins.push({
|
||||||
name: plugin_0.entry.name,
|
name: plugin_0.entry.name,
|
||||||
reason: result.error
|
reason: (result as { success: false; error: string }).error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -306,7 +306,7 @@ export function DiscoverPlugins({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setIsInstalling(false);
|
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);
|
const result = await importGithubToken(token);
|
||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
|
const importErr = (result as { ok: false; error: ImportTokenError }).error;
|
||||||
logEvent('tengu_remote_setup_result', {
|
logEvent('tengu_remote_setup_result', {
|
||||||
result: 'import_failed' as SafeString,
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
// Auto-generated type stub — replace with real implementation
|
// Auto-generated stub — replace with real implementation
|
||||||
export type resetLimits = any;
|
const stub = { isEnabled: () => false, isHidden: true, name: 'stub' };
|
||||||
export type resetLimitsNonInteractive = any;
|
export const resetLimits = stub;
|
||||||
|
export const resetLimitsNonInteractive = stub;
|
||||||
|
|||||||
@ -154,11 +154,12 @@ function ResumeCommand({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Different project - show command instead of resuming
|
// 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);
|
if (raw) process.stdout.write(raw);
|
||||||
|
|
||||||
// Format the output message
|
// 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, {
|
onDone(message, {
|
||||||
display: 'user'
|
display: 'user'
|
||||||
});
|
});
|
||||||
|
|||||||
@ -136,7 +136,7 @@ export async function launchRemoteReview(
|
|||||||
// consume at session creation routes billing: first N zero-rate, then
|
// consume at session creation routes billing: first N zero-rate, then
|
||||||
// anthropic:cccr org-service-key (overage-only).
|
// anthropic:cccr org-service-key (overage-only).
|
||||||
if (!eligibility.eligible) {
|
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',
|
e => e.type !== 'no_remote_environment',
|
||||||
)
|
)
|
||||||
if (blockers.length > 0) {
|
if (blockers.length > 0) {
|
||||||
|
|||||||
@ -314,11 +314,12 @@ async function launchDetached(opts: {
|
|||||||
const model = getUltraplanModel();
|
const model = getUltraplanModel();
|
||||||
const eligibility = await checkRemoteAgentEligibility();
|
const eligibility = await checkRemoteAgentEligibility();
|
||||||
if (!eligibility.eligible) {
|
if (!eligibility.eligible) {
|
||||||
|
const eligErrors = (eligibility as { eligible: false; errors: Array<{ type: string }> }).errors;
|
||||||
logEvent('tengu_ultraplan_create_failed', {
|
logEvent('tengu_ultraplan_create_failed', {
|
||||||
reason: 'precondition' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
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({
|
enqueuePendingNotification({
|
||||||
value: `ultraplan: cannot launch remote session —\n${reasons}`,
|
value: `ultraplan: cannot launch remote session —\n${reasons}`,
|
||||||
mode: 'task-notification'
|
mode: 'task-notification'
|
||||||
|
|||||||
@ -235,7 +235,7 @@ export function ConsoleOAuthFlow({
|
|||||||
await installOAuthTokens(result);
|
await installOAuthTokens(result);
|
||||||
const orgResult = await validateForceLoginOrg();
|
const orgResult = await validateForceLoginOrg();
|
||||||
if (!orgResult.valid) {
|
if (!orgResult.valid) {
|
||||||
throw new Error(orgResult.message);
|
throw new Error((orgResult as { valid: false; message: string }).message);
|
||||||
}
|
}
|
||||||
setOAuthStatus({
|
setOAuthStatus({
|
||||||
state: 'success'
|
state: 'success'
|
||||||
|
|||||||
@ -59,8 +59,11 @@ type FeedbackData = {
|
|||||||
description: string;
|
description: string;
|
||||||
platform: string;
|
platform: string;
|
||||||
gitRepo: boolean;
|
gitRepo: boolean;
|
||||||
|
terminal: string;
|
||||||
version: string | null;
|
version: string | null;
|
||||||
transcript: Message[];
|
transcript: Message[];
|
||||||
|
errors: unknown;
|
||||||
|
lastApiRequest: unknown;
|
||||||
subagentTranscripts?: {
|
subagentTranscripts?: {
|
||||||
[agentId: string]: Message[];
|
[agentId: string]: Message[];
|
||||||
};
|
};
|
||||||
@ -203,8 +206,8 @@ export function Feedback({
|
|||||||
...diskTranscripts,
|
...diskTranscripts,
|
||||||
...teammateTranscripts
|
...teammateTranscripts
|
||||||
};
|
};
|
||||||
const reportData = {
|
const reportData: FeedbackData = {
|
||||||
latestAssistantMessageId: lastAssistantMessageId,
|
latestAssistantMessageId: lastAssistantMessageId as string | null,
|
||||||
message_count: messages.length,
|
message_count: messages.length,
|
||||||
datetime: new Date().toISOString(),
|
datetime: new Date().toISOString(),
|
||||||
description,
|
description,
|
||||||
|
|||||||
@ -50,7 +50,7 @@ export function FileEditToolDiff(props) {
|
|||||||
}
|
}
|
||||||
return t2;
|
return t2;
|
||||||
}
|
}
|
||||||
function DiffBody(t0) {
|
function DiffBody(t0: { promise: Promise<DiffData>; file_path: string }) {
|
||||||
const $ = _c(6);
|
const $ = _c(6);
|
||||||
const {
|
const {
|
||||||
promise,
|
promise,
|
||||||
|
|||||||
@ -134,7 +134,7 @@ function Highlighted(t0) {
|
|||||||
} else {
|
} else {
|
||||||
t1 = $[0];
|
t1 = $[0];
|
||||||
}
|
}
|
||||||
const hl = use(t1);
|
const hl = use(t1) as NonNullable<Awaited<ReturnType<typeof getCliHighlightPromise>>> | null;
|
||||||
let t2;
|
let t2;
|
||||||
if ($[1] !== codeWithSpaces || $[2] !== hl || $[3] !== language) {
|
if ($[1] !== codeWithSpaces || $[2] !== hl || $[3] !== language) {
|
||||||
bb0: {
|
bb0: {
|
||||||
|
|||||||
@ -129,7 +129,7 @@ function MarkdownBody(t0) {
|
|||||||
} = t0;
|
} = t0;
|
||||||
const [theme] = useTheme();
|
const [theme] = useTheme();
|
||||||
configureMarked();
|
configureMarked();
|
||||||
let elements;
|
let elements: React.ReactNode[];
|
||||||
if ($[0] !== children || $[1] !== dimColor || $[2] !== highlight || $[3] !== theme) {
|
if ($[0] !== children || $[1] !== dimColor || $[2] !== highlight || $[3] !== theme) {
|
||||||
const tokens = cachedLexer(stripPromptXMLTags(children));
|
const tokens = cachedLexer(stripPromptXMLTags(children));
|
||||||
elements = [];
|
elements = [];
|
||||||
@ -156,7 +156,7 @@ function MarkdownBody(t0) {
|
|||||||
$[3] = theme;
|
$[3] = theme;
|
||||||
$[4] = elements;
|
$[4] = elements;
|
||||||
} else {
|
} else {
|
||||||
elements = $[4];
|
elements = $[4] as React.ReactNode[];
|
||||||
}
|
}
|
||||||
const elements_0 = elements;
|
const elements_0 = elements;
|
||||||
let t1;
|
let t1;
|
||||||
|
|||||||
@ -606,7 +606,7 @@ export function areMessagePropsEqual(prev: Props, next: Props): boolean {
|
|||||||
// Only re-render on lastThinkingBlockId change if this message actually
|
// Only re-render on lastThinkingBlockId change if this message actually
|
||||||
// has thinking content — otherwise every message in scrollback re-renders
|
// has thinking content — otherwise every message in scrollback re-renders
|
||||||
// whenever streaming thinking starts/stops (CC-941).
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
// Verbose toggle changes thinking block visibility/expansion
|
// 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>
|
<Box flexDirection="column" paddingLeft={1} borderStyle="single" borderRight={false} borderTop={false} borderBottom={false} borderLeft={true} borderLeftDimColor>
|
||||||
<UserMessageOption userMessage={messageToRestore} color="text" isCurrent={false} />
|
<UserMessageOption userMessage={messageToRestore} color="text" isCurrent={false} />
|
||||||
<Text dimColor>
|
<Text dimColor>
|
||||||
({formatRelativeTimeAgo(new Date(messageToRestore.timestamp))})
|
({formatRelativeTimeAgo(new Date(messageToRestore.timestamp as number))})
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<RestoreOptionDescription selectedRestoreOption={selectedRestoreOption} canRestoreCode={!!canRestoreCode_0} diffStatsForRestore={diffStatsForRestore} />
|
<RestoreOptionDescription selectedRestoreOption={selectedRestoreOption} canRestoreCode={!!canRestoreCode_0} diffStatsForRestore={diffStatsForRestore} />
|
||||||
|
|||||||
@ -95,7 +95,8 @@ export function Settings(t0) {
|
|||||||
}
|
}
|
||||||
let t7;
|
let t7;
|
||||||
if ($[13] !== contentHeight) {
|
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;
|
$[13] = contentHeight;
|
||||||
$[14] = t7;
|
$[14] = t7;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -206,7 +206,7 @@ function Diagnostics(t0) {
|
|||||||
const {
|
const {
|
||||||
promise
|
promise
|
||||||
} = t0;
|
} = t0;
|
||||||
const diagnostics = use(promise);
|
const diagnostics = use(promise) as any[];
|
||||||
if (diagnostics.length === 0) {
|
if (diagnostics.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -272,10 +272,10 @@ export function GlimmerMessage(t0) {
|
|||||||
$[57] = shim;
|
$[57] = shim;
|
||||||
$[58] = colPos;
|
$[58] = colPos;
|
||||||
} else {
|
} else {
|
||||||
before = $[55];
|
before = $[55] as string;
|
||||||
after = $[56];
|
after = $[56] as string;
|
||||||
shim = $[57];
|
shim = $[57] as string;
|
||||||
colPos = $[58];
|
colPos = $[58] as number;
|
||||||
}
|
}
|
||||||
let t3;
|
let t3;
|
||||||
if ($[59] !== before || $[60] !== messageColor) {
|
if ($[59] !== before || $[60] !== messageColor) {
|
||||||
|
|||||||
@ -31,7 +31,7 @@ export function StatusNotices(t0) {
|
|||||||
const context = {
|
const context = {
|
||||||
config: t1,
|
config: t1,
|
||||||
agentDefinitions,
|
agentDefinitions,
|
||||||
memoryFiles: use(t2)
|
memoryFiles: use(t2) as any
|
||||||
};
|
};
|
||||||
const activeNotices = getActiveNotices(context);
|
const activeNotices = getActiveNotices(context);
|
||||||
if (activeNotices.length === 0) {
|
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
|
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
|
||||||
useVoiceState(s => s.voiceState) : 'idle' as const;
|
useVoiceState(s => s.voiceState) : 'idle' as const;
|
||||||
const isVoiceRecording = voiceState === 'recording';
|
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
|
// 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 smoothedRef = useRef<number[]>(new Array(CURSOR_WAVEFORM_WIDTH).fill(0));
|
||||||
const needsAnimation = isVoiceRecording && !reducedMotion;
|
const needsAnimation = isVoiceRecording && !reducedMotion;
|
||||||
const [animRef, animTime] = feature('VOICE_MODE') ?
|
const [animRef, animTime] = feature('VOICE_MODE') ?
|
||||||
|
|||||||
@ -72,7 +72,7 @@ export function ThemePicker(t0) {
|
|||||||
} = usePreviewTheme();
|
} = usePreviewTheme();
|
||||||
const syntaxHighlightingDisabled = useAppState(_temp) ?? false;
|
const syntaxHighlightingDisabled = useAppState(_temp) ?? false;
|
||||||
const setAppState = useSetAppState();
|
const setAppState = useSetAppState();
|
||||||
useRegisterKeybindingContext("ThemePicker");
|
useRegisterKeybindingContext("ThemePicker", undefined);
|
||||||
const syntaxToggleShortcut = useShortcutDisplay("theme:toggleSyntaxHighlighting", "ThemePicker", "ctrl+t");
|
const syntaxToggleShortcut = useShortcutDisplay("theme:toggleSyntaxHighlighting", "ThemePicker", "ctrl+t");
|
||||||
let t8;
|
let t8;
|
||||||
if ($[3] !== setAppState || $[4] !== syntaxHighlightingDisabled) {
|
if ($[3] !== setAppState || $[4] !== syntaxHighlightingDisabled) {
|
||||||
|
|||||||
@ -16,7 +16,7 @@ const HEADROOM = 3;
|
|||||||
import { logForDebugging } from '../utils/debug.js';
|
import { logForDebugging } from '../utils/debug.js';
|
||||||
import { sleep } from '../utils/sleep.js';
|
import { sleep } from '../utils/sleep.js';
|
||||||
import { renderableSearchText } from '../utils/transcriptSearch.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
|
// Fallback extractor: lower + cache here for callers without the
|
||||||
// Messages.tsx tool-lookup path (tests, static contexts). Messages.tsx
|
// Messages.tsx tool-lookup path (tests, static contexts). Messages.tsx
|
||||||
@ -151,7 +151,7 @@ function computeStickyPromptText(msg: RenderableMessage): string | null {
|
|||||||
raw = block.text;
|
raw = block.text;
|
||||||
} else if (msg.type === 'attachment' && msg.attachment.type === 'queued_command' && msg.attachment.commandMode !== 'task-notification' && !msg.attachment.isMeta) {
|
} else if (msg.type === 'attachment' && msg.attachment.type === 'queued_command' && msg.attachment.commandMode !== 'task-notification' && !msg.attachment.isMeta) {
|
||||||
const p = msg.attachment.prompt;
|
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;
|
if (raw === null) return null;
|
||||||
const t = stripSystemReminders(raw);
|
const t = stripSystemReminders(raw);
|
||||||
@ -345,7 +345,7 @@ export function VirtualMessageList({
|
|||||||
useImperativeHandle(cursorNavRef, (): MessageActionsNav => {
|
useImperativeHandle(cursorNavRef, (): MessageActionsNav => {
|
||||||
const select = (m: NavigableMessage) => setCursor?.({
|
const select = (m: NavigableMessage) => setCursor?.({
|
||||||
uuid: m.uuid,
|
uuid: m.uuid,
|
||||||
msgType: m.type,
|
msgType: m.type as NavigableType,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
toolName: toolCallOf(m)?.name
|
toolName: toolCallOf(m)?.name
|
||||||
});
|
});
|
||||||
|
|||||||
@ -251,7 +251,7 @@ export function ToolSelector(t0) {
|
|||||||
let t9;
|
let t9;
|
||||||
if ($[22] !== selectedSet) {
|
if ($[22] !== selectedSet) {
|
||||||
t9 = bucketTools => {
|
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;
|
const needsSelection = selected < bucketTools.length;
|
||||||
return () => {
|
return () => {
|
||||||
const toolNames_1 = bucketTools.map(_temp4);
|
const toolNames_1 = bucketTools.map(_temp4);
|
||||||
@ -321,7 +321,7 @@ export function ToolSelector(t0) {
|
|||||||
if (bucketTools_0.length === 0) {
|
if (bucketTools_0.length === 0) {
|
||||||
return;
|
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;
|
const isFullySelected = selected_0 === bucketTools_0.length;
|
||||||
navigableItems.push({
|
navigableItems.push({
|
||||||
id,
|
id,
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export function MemoryFileSelector(t0) {
|
|||||||
onSelect,
|
onSelect,
|
||||||
onCancel
|
onCancel
|
||||||
} = t0;
|
} = t0;
|
||||||
const existingMemoryFiles = use(getMemoryFiles());
|
const existingMemoryFiles = use(getMemoryFiles()) as MemoryFileInfo[];
|
||||||
const userMemoryPath = join(getClaudeConfigHomeDir(), "CLAUDE.md");
|
const userMemoryPath = join(getClaudeConfigHomeDir(), "CLAUDE.md");
|
||||||
const projectMemoryPath = join(getOriginalCwd(), "CLAUDE.md");
|
const projectMemoryPath = join(getOriginalCwd(), "CLAUDE.md");
|
||||||
const hasUserMemory = existingMemoryFiles.some(f => f.path === userMemoryPath);
|
const hasUserMemory = existingMemoryFiles.some(f => f.path === userMemoryPath);
|
||||||
|
|||||||
@ -337,7 +337,7 @@ function renderToolUseProgressMessage(tool: Tool, tools: Tools, lookups: ReturnT
|
|||||||
columns: number;
|
columns: number;
|
||||||
rows: number;
|
rows: number;
|
||||||
}): React.ReactNode {
|
}): 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 {
|
try {
|
||||||
const toolMessages = tool.renderToolUseProgressMessage?.(toolProgressMessages, {
|
const toolMessages = tool.renderToolUseProgressMessage?.(toolProgressMessages, {
|
||||||
tools,
|
tools,
|
||||||
|
|||||||
@ -40,8 +40,6 @@ const NULL_RENDERING_TYPES = [
|
|||||||
'auto_mode',
|
'auto_mode',
|
||||||
'auto_mode_exit',
|
'auto_mode_exit',
|
||||||
'output_token_usage',
|
'output_token_usage',
|
||||||
'pen_mode_enter',
|
|
||||||
'pen_mode_exit',
|
|
||||||
'verify_plan_reminder',
|
'verify_plan_reminder',
|
||||||
'current_session_memory',
|
'current_session_memory',
|
||||||
'compaction_reminder',
|
'compaction_reminder',
|
||||||
@ -65,6 +63,6 @@ export function isNullRenderingAttachment(
|
|||||||
): boolean {
|
): boolean {
|
||||||
return (
|
return (
|
||||||
msg.type === 'attachment' &&
|
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;
|
$[10] = theme;
|
||||||
$[11] = maxHeight;
|
$[11] = maxHeight;
|
||||||
} else {
|
} else {
|
||||||
maxHeight = $[11];
|
maxHeight = $[11] as number;
|
||||||
}
|
}
|
||||||
const t3 = Math.min(Math.max(maxHeight, MIN_CONTENT_HEIGHT), maxAllowedHeight);
|
const t3 = Math.min(Math.max(maxHeight, MIN_CONTENT_HEIGHT), maxAllowedHeight);
|
||||||
const t4 = Math.max(maxWidth, MIN_CONTENT_WIDTH);
|
const t4 = Math.max(maxWidth, MIN_CONTENT_WIDTH);
|
||||||
@ -177,7 +177,7 @@ function AskUserQuestionPermissionRequestBody(t0) {
|
|||||||
const pasteId = nextPasteIdRef.current;
|
const pasteId = nextPasteIdRef.current;
|
||||||
const newContent = {
|
const newContent = {
|
||||||
id: pasteId,
|
id: pasteId,
|
||||||
type: "image",
|
type: "image" as const,
|
||||||
content: base64Image,
|
content: base64Image,
|
||||||
mediaType: mediaType || "image/png",
|
mediaType: mediaType || "image/png",
|
||||||
filename: filename || "Pasted image",
|
filename: filename || "Pasted image",
|
||||||
|
|||||||
@ -151,7 +151,7 @@ export function ExitPlanModePermissionRequest({
|
|||||||
const options = useMemo(() => buildPlanApprovalOptions({
|
const options = useMemo(() => buildPlanApprovalOptions({
|
||||||
showClearContext,
|
showClearContext,
|
||||||
showUltraplan,
|
showUltraplan,
|
||||||
usedPercent: showClearContext ? getContextUsedPercent(usage, mode) : null,
|
usedPercent: showClearContext ? getContextUsedPercent(usage as any, mode) : null,
|
||||||
isAutoModeAvailable,
|
isAutoModeAvailable,
|
||||||
isBypassPermissionsModeAvailable,
|
isBypassPermissionsModeAvailable,
|
||||||
onFeedbackChange: setPlanFeedback
|
onFeedbackChange: setPlanFeedback
|
||||||
|
|||||||
@ -62,7 +62,7 @@ function PermissionDecisionInfoItem(t0) {
|
|||||||
return <Box flexDirection="column">{Array.from(decisionReason.reasons.entries()).map(t2 => {
|
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 [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);
|
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>;
|
})}</Box>;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -102,7 +102,7 @@ function SedEditPermissionRequestInner(t0) {
|
|||||||
const {
|
const {
|
||||||
oldContent,
|
oldContent,
|
||||||
fileExists
|
fileExists
|
||||||
} = use(contentPromise);
|
} = use(contentPromise) as any;
|
||||||
let t1;
|
let t1;
|
||||||
if ($[4] !== oldContent || $[5] !== sedInfo) {
|
if ($[4] !== oldContent || $[5] !== sedInfo) {
|
||||||
t1 = applySedSubstitution(oldContent, sedInfo);
|
t1 = applySedSubstitution(oldContent, sedInfo);
|
||||||
|
|||||||
@ -310,7 +310,7 @@ function ShellOutputContent(t0) {
|
|||||||
const {
|
const {
|
||||||
content,
|
content,
|
||||||
bytesTotal
|
bytesTotal
|
||||||
} = use(outputPromise);
|
} = use(outputPromise) as any;
|
||||||
if (!content) {
|
if (!content) {
|
||||||
let t1;
|
let t1;
|
||||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||||
|
|||||||
@ -132,11 +132,11 @@ function useCanUseTool(setToolUseConfirmQueue, setToolPermissionContext) {
|
|||||||
if (ctx.resolveIfAborted(resolve)) {
|
if (ctx.resolveIfAborted(resolve)) {
|
||||||
return;
|
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 {
|
consumeSpeculativeClassifierCheck((input as {
|
||||||
command: string;
|
command: string;
|
||||||
}).command);
|
}).command);
|
||||||
const matchedRule = raceResult.result.matchedDescription ?? undefined;
|
const matchedRule = (raceResult as any).result.matchedDescription ?? undefined;
|
||||||
if (matchedRule) {
|
if (matchedRule) {
|
||||||
setClassifierApproval(toolUseID, matchedRule);
|
setClassifierApproval(toolUseID, matchedRule);
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ function useCanUseTool(setToolUseConfirmQueue, setToolPermissionContext) {
|
|||||||
decisionReason: {
|
decisionReason: {
|
||||||
type: "classifier" as const,
|
type: "classifier" as const,
|
||||||
classifier: "bash_allow" 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;
|
return;
|
||||||
|
|||||||
@ -370,7 +370,7 @@ export function useReplBridge(messages: Message[], setMessages: (action: React.S
|
|||||||
|
|
||||||
// Dispatch incoming control_response messages to registered handlers
|
// Dispatch incoming control_response messages to registered handlers
|
||||||
function handlePermissionResponse(msg_0: SDKControlResponse): void {
|
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;
|
if (!requestId) return;
|
||||||
const handler = pendingPermissionHandlers.get(requestId);
|
const handler = pendingPermissionHandlers.get(requestId);
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
@ -379,7 +379,7 @@ export function useReplBridge(messages: Message[], setMessages: (action: React.S
|
|||||||
}
|
}
|
||||||
pendingPermissionHandlers.delete(requestId);
|
pendingPermissionHandlers.delete(requestId);
|
||||||
// Extract the permission decision from the control_response payload
|
// 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)) {
|
if (inner.subtype === 'success' && inner.response && isBridgePermissionResponse(inner.response)) {
|
||||||
handler(inner.response);
|
handler(inner.response);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -225,9 +225,9 @@ export function useVoiceIntegration({
|
|||||||
const voiceState = feature('VOICE_MODE') ?
|
const voiceState = feature('VOICE_MODE') ?
|
||||||
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
|
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
|
||||||
useVoiceState(s => s.voiceState) : 'idle' as const;
|
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
|
// 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
|
// Set the voice anchor for focus mode (where recording starts via terminal
|
||||||
// focus, not key hold). Key-hold sets the anchor in stripTrailing.
|
// 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.
|
* 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 $ = _c(12);
|
||||||
const {
|
const {
|
||||||
children,
|
children,
|
||||||
|
|||||||
@ -159,7 +159,7 @@ export async function getAnthropicClient({
|
|||||||
? process.env.ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION
|
? process.env.ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION
|
||||||
: getAWSRegion()
|
: getAWSRegion()
|
||||||
|
|
||||||
const bedrockArgs: ConstructorParameters<typeof AnthropicBedrock>[0] = {
|
const bedrockArgs: any = {
|
||||||
...ARGS,
|
...ARGS,
|
||||||
awsRegion,
|
awsRegion,
|
||||||
...(isEnvTruthy(process.env.CLAUDE_CODE_SKIP_BEDROCK_AUTH) && {
|
...(isEnvTruthy(process.env.CLAUDE_CODE_SKIP_BEDROCK_AUTH) && {
|
||||||
@ -290,7 +290,7 @@ export async function getAnthropicClient({
|
|||||||
const vertexArgs: ConstructorParameters<typeof AnthropicVertex>[0] = {
|
const vertexArgs: ConstructorParameters<typeof AnthropicVertex>[0] = {
|
||||||
...ARGS,
|
...ARGS,
|
||||||
region: getVertexRegionForModel(model),
|
region: getVertexRegionForModel(model),
|
||||||
googleAuth,
|
googleAuth: googleAuth as any,
|
||||||
...(isDebugToStdErr() && { logger: createStderrLogger() }),
|
...(isDebugToStdErr() && { logger: createStderrLogger() }),
|
||||||
}
|
}
|
||||||
// we have always been lying about the return type - this doesn't support batching or models
|
// 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
|
return undefined
|
||||||
}
|
}
|
||||||
const { actualTokens, limitTokens } = parsePromptTooLongTokenCounts(
|
const { actualTokens, limitTokens } = parsePromptTooLongTokenCounts(
|
||||||
msg.errorDetails,
|
msg.errorDetails as string,
|
||||||
)
|
)
|
||||||
if (actualTokens === undefined || limitTokens === undefined) {
|
if (actualTokens === undefined || limitTokens === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
@ -148,7 +148,7 @@ export function isMediaSizeErrorMessage(msg: AssistantMessage): boolean {
|
|||||||
return (
|
return (
|
||||||
msg.isApiErrorMessage === true &&
|
msg.isApiErrorMessage === true &&
|
||||||
msg.errorDetails !== undefined &&
|
msg.errorDetails !== undefined &&
|
||||||
isMediaSizeError(msg.errorDetails)
|
isMediaSizeError(msg.errorDetails as string)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export const CREDIT_BALANCE_TOO_LOW_ERROR_MESSAGE = 'Credit balance is too low'
|
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
|
// Keys never collide (`slack` vs `claude.ai Slack`) so the merge below
|
||||||
// won't catch this — need content-based dedup by URL signature.
|
// won't catch this — need content-based dedup by URL signature.
|
||||||
const { servers: dedupedClaudeAi } = dedupClaudeAiMcpServers(
|
const { servers: dedupedClaudeAi } = dedupClaudeAiMcpServers(
|
||||||
claudeaiMcpServers,
|
claudeaiMcpServers as Record<string, ScopedMcpServerConfig>,
|
||||||
claudeCodeServers,
|
claudeCodeServers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1351,6 +1351,7 @@ export function parseMcpConfig(params: {
|
|||||||
if (
|
if (
|
||||||
getPlatform() === 'windows' &&
|
getPlatform() === 'windows' &&
|
||||||
(!configToCheck.type || configToCheck.type === 'stdio') &&
|
(!configToCheck.type || configToCheck.type === 'stdio') &&
|
||||||
|
('command' in configToCheck) &&
|
||||||
(configToCheck.command === 'npx' ||
|
(configToCheck.command === 'npx' ||
|
||||||
configToCheck.command.endsWith('\\npx') ||
|
configToCheck.command.endsWith('\\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'
|
result.message.attachment.type === 'hook_blocking_error'
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
yield { message: result.message }
|
yield { message: result.message as AttachmentMessage | ProgressMessage<HookProgress> }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.blockingError) {
|
if (result.blockingError) {
|
||||||
@ -251,7 +251,7 @@ export async function* runPostToolUseFailureHooks<Input extends AnyObject>(
|
|||||||
result.message.attachment.type === 'hook_blocking_error'
|
result.message.attachment.type === 'hook_blocking_error'
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
yield { message: result.message }
|
yield { message: result.message as AttachmentMessage | ProgressMessage<HookProgress> }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.blockingError) {
|
if (result.blockingError) {
|
||||||
@ -476,7 +476,7 @@ export async function* runPreToolUseHooks(
|
|||||||
)) {
|
)) {
|
||||||
try {
|
try {
|
||||||
if (result.message) {
|
if (result.message) {
|
||||||
yield { type: 'message', message: { message: result.message } }
|
yield { type: 'message', message: { message: result.message as AttachmentMessage | ProgressMessage<HookProgress> } }
|
||||||
}
|
}
|
||||||
if (result.blockingError) {
|
if (result.blockingError) {
|
||||||
const denialMessage = getPreToolHookBlockingMessage(
|
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 { createHash, randomUUID, type UUID } from 'crypto'
|
||||||
import { mkdir, readFile, writeFile } from 'fs/promises'
|
import { mkdir, readFile, writeFile } from 'fs/promises'
|
||||||
import isPlainObject from 'lodash-es/isPlainObject.js'
|
import isPlainObject from 'lodash-es/isPlainObject.js'
|
||||||
@ -166,8 +166,8 @@ function addCachedCostToTotalSessionCost(
|
|||||||
if (message.type === 'stream_event') {
|
if (message.type === 'stream_event') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const model = message.message.model
|
const model = (message as AssistantMessage).message.model as string
|
||||||
const usage = message.message.usage
|
const usage = (message as AssistantMessage).message.usage as BetaUsage
|
||||||
const costUSD = calculateUSDCost(model, usage)
|
const costUSD = calculateUSDCost(model, usage)
|
||||||
addToTotalSessionCost(costUSD, usage, model)
|
addToTotalSessionCost(costUSD, usage, model)
|
||||||
}
|
}
|
||||||
@ -251,7 +251,7 @@ function mapAssistantMessage(
|
|||||||
timestamp: message.timestamp,
|
timestamp: message.timestamp,
|
||||||
message: {
|
message: {
|
||||||
...message.message,
|
...message.message,
|
||||||
content: message.message.content
|
content: (message.message.content as BetaContentBlock[])
|
||||||
.map(_ => {
|
.map(_ => {
|
||||||
switch (_.type) {
|
switch (_.type) {
|
||||||
case 'text':
|
case 'text':
|
||||||
@ -269,7 +269,7 @@ function mapAssistantMessage(
|
|||||||
return _ // Handle other block types unchanged
|
return _ // Handle other block types unchanged
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(Boolean) as BetaContentBlock[],
|
.filter(Boolean) as any,
|
||||||
},
|
},
|
||||||
type: 'assistant',
|
type: 'assistant',
|
||||||
}
|
}
|
||||||
@ -282,7 +282,7 @@ function mapMessage(
|
|||||||
uuid?: UUID,
|
uuid?: UUID,
|
||||||
): AssistantMessage | SystemAPIErrorMessage | StreamEvent {
|
): AssistantMessage | SystemAPIErrorMessage | StreamEvent {
|
||||||
if (message.type === 'assistant') {
|
if (message.type === 'assistant') {
|
||||||
return mapAssistantMessage(message, f, index, uuid)
|
return mapAssistantMessage(message as AssistantMessage, f, index, uuid)
|
||||||
} else {
|
} else {
|
||||||
return message
|
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
|
// If the task was already marked as notified (e.g., by TaskStopTool), skip
|
||||||
// enqueueing to avoid sending redundant messages to the model.
|
// enqueueing to avoid sending redundant messages to the model.
|
||||||
let shouldEnqueue = false;
|
let shouldEnqueue = false;
|
||||||
updateTaskState(taskId, setAppState, task => {
|
updateTaskState<LocalShellTaskState>(taskId, setAppState, task => {
|
||||||
if (task.notified) {
|
if (task.notified) {
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
@ -479,7 +479,7 @@ export function backgroundExistingForegroundTask(taskId: string, shellCommand: S
|
|||||||
* carries the full output, so the <task_notification> would be redundant.
|
* carries the full output, so the <task_notification> would be redundant.
|
||||||
*/
|
*/
|
||||||
export function markTaskNotified(taskId: string, setAppState: SetAppState): void {
|
export function markTaskNotified(taskId: string, setAppState: SetAppState): void {
|
||||||
updateTaskState(taskId, setAppState, t => t.notified ? t : {
|
updateTaskState<LocalShellTaskState>(taskId, setAppState, t => t.notified ? t : {
|
||||||
...t,
|
...t,
|
||||||
notified: true
|
notified: true
|
||||||
});
|
});
|
||||||
|
|||||||
@ -760,17 +760,17 @@ export async function* runAgent({
|
|||||||
// so TTFT/OTPS update during subagent execution.
|
// so TTFT/OTPS update during subagent execution.
|
||||||
if (
|
if (
|
||||||
message.type === 'stream_event' &&
|
message.type === 'stream_event' &&
|
||||||
message.event.type === 'message_start' &&
|
(message as any).event.type === 'message_start' &&
|
||||||
message.ttftMs != null
|
(message as any).ttftMs != null
|
||||||
) {
|
) {
|
||||||
toolUseContext.pushApiMetricsEntry?.(message.ttftMs)
|
toolUseContext.pushApiMetricsEntry?.((message as any).ttftMs)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yield attachment messages (e.g., structured_output) without recording them
|
// Yield attachment messages (e.g., structured_output) without recording them
|
||||||
if (message.type === 'attachment') {
|
if (message.type === 'attachment') {
|
||||||
// Handle max turns reached signal from query.ts
|
// 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(
|
logForDebugging(
|
||||||
`[Agent
|
`[Agent
|
||||||
: $
|
: $
|
||||||
@ -779,13 +779,13 @@ export async function* runAgent({
|
|||||||
}
|
}
|
||||||
] Reached max turns limit ($
|
] Reached max turns limit ($
|
||||||
{
|
{
|
||||||
message.attachment.maxTurns
|
(message as any).attachment.maxTurns
|
||||||
}
|
}
|
||||||
)`,
|
)`,
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
yield message
|
yield message as Message
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -222,7 +222,7 @@ function EditRejectionBody(t0) {
|
|||||||
patch,
|
patch,
|
||||||
firstLine,
|
firstLine,
|
||||||
fileContent
|
fileContent
|
||||||
} = use(promise);
|
} = use(promise) as any;
|
||||||
let t1;
|
let t1;
|
||||||
if ($[0] !== fileContent || $[1] !== filePath || $[2] !== firstLine || $[3] !== patch || $[4] !== style || $[5] !== verbose) {
|
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} />;
|
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,
|
parsedRange ?? undefined,
|
||||||
)
|
)
|
||||||
if (!extractResult.success) {
|
if (!extractResult.success) {
|
||||||
throw new Error(extractResult.error.message)
|
throw new Error((extractResult as any).error.message)
|
||||||
}
|
}
|
||||||
logEvent('tengu_pdf_page_extraction', {
|
logEvent('tengu_pdf_page_extraction', {
|
||||||
success: true,
|
success: true,
|
||||||
pageCount: extractResult.data.file.count,
|
pageCount: (extractResult as any).data.file.count,
|
||||||
fileSize: extractResult.data.file.originalSize,
|
fileSize: extractResult.data.file.originalSize,
|
||||||
hasPageRange: true,
|
hasPageRange: true,
|
||||||
})
|
})
|
||||||
@ -970,7 +970,7 @@ async function callInner(
|
|||||||
} else {
|
} else {
|
||||||
logEvent('tengu_pdf_page_extraction', {
|
logEvent('tengu_pdf_page_extraction', {
|
||||||
success: false,
|
success: false,
|
||||||
available: extractResult.error.reason !== 'unavailable',
|
available: (extractResult as any).error.reason !== 'unavailable',
|
||||||
fileSize: stats.size,
|
fileSize: stats.size,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -986,7 +986,7 @@ async function callInner(
|
|||||||
|
|
||||||
const readResult = await readPDF(resolvedFilePath)
|
const readResult = await readPDF(resolvedFilePath)
|
||||||
if (!readResult.success) {
|
if (!readResult.success) {
|
||||||
throw new Error(readResult.error.message)
|
throw new Error((readResult as any).error.message)
|
||||||
}
|
}
|
||||||
const pdfData = readResult.data
|
const pdfData = readResult.data
|
||||||
logFileOperation({
|
logFileOperation({
|
||||||
@ -1158,12 +1158,12 @@ export async function readImageWithTokenBudget(
|
|||||||
const sharpModule = await import('sharp')
|
const sharpModule = await import('sharp')
|
||||||
const sharp =
|
const sharp =
|
||||||
(
|
(
|
||||||
sharpModule as {
|
sharpModule as unknown as {
|
||||||
default?: typeof sharpModule
|
default?: typeof sharpModule
|
||||||
} & typeof sharpModule
|
} & typeof sharpModule
|
||||||
).default || sharpModule
|
).default || sharpModule
|
||||||
|
|
||||||
const fallbackBuffer = await sharp(imageBuffer)
|
const fallbackBuffer = await (sharp as any)(imageBuffer)
|
||||||
.resize(400, 400, {
|
.resize(400, 400, {
|
||||||
fit: 'inside',
|
fit: 'inside',
|
||||||
withoutEnlargement: true,
|
withoutEnlargement: true,
|
||||||
|
|||||||
@ -826,7 +826,7 @@ export const SendMessageTool: Tool<InputSchema, SendMessageToolOutput> =
|
|||||||
prompt: input.message,
|
prompt: input.message,
|
||||||
toolUseContext: context,
|
toolUseContext: context,
|
||||||
canUseTool,
|
canUseTool,
|
||||||
invokingRequestId: assistantMessage?.requestId,
|
invokingRequestId: assistantMessage?.requestId as string | undefined,
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
data: {
|
data: {
|
||||||
@ -853,7 +853,7 @@ export const SendMessageTool: Tool<InputSchema, SendMessageToolOutput> =
|
|||||||
prompt: input.message,
|
prompt: input.message,
|
||||||
toolUseContext: context,
|
toolUseContext: context,
|
||||||
canUseTool,
|
canUseTool,
|
||||||
invokingRequestId: assistantMessage?.requestId,
|
invokingRequestId: assistantMessage?.requestId as string | undefined,
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
data: {
|
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 BUILD_ENV: string
|
||||||
declare const INTERFACE_TYPE: 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
|
// Bun text/file loaders — allow importing non-TS assets as strings
|
||||||
declare module '*.md' {
|
declare module '*.md' {
|
||||||
|
|||||||
@ -262,7 +262,7 @@ export async function startNodeRelay(
|
|||||||
},
|
},
|
||||||
end: () => sock.end(),
|
end: () => sock.end(),
|
||||||
}
|
}
|
||||||
sock.on('data', data =>
|
sock.on('data', (data: Buffer) =>
|
||||||
handleData(adapter, st, data, wsUrl, authHeader, wsAuthHeader),
|
handleData(adapter, st, data, wsUrl, authHeader, wsAuthHeader),
|
||||||
)
|
)
|
||||||
sock.on('close', () => cleanupConn(states.get(sock)))
|
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.
|
// responds with its own "HTTP/1.1 200" over the tunnel; we just pipe it.
|
||||||
const head =
|
const head =
|
||||||
`${connectLine}\r\n` + `Proxy-Authorization: ${authHeader}\r\n` + `\r\n`
|
`${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 —
|
// Flush anything that arrived while the WS handshake was in flight —
|
||||||
// trailing bytes from the CONNECT packet and any data() callbacks that
|
// trailing bytes from the CONNECT packet and any data() callbacks that
|
||||||
// fired before onopen.
|
// fired before onopen.
|
||||||
@ -429,15 +429,15 @@ function openTunnel(
|
|||||||
|
|
||||||
function sendKeepalive(ws: WebSocketLike): void {
|
function sendKeepalive(ws: WebSocketLike): void {
|
||||||
if (ws.readyState === WebSocket.OPEN) {
|
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 {
|
function forwardToWs(ws: WebSocketLike, data: Buffer): void {
|
||||||
if (ws.readyState !== WebSocket.OPEN) return
|
if (ws.readyState !== WebSocket.OPEN) return
|
||||||
for (let off = 0; off < data.length; off += MAX_CHUNK_BYTES) {
|
for (let off = 0; off < data.length; off += MAX_CHUNK_BYTES) {
|
||||||
const slice = data.subarray(off, off + MAX_CHUNK_BYTES)
|
const slice = new Uint8Array(data.subarray(off, off + MAX_CHUNK_BYTES))
|
||||||
ws.send(encodeChunk(slice))
|
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
|
// 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
|
// prompt containing ``` cannot close the fence early and un-wrap the
|
||||||
// trailing text (CommonMark fence-matching rule).
|
// trailing text (CommonMark fence-matching rule).
|
||||||
const longestRun = (t.prompt.match(/`+/g) ?? []).reduce(
|
const longestRun = (t.prompt.match(/`+/g) ?? ([] as string[])).reduce(
|
||||||
(max, run) => Math.max(max, run.length),
|
(max: number, run: string) => Math.max(max, run.length),
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
const fence = '`'.repeat(Math.max(3, longestRun + 1))
|
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 { errorMessage } from '../errors.js'
|
||||||
import { jsonParse } from '../slowOperations.js'
|
import { jsonParse } from '../slowOperations.js'
|
||||||
|
|
||||||
@ -12,15 +12,15 @@ import { jsonParse } from '../slowOperations.js'
|
|||||||
*/
|
*/
|
||||||
export async function validateManifest(
|
export async function validateManifest(
|
||||||
manifestJson: unknown,
|
manifestJson: unknown,
|
||||||
): Promise<McpbManifest> {
|
): Promise<McpbManifestAny> {
|
||||||
const { McpbManifestSchema } = await import('@anthropic-ai/mcpb')
|
const { vAny } = await import('@anthropic-ai/mcpb')
|
||||||
const parseResult = McpbManifestSchema.safeParse(manifestJson)
|
const parseResult = vAny.McpbManifestSchema.safeParse(manifestJson)
|
||||||
|
|
||||||
if (!parseResult.success) {
|
if (!parseResult.success) {
|
||||||
const errors = parseResult.error.flatten()
|
const errors = parseResult.error.flatten()
|
||||||
const errorMessages = [
|
const errorMessages = [
|
||||||
...Object.entries(errors.fieldErrors).map(
|
...Object.entries(errors.fieldErrors).map(
|
||||||
([field, errs]) => `${field}: ${errs?.join(', ')}`,
|
([field, errs]) => `${field}: ${(errs as any)?.join(', ')}`,
|
||||||
),
|
),
|
||||||
...(errors.formErrors || []),
|
...(errors.formErrors || []),
|
||||||
]
|
]
|
||||||
@ -38,7 +38,7 @@ export async function validateManifest(
|
|||||||
*/
|
*/
|
||||||
export async function parseAndValidateManifestFromText(
|
export async function parseAndValidateManifestFromText(
|
||||||
manifestText: string,
|
manifestText: string,
|
||||||
): Promise<McpbManifest> {
|
): Promise<McpbManifestAny> {
|
||||||
let manifestJson: unknown
|
let manifestJson: unknown
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -55,7 +55,7 @@ export async function parseAndValidateManifestFromText(
|
|||||||
*/
|
*/
|
||||||
export async function parseAndValidateManifestFromBytes(
|
export async function parseAndValidateManifestFromBytes(
|
||||||
manifestData: Uint8Array,
|
manifestData: Uint8Array,
|
||||||
): Promise<McpbManifest> {
|
): Promise<McpbManifestAny> {
|
||||||
const manifestText = new TextDecoder().decode(manifestData)
|
const manifestText = new TextDecoder().decode(manifestData)
|
||||||
return parseAndValidateManifestFromText(manifestText)
|
return parseAndValidateManifestFromText(manifestText)
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ export async function parseAndValidateManifestFromBytes(
|
|||||||
* Uses the same algorithm as the directory backend for consistency.
|
* Uses the same algorithm as the directory backend for consistency.
|
||||||
*/
|
*/
|
||||||
export function generateExtensionId(
|
export function generateExtensionId(
|
||||||
manifest: McpbManifest,
|
manifest: McpbManifestAny,
|
||||||
prefix?: 'local.unpacked' | 'local.dxt',
|
prefix?: 'local.unpacked' | 'local.dxt',
|
||||||
): string {
|
): string {
|
||||||
const sanitize = (str: string) =>
|
const sanitize = (str: string) =>
|
||||||
|
|||||||
@ -64,12 +64,12 @@ export async function findModifiedFiles(
|
|||||||
outputsDir: string,
|
outputsDir: string,
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
// Use recursive flag to get all entries in one call
|
// 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 {
|
try {
|
||||||
entries = await fs.readdir(outputsDir, {
|
entries = await fs.readdir(outputsDir, {
|
||||||
withFileTypes: true,
|
withFileTypes: true,
|
||||||
recursive: true,
|
recursive: true,
|
||||||
})
|
}) as any[]
|
||||||
} catch {
|
} catch {
|
||||||
// Directory doesn't exist or is not accessible
|
// Directory doesn't exist or is not accessible
|
||||||
return []
|
return []
|
||||||
@ -113,7 +113,7 @@ export async function findModifiedFiles(
|
|||||||
// Filter to files modified since turn start
|
// Filter to files modified since turn start
|
||||||
const modifiedFiles: string[] = []
|
const modifiedFiles: string[] = []
|
||||||
for (const result of statResults) {
|
for (const result of statResults) {
|
||||||
if (result && result.mtimeMs >= turnStartTime) {
|
if (result && result.mtimeMs >= (turnStartTime as any as number)) {
|
||||||
modifiedFiles.push(result.filePath)
|
modifiedFiles.push(result.filePath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -558,10 +558,10 @@ export async function runForkedAgent({
|
|||||||
if (message.type === 'stream_event') {
|
if (message.type === 'stream_event') {
|
||||||
if (
|
if (
|
||||||
'event' in message &&
|
'event' in message &&
|
||||||
message.event?.type === 'message_delta' &&
|
(message as any).event?.type === 'message_delta' &&
|
||||||
message.event.usage
|
(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)
|
totalUsage = accumulateUsage(totalUsage, turnUsage)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -1325,14 +1325,15 @@ export function checkWritePermissionForTool<Input extends AnyObject>(
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
: generateSuggestions(path, 'write', toolPermissionContext, pathsToCheck)
|
: generateSuggestions(path, 'write', toolPermissionContext, pathsToCheck)
|
||||||
|
const failedCheck = safetyCheck as { safe: false; message: string; classifierApprovable: boolean }
|
||||||
return {
|
return {
|
||||||
behavior: 'ask',
|
behavior: 'ask',
|
||||||
message: safetyCheck.message,
|
message: failedCheck.message,
|
||||||
suggestions: safetySuggestions,
|
suggestions: safetySuggestions,
|
||||||
decisionReason: {
|
decisionReason: {
|
||||||
type: 'safetyCheck',
|
type: 'safetyCheck',
|
||||||
reason: safetyCheck.message,
|
reason: failedCheck.message,
|
||||||
classifierApprovable: safetyCheck.classifierApprovable,
|
classifierApprovable: failedCheck.classifierApprovable,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -112,8 +112,8 @@ export function isPathInSandboxWriteAllowlist(resolvedPath: string): boolean {
|
|||||||
// their resolution to avoid N × config.length redundant syscalls per
|
// their resolution to avoid N × config.length redundant syscalls per
|
||||||
// command with N write targets (matching getResolvedWorkingDirPaths).
|
// command with N write targets (matching getResolvedWorkingDirPaths).
|
||||||
const pathsToCheck = getPathsForPermissionCheck(resolvedPath)
|
const pathsToCheck = getPathsForPermissionCheck(resolvedPath)
|
||||||
const resolvedAllow = allowOnly.flatMap(getResolvedSandboxConfigPath)
|
const resolvedAllow = allowOnly.flatMap(getResolvedSandboxConfigPath) as string[]
|
||||||
const resolvedDeny = denyWithinAllow.flatMap(getResolvedSandboxConfigPath)
|
const resolvedDeny = denyWithinAllow.flatMap(getResolvedSandboxConfigPath) as string[]
|
||||||
return pathsToCheck.every(p => {
|
return pathsToCheck.every(p => {
|
||||||
for (const denyPath of resolvedDeny) {
|
for (const denyPath of resolvedDeny) {
|
||||||
if (pathInWorkingPath(p, denyPath)) return false
|
if (pathInWorkingPath(p, denyPath)) return false
|
||||||
@ -184,12 +184,13 @@ export function isPathAllowed(
|
|||||||
precomputedPathsToCheck,
|
precomputedPathsToCheck,
|
||||||
)
|
)
|
||||||
if (!safetyCheck.safe) {
|
if (!safetyCheck.safe) {
|
||||||
|
const failedCheck = safetyCheck as { safe: false; message: string; classifierApprovable: boolean }
|
||||||
return {
|
return {
|
||||||
allowed: false,
|
allowed: false,
|
||||||
decisionReason: {
|
decisionReason: {
|
||||||
type: 'safetyCheck',
|
type: 'safetyCheck',
|
||||||
reason: safetyCheck.message,
|
reason: failedCheck.message,
|
||||||
classifierApprovable: safetyCheck.classifierApprovable,
|
classifierApprovable: failedCheck.classifierApprovable,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -412,7 +412,7 @@ async function runPermissionRequestHooksForHeadlessAgent(
|
|||||||
input,
|
input,
|
||||||
context,
|
context,
|
||||||
permissionMode,
|
permissionMode,
|
||||||
suggestions,
|
suggestions as any,
|
||||||
context.abortController.signal,
|
context.abortController.signal,
|
||||||
)) {
|
)) {
|
||||||
if (!hookResult.permissionRequestResult) {
|
if (!hookResult.permissionRequestResult) {
|
||||||
@ -423,12 +423,12 @@ async function runPermissionRequestHooksForHeadlessAgent(
|
|||||||
const finalInput = decision.updatedInput ?? input
|
const finalInput = decision.updatedInput ?? input
|
||||||
// Persist permission updates if provided
|
// Persist permission updates if provided
|
||||||
if (decision.updatedPermissions?.length) {
|
if (decision.updatedPermissions?.length) {
|
||||||
persistPermissionUpdates(decision.updatedPermissions)
|
persistPermissionUpdates(decision.updatedPermissions as any)
|
||||||
context.setAppState(prev => ({
|
context.setAppState(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
toolPermissionContext: applyPermissionUpdates(
|
toolPermissionContext: applyPermissionUpdates(
|
||||||
prev.toolPermissionContext,
|
prev.toolPermissionContext,
|
||||||
decision.updatedPermissions!,
|
decision.updatedPermissions as any,
|
||||||
),
|
),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -251,12 +251,12 @@ export async function processUserInput({
|
|||||||
...hookResult.message,
|
...hookResult.message,
|
||||||
attachment: {
|
attachment: {
|
||||||
...hookResult.message.attachment,
|
...hookResult.message.attachment,
|
||||||
content: applyTruncation(hookResult.message.attachment.content),
|
content: applyTruncation(hookResult.message.attachment.content as string),
|
||||||
},
|
},
|
||||||
})
|
} as AttachmentMessage)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
result.messages.push(hookResult.message)
|
result.messages.push(hookResult.message as AttachmentMessage)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -125,7 +125,7 @@ ${question}`
|
|||||||
function extractSideQuestionResponse(messages: Message[]): string | null {
|
function extractSideQuestionResponse(messages: Message[]): string | null {
|
||||||
// Flatten all assistant content blocks across the per-block messages.
|
// Flatten all assistant content blocks across the per-block messages.
|
||||||
const assistantBlocks = messages.flatMap(m =>
|
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) {
|
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.
|
// No text — check if the model tried to call a tool despite instructions.
|
||||||
const toolUse = assistantBlocks.find(b => b.type === 'tool_use')
|
const toolUse = assistantBlocks.find(b => b.type === 'tool_use')
|
||||||
if (toolUse) {
|
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.)`
|
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',
|
m.type === 'system' && 'subtype' in m && m.subtype === 'api_error',
|
||||||
)
|
)
|
||||||
if (apiErr) {
|
if (apiErr) {
|
||||||
return `(API error: ${formatAPIError(apiErr.error)})`
|
return `(API error: ${formatAPIError(apiErr.error as any)})`
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export default function sliceAnsi(
|
|||||||
// pass start/end in display cells (via stringWidth), so position must
|
// pass start/end in display cells (via stringWidth), so position must
|
||||||
// track the same units.
|
// track the same units.
|
||||||
const width =
|
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
|
// Break AFTER trailing zero-width marks — a combining mark attaches to
|
||||||
// the preceding base char, so "भा" (भ + ा, 1 display cell) sliced at
|
// the preceding base char, so "भा" (भ + ा, 1 display cell) sliced at
|
||||||
@ -77,7 +77,7 @@ export default function sliceAnsi(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (include) {
|
if (include) {
|
||||||
result += token.value
|
result += (token as any).value
|
||||||
}
|
}
|
||||||
|
|
||||||
position += width
|
position += width
|
||||||
|
|||||||
@ -231,16 +231,17 @@ export async function createAndUploadGitBundle(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!bundle.ok) {
|
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', {
|
logEvent('tengu_ccr_bundle_upload', {
|
||||||
outcome:
|
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,
|
max_bytes: maxBytes,
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: bundle.error,
|
error: failedBundle.error,
|
||||||
failReason: bundle.failReason,
|
failReason: failedBundle.failReason,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,7 +255,7 @@ export async function createAndUploadGitBundle(
|
|||||||
outcome:
|
outcome:
|
||||||
'failed' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
'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(
|
logForDebugging(
|
||||||
|
|||||||
@ -551,7 +551,7 @@ export function extractDiscoveredToolNames(messages: Message[]): Set<string> {
|
|||||||
// check rather than isCompactBoundaryMessage — utils/messages.ts imports
|
// check rather than isCompactBoundaryMessage — utils/messages.ts imports
|
||||||
// from this file, so importing back would be circular.
|
// from this file, so importing back would be circular.
|
||||||
if (msg.type === 'system' && msg.subtype === 'compact_boundary') {
|
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) {
|
if (carried) {
|
||||||
for (const name of carried) discoveredTools.add(name)
|
for (const name of carried) discoveredTools.add(name)
|
||||||
carriedFromBoundary += carried.length
|
carriedFromBoundary += carried.length
|
||||||
@ -658,8 +658,8 @@ export function getDeferredToolsDelta(
|
|||||||
attachmentTypesSeen.add(msg.attachment.type)
|
attachmentTypesSeen.add(msg.attachment.type)
|
||||||
if (msg.attachment.type !== 'deferred_tools_delta') continue
|
if (msg.attachment.type !== 'deferred_tools_delta') continue
|
||||||
dtdCount++
|
dtdCount++
|
||||||
for (const n of msg.attachment.addedNames) announced.add(n)
|
for (const n of (msg.attachment as any).addedNames) announced.add(n)
|
||||||
for (const n of msg.attachment.removedNames) announced.delete(n)
|
for (const n of (msg.attachment as any).removedNames) announced.delete(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
const deferred: Tool[] = tools.filter(isDeferredTool)
|
const deferred: Tool[] = tools.filter(isDeferredTool)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user