Files
claude-code/docs/features/builtin-statusline-disconnected-analysis.md
unraid 95fece4b51 feat: 整合功能恢复与技能学习闭环(含 ECC v2.1 parity + Opus 4.7 接入 + prompt 工程优化)
主要变更:
- Skill Learning 闭环系统 (9/9 AC)
- Opus 4.7 模型层接入 + adaptive thinking
- Prompt 工程优化 (64 审计测试)
- Agent Teams 简化门控 (默认启用)
- Windows Terminal 后端修复 (EncodedCommand/WT_SESSION)
- TF-IDF 技能搜索精准化 (字段加权/CJK 优化)
- Autonomy 系统 (/autonomy 命令)
- ACP 协议完整实现
- mock.module 泄漏修复 (CI 全绿)
- 152+ lint/type 修复
2026-04-22 16:07:42 +08:00

293 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# BuiltinStatusLine 断连分析报告
## 概述
内置额度状态行组件 `BuiltinStatusLine` 在当前分支 `chore/lint-cleanup` 上不显示。该组件能够直接在终端底部渲染模型名称、Context 用量百分比、速率限制 bucket 进度条、余额Balance和累计花费Cost无需任何外部脚本配置。
当前状态:**组件已升级到新的 `providerUsage` 类型系统,但未被接入渲染树,处于孤岛状态。**
---
## 时间线
### 1. PR #89 (commit `913702d9`) — 功能正常
- 创建 `BuiltinStatusLine.tsx` 组件
- `StatusLine.tsx``import { BuiltinStatusLine }` 并在 `StatusLineInner` 中直接渲染 `<BuiltinStatusLine />`
- `statusLineShouldDisplay()` 返回 `return true`(无条件显示)
- 文件数:仅修改 `BuiltinStatusLine.tsx` + `StatusLine.tsx`
### 2. commit `5b1a52b8`"更新大量 tsx 原始文件")— 上游覆盖
- 合入上游 Anthropic 官方代码,`StatusLine.tsx` 被完整替换为外部命令版本
- `import { BuiltinStatusLine }` 被移除
- `statusLineShouldDisplay()` 变为 `return settings?.statusLine !== undefined`
- `StatusLineInner` 变为调用 `executeStatusLineCommand()` 的外部脚本执行逻辑
- `BuiltinStatusLine.tsx` 文件保留,但无人引用
### 3. commit `7b9287b1`(当前分支 `chore/lint-cleanup`)— 升级组件但未恢复接线
- 升级 `BuiltinStatusLine.tsx` 的 props 接口:`rateLimits: { five_hour?, seven_day? }``buckets: ProviderUsageBucket[]` + `balance?: ProviderBalance`
- 新建完整的 `providerUsage` 服务层11 个文件,+704 行)
- **未修改 `StatusLine.tsx`**git diff main...HEAD 为空)
- 结果:组件升级完成,数据源就绪,但渲染入口仍然缺失
---
## 当前状态对比
### StatusLine.tsx当前 — 外部命令版本)
**文件**: `src/components/StatusLine.tsx`
**`statusLineShouldDisplay` (行 59-64):**
```typescript
export function statusLineShouldDisplay(settings: ReadonlySettings): boolean {
if (feature('KAIROS') && getKairosActive()) return false
return settings?.statusLine !== undefined // ← 需要 settings 配置
}
```
**`StatusLineInner` 渲染逻辑 (行 273-278):**
```typescript
const text = await executeStatusLineCommand( // ← 调用外部 shell 命令
statusInput,
controller.signal,
undefined,
logResult,
)
```
**渲染输出 (行 397-407):**
```tsx
<Box paddingX={paddingX} gap={2}>
{statusLineText ? (
<Text dimColor wrap="truncate">
<Ansi>{statusLineText}</Ansi> // ← 渲染外部命令的 stdout
</Text>
) : isFullscreenEnvEnabled() ? (
<Text> </Text>
) : null}
</Box>
```
**关键依赖**: 需要 `~/.claude/settings.json` 中配置 `statusLine: { type: "command", command: "..." }`
### StatusLine.tsxPR #89 — 内置版本,能正常工作)
**`statusLineShouldDisplay` (行 17-20):**
```typescript
export function statusLineShouldDisplay(settings: ReadonlySettings): boolean {
if (feature('KAIROS') && getKairosActive()) return false;
return true; // ← 无条件显示
}
```
**import (行 15):**
```typescript
import { BuiltinStatusLine } from './BuiltinStatusLine.js';
```
**`StatusLineInner` 渲染 (行 50-58):**
```tsx
return (
<BuiltinStatusLine
modelName={modelDisplay}
contextUsedPct={contextPercentages.used}
usedTokens={usedTokens}
contextWindowSize={contextWindowSize}
totalCostUsd={totalCost}
rateLimits={rawUtil}
/>
);
```
### BuiltinStatusLine.tsx当前 — 已升级但未接入)
**文件**: `src/components/BuiltinStatusLine.tsx`
**Props 接口 (行 8-16):**
```typescript
type BuiltinStatusLineProps = {
modelName: string;
contextUsedPct: number;
usedTokens: number;
contextWindowSize: number;
totalCostUsd: number;
buckets: ProviderUsageBucket[]; // ← 新接口(原为 rateLimits
balance?: ProviderBalance; // ← 新增
};
```
**渲染内容 (行 80-131):**
- 行 82: 模型名称
- 行 84-87: Context 用量百分比 + token 计数
- 行 89-112: buckets 循环渲染(进度条 + 百分比 + 重置倒计时)
- 行 114-120: Balance 余额显示
- 行 124-129: Cost 花费显示
**导出 (行 134):**
```typescript
export const BuiltinStatusLine = React.memo(BuiltinStatusLineInner);
```
**被引用情况**: 无任何文件 import 此组件grep `import.*BuiltinStatusLine` 返回 0 结果)
---
## 断连的精确位置
### 断点 1: `statusLineShouldDisplay` 条件变化
| 版本 | 代码 | 行为 |
|------|------|------|
| PR #89 (`913702d9`) | `return true` | 无条件显示 |
| 当前 (`StatusLine.tsx:63`) | `return settings?.statusLine !== undefined` | 需要 settings.json 中配置 `statusLine` 字段 |
**文件**: `src/components/StatusLine.tsx` 行 63
### 断点 2: `BuiltinStatusLine` import 被移除
| 版本 | 代码 |
|------|------|
| PR #89 行 15 | `import { BuiltinStatusLine } from './BuiltinStatusLine.js';` |
| 当前 | 无此 import`StatusLine.tsx` 全文不含 `BuiltinStatusLine` |
**文件**: `src/components/StatusLine.tsx`(缺失 import
### 断点 3: 渲染逻辑被替换
| 版本 | 渲染方式 |
|------|---------|
| PR #89 行 50-58 | `<BuiltinStatusLine modelName={...} contextUsedPct={...} ... />` |
| 当前行 273-278 | `executeStatusLineCommand(statusInput, controller.signal, ...)` |
**文件**: `src/components/StatusLine.tsx` 行 273当前vs PR #89 行 50
### 调用链(当前)
```
PromptInputFooter.tsx:165
└─ statusLineShouldDisplay(settings) → settings?.statusLine !== undefined → false无配置
└─ <StatusLine /> 不渲染
└─ BuiltinStatusLine 永远不可见
```
### 调用链PR #89正常工作
```
PromptInputFooter.tsx:165
└─ statusLineShouldDisplay(settings) → true
└─ <StatusLine />
└─ <BuiltinStatusLine modelName={...} buckets={...} balance={...} />
└─ 直接渲染额度信息
```
---
## 数据源状态(已就绪)
当前分支在 commit `7b9287b1` 中新建了完整的 `providerUsage` 服务层,作为 `BuiltinStatusLine` 的数据源:
| 文件 | 行数 | 功能 |
|------|------|------|
| `src/services/providerUsage/types.ts` (行 1-41) | 41 | `ProviderUsageBucket``ProviderBalance``ProviderUsage` 类型定义 |
| `src/services/providerUsage/store.ts` (行 1-69) | 69 | 单例 store`getProviderUsage()``updateProviderBuckets()``setProviderBalance()``subscribeProviderUsage()` |
| `src/services/providerUsage/adapters/anthropic.ts` | 40 | Anthropic 响应头解析 → buckets |
| `src/services/providerUsage/adapters/openai.ts` | 97 | OpenAI 响应头解析 → buckets |
| `src/services/providerUsage/adapters/bedrock.ts` | 38 | AWS Bedrock 适配器 |
| `src/services/providerUsage/balance/generic.ts` | 118 | 通用余额轮询器 |
| `src/services/providerUsage/balance/deepseek.ts` | 85 | DeepSeek 余额轮询 |
| `src/services/providerUsage/balance/poller.ts` | 78 | 余额轮询框架 |
| `src/services/providerUsage/balance/types.ts` | 9 | 余额轮询类型 |
| `src/services/providerUsage/__tests__/providerUsage.test.ts` | 120 | 单元测试 |
| `src/services/claudeAiLimits.ts` (行 15-16) | +12 | 新增 `anthropicAdapter` import + `updateProviderBuckets` 调用 |
**总计**: 11 文件,+704 行。数据从 API 响应头 → adapter 解析 → store 存储 → 可供 UI 消费的完整管道已就绪。
旧数据源 `getRawUtilization()``claudeAiLimits.ts:162`)仍然存在,返回 `{ five_hour?, seven_day? }` 格式,当前 `StatusLine.tsx:96` 仍在使用它构建 `buildStatusLineCommandInput``rate_limits` 字段。
---
## 修复方案
需要修改 **1 个文件**: `src/components/StatusLine.tsx`
### 修改 1: 恢复 `statusLineShouldDisplay` 为无条件显示(或 fallback 到内置)
**当前** (`StatusLine.tsx:59-64`):
```typescript
export function statusLineShouldDisplay(settings: ReadonlySettings): boolean {
if (feature('KAIROS') && getKairosActive()) return false
return settings?.statusLine !== undefined
}
```
**修复为**:
```typescript
export function statusLineShouldDisplay(settings: ReadonlySettings): boolean {
if (feature('KAIROS') && getKairosActive()) return false
return true // 内置 StatusLine 始终可用,不需要 settings 配置
}
```
### 修改 2: 恢复 `BuiltinStatusLine` import
`StatusLine.tsx` 顶部添加:
```typescript
import { BuiltinStatusLine } from './BuiltinStatusLine.js'
```
### 修改 3: 添加 providerUsage store 的数据连接
添加 import:
```typescript
import { getProviderUsage } from '../services/providerUsage/store.js'
```
### 修改 4: `StatusLineInner` 渲染逻辑 — 无外部命令时 fallback 到内置
`StatusLineInner` 中(约行 185-408`settings?.statusLine` 未配置时,直接渲染 `<BuiltinStatusLine />`,否则保留外部命令逻辑。
**推荐方案**: 将 `StatusLineInner` 改为双模式:
```typescript
function StatusLineInner({ messagesRef, lastAssistantMessageId, vimMode }: Props): React.ReactNode {
const settings = useSettings()
// 如果配置了外部命令,走外部命令渲染路径(保留现有逻辑)
if (settings?.statusLine) {
return <ExternalStatusLine messagesRef={messagesRef} lastAssistantMessageId={lastAssistantMessageId} vimMode={vimMode} />
}
// 否则使用内置 BuiltinStatusLine
return <BuiltinStatusLineWrapper messagesRef={messagesRef} lastAssistantMessageId={lastAssistantMessageId} />
}
```
其中 `BuiltinStatusLineWrapper` 需要:
-`useMainLoopModel()` 获取模型名
-`getCurrentUsage()` + `getContextWindowForModel()` 计算 context 百分比
-`getProviderUsage()` 获取 `buckets``balance`
-`getTotalCost()` 获取花费
- 传入 `<BuiltinStatusLine />` 的 props
---
## 相关文件索引
| 文件路径 | 角色 |
|---------|------|
| `src/components/BuiltinStatusLine.tsx` | 内置状态行组件(已升级,未接入) |
| `src/components/StatusLine.tsx` | 状态行入口(当前为外部命令版本,需修改) |
| `src/components/PromptInput/PromptInputFooter.tsx:28-30,165` | 渲染入口import StatusLine + 条件渲染) |
| `src/services/providerUsage/types.ts` | `ProviderUsageBucket``ProviderBalance` 类型定义 |
| `src/services/providerUsage/store.ts` | `getProviderUsage()` 数据存储 |
| `src/services/providerUsage/adapters/anthropic.ts` | Anthropic 响应头 → buckets 适配器 |
| `src/services/providerUsage/adapters/openai.ts` | OpenAI 响应头 → buckets 适配器 |
| `src/services/providerUsage/adapters/bedrock.ts` | Bedrock 适配器 |
| `src/services/providerUsage/balance/generic.ts` | 通用余额轮询 |
| `src/services/providerUsage/balance/deepseek.ts` | DeepSeek 余额轮询 |
| `src/services/providerUsage/balance/poller.ts` | 轮询框架 |
| `src/services/claudeAiLimits.ts:15-16,162-164` | `getRawUtilization()`(旧数据源)+ `updateProviderBuckets`(新数据管道) |