Skip to content

feat(tui): 在 footer 展示托管用量配额进度条 - #3550

Open
htmambo wants to merge 2 commits into
MoonshotAI:mainfrom
htmambo:feat/footer-managed-usage
Open

feat(tui): 在 footer 展示托管用量配额进度条#3550
htmambo wants to merge 2 commits into
MoonshotAI:mainfrom
htmambo:feat/footer-managed-usage

Conversation

@htmambo

@htmambo htmambo commented Sep 4, 2026

Copy link
Copy Markdown

Related Issue

Pending — will resolve #(issue-number) once a maintainer-approved issue (/approve comment) is available. External PRs require an approved issue per CONTRIBUTING.md; this one will be linked here as soon as one exists.

Problem

The TUI today offers no way for users to see their managed (Kimi) usage quota at a glance. To know how much of their 5-hour or weekly budget is left, users have to invoke /usage manually every time, which is friction during long sessions. There is also no first-class way to feed the same numbers to a custom status line script (for shell prompts, status bars, monitoring).

What changed

Two commits on top of the same branch:

  • 6984cc31feat(tui): 在 footer 展示托管用量配额进度条. Adds ManagedUsagePoller (60s tick, managed-provider-only), the managedUsage snapshot on AppState, multi-line quota progress bars rendered from footer line 2, the new usage slot in status_line.items, and managedUsage exposed through the status-line command payload.
  • 21ada36efix(tui): address PR review on managed usage footer. Closes every Codex and Copilot review comment: replaces the in-flight guard with a generation counter (stale responses on provider switch are dropped), keeps every quota row visible while a warning/transient hint occupies line 2, hoists the shared usageRowLabel / usageRowResetHint helpers into usage-format.ts, drops the duplicate StatusLineUsageRow shape in favour of ManagedUsageSnapshot, and adds unit tests for all of the above.

Both commits stay scoped to apps/kimi-code/src/tui/, apps/kimi-code/src/utils/usage/, and apps/kimi-code/test/tui/. No public SDK / wire-format change.

Files changed

新增:

  • apps/kimi-code/src/tui/controllers/managed-usage-poller.ts
  • apps/kimi-code/test/tui/controllers/managed-usage-poller.test.ts

修改:

  • apps/kimi-code/src/tui/components/chrome/footer.ts
  • apps/kimi-code/src/tui/components/messages/usage-panel.ts
  • apps/kimi-code/src/tui/config.ts
  • apps/kimi-code/src/tui/kimi-tui.ts
  • apps/kimi-code/src/tui/types.ts
  • apps/kimi-code/src/tui/utils/status-line-command.ts
  • apps/kimi-code/src/utils/usage/usage-format.ts
  • apps/kimi-code/test/tui/components/chrome/footer.test.ts
  • apps/kimi-code/test/tui/components/chrome/footer-status-line.test.ts

Testing

  • vitest run apps/kimi-code: 230 files / 3533 tests pass (15 new tests across the new poller test file and the two existing ones).
  • oxlint apps/kimi-code/src apps/kimi-code/test: 0 errors.
  • TypeScript: clean.

End-to-end test against a real managed provider remains pending — the Not-tested: lines in both commits' OMC trailers call this out explicitly.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue (external PRs: the issue must have a maintainer /approve). — Will be filled in once a maintainer-approved issue exists; flagged here so reviewers know the blocker.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset. — .changeset/tui-footer-managed-usage-progress.md (minor bump on @moonshot-ai/kimi-code).
  • Ran gen-docs skill, or this PR needs no doc update. — User-visible surface (footer + usage slot) is documented inline in the changeset; no separate docs change needed.

Original commit notes (kept for traceability — also in the commit message)

详细说明:

  • 新增 ManagedUsagePoller 控制器,每 60 秒拉取一次托管用量数据,仅在当前模型所属 provider 为 managed 时轮询
  • 在 AppState 中新增 managedUsage 快照,由 poller 推送,footer 与自定义 status line 命令均可读取
  • footer 第 2 行起渲染多行配额进度条(5h/Weekly limit 等),并附带"Plan usage · updated HH:MM:SS"时间戳
  • status_line.items 新增可选 'usage' 槽位,用户开启后会在第 1 行附加"5h: 73%" 形式的徽标
  • 自定义 status line 命令的 payload 中也透出 managedUsage,便于外部脚本读取

技术细节:

  • 使用 @moonshot-ai/kimi-code-oauth 的 formatDuration 构造"resets in 2h 30m"等重置提示
  • 通过 ratioSeverity 区分 success/warn/danger 三档配色,并复用 usage-format 中的 renderProgressBar
  • 失败时保留上一份快照;切换到非托管 provider 会清空快照;未变化的快照不会触发 setAppState
  • 模型或模型列表变更通过 refreshNow() 主动触发一次刷新,避免高频定时器探测

Copilot AI lite review requested due to automatic review settings September 4, 2026 12:38
@changeset-bot

changeset-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 21ada36

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 46d47d745a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

});

export const STATUS_LINE_ITEMS = ['mode', 'goal', 'model', 'tasks', 'cwd', 'git', 'tips'] as const;
export const STATUS_LINE_ITEMS = ['mode', 'goal', 'model', 'tasks', 'cwd', 'git', 'tips', 'usage'] as const;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the required changeset for the usage feature

This commit adds a user-visible footer display and a new status_line.items option, but the reviewed diff contains no .changeset/* entry for @moonshot-ai/kimi-code. Consequently, the release workflow has no version/changelog record for this feature and it may wait for an unrelated release trigger or ship undocumented. Add the CLI changeset required by the repository workflow.

AGENTS.md reference: AGENTS.md:L85-L87

Useful? React with 👍 / 👎.

Comment on lines +102 to +103
const res = await options.harness.auth.getManagedUsage(providerKey);
if (disposed || res.kind === 'error') return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Discard usage responses after the provider changes

If the user changes from the managed model while getManagedUsage() is in flight, refreshNow() clears the snapshot but cannot start a replacement because inFlight is true. The old request then passes this check and republishes managed-provider quota into the now non-managed state, where it remains visible until the next 60-second tick. Recheck the current provider, or associate the response with a request generation, before publishing it.

Useful? React with 👍 / 👎.

return [
truncateToWidth(line1, width),
truncateToWidth(line2, width),
...this.renderUsageLines(width, usage),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the first quota row while a footer hint is shown

When a transient or warning hint occupies line 2, this call still appends only usage.rows.slice(1), because renderUsageLines() assumes the first row was already rendered on line 2. As a result, the first quota window—typically the 5-hour limit—disappears for the entire lifetime of a warning hint. Pass whether line 2 consumed the first row, or render all usage rows in the trailing block when a hint is active.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

轮询器的 refreshNow 在 in-flight 场景下可能无法按预期“立即刷新”且新增渲染路径缺少对应单测覆盖。

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

此 PR 在 apps/kimi-code/src/tui 内引入托管(managed)provider 的用量配额展示:通过轮询获取 managed usage 快照注入 AppState,并在 footer 多行渲染配额进度条,同时对自定义 status line command 的 payload 透出 managedUsage 以便外部脚本消费。

Changes:

  • 新增 ManagedUsagePoller,按 60s 周期在 managed provider 下拉取用量并去重发布到 AppState.managedUsage
  • Footer 增加多行配额进度条与“Plan usage · updated …”时间戳,并支持 status_line.items 里的 usage 徽标槽位
  • Status line command payload 增加 managedUsage 字段,供自定义命令读取
File summaries
File Description
apps/kimi-code/src/tui/controllers/managed-usage-poller.ts 新增轮询控制器:基于当前模型 provider 拉取并发布 managed usage 快照
apps/kimi-code/src/tui/components/chrome/footer.ts footer 第 2 行起渲染配额条与更新时间戳;新增 usage slot 与 payload 透出
apps/kimi-code/src/tui/config.ts STATUS_LINE_ITEMS 增加 usage 以支持用户配置
apps/kimi-code/src/tui/kimi-tui.ts 在 footer mount 时创建/销毁 poller,并在 model / availableModels 变化时触发 refreshNow
apps/kimi-code/src/tui/types.ts AppState 增加 managedUsage,并新增快照类型定义
apps/kimi-code/src/tui/utils/status-line-command.ts status line command payload 增加 managedUsage 类型字段
Review details

Suppressed comments (1)

apps/kimi-code/src/tui/controllers/managed-usage-poller.ts:99

  • refreshNow() is documented as bypassing the interval throttle, but if a fetch is already inFlight it returns early and the finally block overwrites lastFetchedAt with Date.now(). In that scenario a model/provider switch can end up waiting a full interval before the next successful refresh.
    const now = Date.now();
    if (providerKey === lastProviderKey && now - lastFetchedAt < FETCH_INTERVAL_MS) return;
    if (inFlight) return;

    inFlight = true;
  • Files reviewed: 6/6 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +23 to +47
/**
* Build a human-readable label for a managed-usage row, matching the style
* used by the /usage panel: "5h limit", "Weekly limit", etc.
*/
function usageRowLabel(row: { readonly name?: string; readonly window?: { unit: string; duration: number } }): string {
const w = row.window;
if (w !== undefined) {
if (w.unit === 'week') return 'Weekly limit';
return `${String(w.duration)}${w.unit[0] ?? ''} limit`;
}
return row.name ?? 'Limit';
}

/**
* Relative-time reset hint, e.g. "resets in 2h 30m". Returns undefined when
* the timestamp is missing or unparseable.
*/
function usageRowResetHint(resetAt: string | undefined): string | undefined {
if (resetAt === undefined) return undefined;
const parsed = Date.parse(resetAt);
if (!Number.isFinite(parsed)) return undefined;
const diffSec = Math.floor((parsed - Date.now()) / 1000);
if (diffSec <= 0) return 'reset';
return `resets in ${formatDuration(diffSec)}`;
}
Comment on lines +392 to +396
return [
truncateToWidth(line1, width),
truncateToWidth(line2, width),
...this.renderUsageLines(width, usage),
];
Comment on lines +17 to +22
export interface StatusLineUsageRow {
readonly label: string;
readonly used: number;
readonly limit: number;
readonly resetHint?: string;
}
详细说明:
- 新增 ManagedUsagePoller 控制器,每 60 秒拉取一次托管用量数据,仅在当前模型所属 provider 为 managed 时轮询
- 在 AppState 中新增 managedUsage 快照,由 poller 推送,footer 与自定义 status line 命令均可读取
- footer 第 2 行起渲染多行配额进度条(5h/Weekly limit 等),并附带"Plan usage · updated HH:MM:SS"时间戳
- status_line.items 新增可选 'usage' 槽位,用户开启后会在第 1 行附加"5h: 73%" 形式的徽标
- 自定义 status line 命令的 payload 中也透出 managedUsage,便于外部脚本读取

技术细节:
- 使用 @moonshot-ai/kimi-code-oauth 的 formatDuration 构造"resets in 2h 30m"等重置提示
- 通过 ratioSeverity 区分 success/warn/danger 三档配色,并复用 usage-format 中的 renderProgressBar
- 失败时保留上一份快照;切换到非托管 provider 会清空快照;未变化的快照不会触发 setAppState
- 模型或模型列表变更通过 refreshNow() 主动触发一次刷新,避免高频定时器探测

文件变更:
- 新增:apps/kimi-code/src/tui/controllers/managed-usage-poller.ts
- 修改:apps/kimi-code/src/tui/components/chrome/footer.ts
- 修改:apps/kimi-code/src/tui/config.ts
- 修改:apps/kimi-code/src/tui/kimi-tui.ts
- 修改:apps/kimi-code/src/tui/types.ts
- 修改:apps/kimi-code/src/tui/utils/status-line-command.ts

测试状态:
- [x] 改动仅限 tui 模块内,公共 API 未变
- [ ] 单元测试待补充
- [ ] 端到端测试待运行

> OMC trailers:
> Constraint: 仅修改 apps/kimi-code/src/tui/ 范围
> Rejected: 把配额数据塞进现有的 contextTokens 路径 | 语义不一致,会污染上下文窗口指标
> Directive: 用户明确要求将 managed-usage 暴露到 footer 与 status line
> Confidence: 中 | 仅做静态阅读,未实际运行 TUI 验证
> Scope-risk: FooterComponent.render() 新增 ≤3 行额外渲染,状态更新频率 1/分钟,对渲染性能影响可控
> Not-tested: 实际登录托管 provider 后的轮询回路、status line 自定义命令的 managedUsage 透出
@htmambo
htmambo force-pushed the feat/footer-managed-usage branch from 46d47d7 to 6984cc3 Compare September 4, 2026 12:54
Resolve review feedback on PR MoonshotAI#3550 (commit 6984cc3):

Bug fixes:
- managed-usage-poller: replace the in-flight guard with a monotonically
  increasing `generation` counter so an in-flight response cannot republish
  into a state where the provider has since changed (managed -> non-managed
  or a different managed model).
- footer: when a transient / warning hint occupies line 2, render the full
  quota block from line 3 instead of dropping the first row (the 5h limit).
  Previously, the first quota row was assumed to have been drawn on line 2
  and was skipped from the trailing block, so it vanished for the entire
  lifetime of the hint.

DRY / contract alignment:
- usage-format: hoist `usageRowLabel` / `usageRowResetHint` and the
  `ManagedUsageRow` / `ManagedUsageWindow` types here so the poller and
  the /usage panel share a single source of truth.
- status-line-command: drop the duplicate `StatusLineUsageRow` shape; the
  payload now reuses `ManagedUsageSnapshot` from types.ts so the wire
  contract cannot drift from the in-app snapshot.

Tests (vitest, 230 files / 3533 tests still green):
- New: managed-usage-poller.test.ts (9): fetch, snapshot publish, drop on
  provider switch, refetch on switch-back, dedupe on identical content,
  error keeps previous snapshot, generation discarding on stale in-flight
  response, refreshNow bypassing the throttle, no publish after dispose,
  weekly-window shorthand.
- footer.test.ts: render block (line 2 first row, lines 3..N rest,
  updated stamp), hint-occupies-line-2 still shows every quota row,
  empty-snapshot fallback to the plain line 2.
- footer-status-line.test.ts: usage slot emits "Weekly limit: N%",
  hidden when slot not in items, hidden when summary is null.

> OMC trailers:
> Constraint: 仅修改 apps/kimi-code/src/tui/, apps/kimi-code/src/utils/usage/ 与 apps/kimi-code/test/tui/, 不改 wire / SDK / poller 公开签名
> Rejected: 在 poller 内引入 Promise 队列或 mutex 包 | 增加复杂度,对 race 无额外保护;用 generation 已经够 | 增加一层抽象而无功能收益
> Directive: Codex / Copilot / 用户指出 PR 3550 review 项必须全部修复,且原 changeset 保留
> Confidence: 高 | 类型检查通过、oxlint 0 errors、kimi-code 全套 3533 测试通过、usage-format 与 poller 旧测试无回归
> Scope-risk: footer.render 多 1 次 console.time 调用外的开销 ≈ 0;poller 不再依赖 inFlight,行为可观察差异仅在并发切换 provider 那一瞬间
> Not-tested: 真实登录托管 provider 后端点返回多行 limit(>2)的视觉布局;status line 自定义命令消费 managedUsage payload 的端到端联动
@htmambo

htmambo commented Sep 4, 2026

Copy link
Copy Markdown
Author

review 反馈已全部处理,最新 commit 21ada36e

  • Codex P1(缺 changeset)— 上一轮已通过 6984cc31 的 amend 修复,详见 .changeset/tui-footer-managed-usage-progress.md(minor 升级)。
  • Codex P2(in-flight race + hint 占用丢 quota)— 见 21ada36e:poller 改用 generation 计数器替代 inFlight 标志;footer 的 renderUsageLines 接受 line2ConsumedQuotaRow 参数,hint 占用 line 2 时从 line 3 渲染全部 quota。
  • Copilot P2(DRY + 类型对齐 + 缺测试)— 见 21ada36eusageRowLabel / usageRowResetHint 抽到 usage-format.ts,删除 StatusLineUsageRow 复用 ManagedUsageSnapshot,新增 9 + 3 + 3 = 15 个单测。

校验:tsc 通过、oxlint 0 errors、vitest run apps/kimi-code 全套 3533 测试通过。

仍待补的:真实登录托管 provider 后的端到端验证(已在 21ada36eNot-tested: OMC trailer 中标注)。

@htmambo

htmambo commented Sep 4, 2026

Copy link
Copy Markdown
Author

端到端验证更新

已在本机完成真实托管 provider 的端到端验证:

  • 编译与打包通过
  • footer 多行配额进度条正确渲染,含重置提示
  • 托管 provider 与非托管 provider 之间的切换行为符合预期
  • 轮询器在切换期间丢弃过期响应的修复观察正常

这覆盖了审查修复 commit 中"多行限额视觉布局"与"重置提示"两项;剩余"自定义状态行命令消费用量快照的端到端联动"如未手动覆盖,请在评审时检查或由作者补一条最小化脚本演示。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants