From 54abb593e3d5f7cb753aef23552090f07ef18aab Mon Sep 17 00:00:00 2001 From: qer Date: Thu, 23 Apr 2026 21:57:39 +0800 Subject: [PATCH] fix(shell): show active agent task count in prompt status bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing `⚙ bash: N` badge only counted background Shell tasks and filtered out background Agent subagents, so when many subagents were running the prompt looked idle and users could not tell work was in progress. The toolbar now renders `⚙ bash: N` and `⚙ agent: N` as two independent badges (each hidden when its count is 0) and drops the agent badge first when the terminal is too narrow to fit both. --- CHANGELOG.md | 2 ++ docs/en/release-notes/changelog.md | 2 ++ docs/zh/release-notes/changelog.md | 2 ++ src/kimi_cli/ui/shell/__init__.py | 17 ++++++----- src/kimi_cli/ui/shell/prompt.py | 31 +++++++++++++------ tests/ui_and_conv/test_prompt_tips.py | 43 ++++++++++++++++++++++++++- 6 files changed, 80 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7896cfc84..8084d656da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ Only write entries that are worth mentioning to users. ## Unreleased +- Shell: Show active background agent task count in the prompt status bar — the existing `⚙ bash: N` badge only counted background Shell tasks and filtered out background Agent subagents, so when many subagents were running the prompt looked idle and users could not tell work was in progress; the toolbar now renders `⚙ bash: N` and `⚙ agent: N` as two independent badges (each hidden when its count is 0) and drops the agent badge first when the terminal is too narrow to fit both + ## 1.39.0 (2026-04-24) - Skill: Fix project-scope skills being ignored and user-scope skills silently winning name conflicts — the system prompt now groups discovered skills under `### Project` / `### User` / `### Extra` / `### Built-in` headings so the model can tell where each skill came from, and when the same name exists in multiple scopes the more specific scope wins (Project > User > Extra > Built-in) so a project's own `.kimi/skills/foo` or `.claude/skills/foo` correctly overrides a user-level or bundled `foo` instead of the other way around diff --git a/docs/en/release-notes/changelog.md b/docs/en/release-notes/changelog.md index 1085b17d6d..64447d621c 100644 --- a/docs/en/release-notes/changelog.md +++ b/docs/en/release-notes/changelog.md @@ -4,6 +4,8 @@ This page documents the changes in each Kimi Code CLI release. ## Unreleased +- Shell: Show active background agent task count in the prompt status bar — the existing `⚙ bash: N` badge only counted background Shell tasks and filtered out background Agent subagents, so when many subagents were running the prompt looked idle and users could not tell work was in progress; the toolbar now renders `⚙ bash: N` and `⚙ agent: N` as two independent badges (each hidden when its count is 0) and drops the agent badge first when the terminal is too narrow to fit both + ## 1.39.0 (2026-04-24) - Skill: Fix project-scope skills being ignored and user-scope skills silently winning name conflicts — the system prompt now groups discovered skills under `### Project` / `### User` / `### Extra` / `### Built-in` headings so the model can tell where each skill came from, and when the same name exists in multiple scopes the more specific scope wins (Project > User > Extra > Built-in) so a project's own `.kimi/skills/foo` or `.claude/skills/foo` correctly overrides a user-level or bundled `foo` instead of the other way around diff --git a/docs/zh/release-notes/changelog.md b/docs/zh/release-notes/changelog.md index ff61d0790d..8f03ac4e62 100644 --- a/docs/zh/release-notes/changelog.md +++ b/docs/zh/release-notes/changelog.md @@ -4,6 +4,8 @@ ## 未发布 +- Shell:在提示框状态栏显示当前正在运行的后台 Agent 任务数——原有的 `⚙ bash: N` 徽章只统计后台 Shell 任务,把后台 Agent 子代理过滤掉了,所以多个子代理同时在跑时提示框看起来像空闲,用户无法判断工作是否还在进行;现在状态栏会渲染 `⚙ bash: N` 与 `⚙ agent: N` 两个相互独立的徽章(任一计数为 0 时自动隐藏),终端太窄无法同时容纳两者时优先丢弃 agent 徽章 + ## 1.39.0 (2026-04-24) - Skill:修复项目级 Skill 被忽略、用户级 Skill 在同名冲突时静默获胜的问题——系统提示现在会把发现到的 Skill 按 `### Project` / `### User` / `### Extra` / `### Built-in` 四个分组呈现,让模型能分辨出每个 Skill 来自哪一层;当同一 Skill 名称同时存在于多个作用域时,越具体的作用域优先(Project > User > Extra > Built-in),项目自身的 `.kimi/skills/foo` 或 `.claude/skills/foo` 现在能正确覆盖用户级或内置的同名 `foo`,而不是被它们覆盖 diff --git a/src/kimi_cli/ui/shell/__init__.py b/src/kimi_cli/ui/shell/__init__.py index d88cb97b0f..865b03e217 100644 --- a/src/kimi_cli/ui/shell/__init__.py +++ b/src/kimi_cli/ui/shell/__init__.py @@ -33,6 +33,7 @@ from kimi_cli.ui.shell.echo import render_user_echo_text from kimi_cli.ui.shell.mcp_status import render_mcp_prompt from kimi_cli.ui.shell.prompt import ( + BgTaskCounts, CustomPromptSession, CwdLostError, PromptMode, @@ -416,26 +417,28 @@ def _mcp_status_loading() -> bool: @dataclass class _BgCountCache: time: float = 0.0 - count: int = 0 + counts: BgTaskCounts = BgTaskCounts() _bg_cache = _BgCountCache() - def _bg_task_count() -> int: + def _bg_task_counts() -> BgTaskCounts: if not isinstance(self.soul, KimiSoul): - return 0 + return BgTaskCounts() now = time.monotonic() if now - _bg_cache.time < 1.0: - return _bg_cache.count + return _bg_cache.counts views = list_task_views(self.soul.runtime.background_tasks, active_only=True) - _bg_cache.count = sum(1 for v in views if v.spec.kind == "bash") + bash_n = sum(1 for v in views if v.spec.kind == "bash") + agent_n = sum(1 for v in views if v.spec.kind == "agent") + _bg_cache.counts = BgTaskCounts(bash=bash_n, agent=agent_n) _bg_cache.time = now - return _bg_cache.count + return _bg_cache.counts with CustomPromptSession( status_provider=lambda: self.soul.status, status_block_provider=_mcp_status_block, fast_refresh_provider=_mcp_status_loading, - background_task_count_provider=_bg_task_count, + background_task_count_provider=_bg_task_counts, model_capabilities=self.soul.model_capabilities or set(), model_name=model_display_name( self.soul.model_name, diff --git a/src/kimi_cli/ui/shell/prompt.py b/src/kimi_cli/ui/shell/prompt.py index 5a192b93f1..5cb2621b7b 100644 --- a/src/kimi_cli/ui/shell/prompt.py +++ b/src/kimi_cli/ui/shell/prompt.py @@ -1094,6 +1094,12 @@ def should_handle_running_prompt_key(self, key: str) -> bool: ... def handle_running_prompt_key(self, key: str, event: KeyPressEvent) -> None: ... +@dataclass(frozen=True, slots=True) +class BgTaskCounts: + bash: int = 0 + agent: int = 0 + + @runtime_checkable class AgentStatusProvider(Protocol): """Optional protocol for delegates that render always-visible agent status. @@ -1170,7 +1176,7 @@ def __init__( status_provider: Callable[[], StatusSnapshot], status_block_provider: Callable[[int], AnyFormattedText | None] | None = None, fast_refresh_provider: Callable[[], bool] | None = None, - background_task_count_provider: Callable[[], int] | None = None, + background_task_count_provider: Callable[[], BgTaskCounts] | None = None, model_capabilities: set[ModelCapability], model_name: str | None, thinking: bool, @@ -2149,16 +2155,23 @@ def _render_bottom_toolbar(self) -> FormattedText: fragments.extend([(tc.cwd, cwd_text), ("", " ")]) remaining -= cwd_w + 2 - # Active background bash task count - bg_count = ( - self._background_task_count_provider() if self._background_task_count_provider else 0 + # Active background task counts (bash + agent, each rendered as its own + # badge). Order matters: bash renders first; if there isn't room for the + # agent badge too, drop agent and keep bash. + bg_counts = ( + self._background_task_count_provider() + if self._background_task_count_provider + else BgTaskCounts() ) - if bg_count > 0: - bg_text = f"⚙ bash: {bg_count}" + for kind_label, kind_count in (("bash", bg_counts.bash), ("agent", bg_counts.agent)): + if kind_count <= 0: + continue + bg_text = f"⚙ {kind_label}: {kind_count}" bg_width = _display_width(bg_text) - if remaining >= bg_width + 2: - fragments.extend([(tc.bg_tasks, bg_text), ("", " ")]) - remaining -= bg_width + 2 + if remaining < bg_width + 2: + break + fragments.extend([(tc.bg_tasks, bg_text), ("", " ")]) + remaining -= bg_width + 2 # Tips fill remaining space on line 1 tip_text = self._get_two_rotating_tips() diff --git a/tests/ui_and_conv/test_prompt_tips.py b/tests/ui_and_conv/test_prompt_tips.py index 65a2434053..fc322804b7 100644 --- a/tests/ui_and_conv/test_prompt_tips.py +++ b/tests/ui_and_conv/test_prompt_tips.py @@ -14,6 +14,7 @@ from kimi_cli.ui.shell.prompt import ( _GIT_STATUS_TTL, PROMPT_SYMBOL, + BgTaskCounts, CustomPromptSession, PromptMode, UserInput, @@ -332,7 +333,7 @@ def test_bottom_toolbar_narrow_terminal_with_full_decoration(width: int, monkeyp model_name="kimi-latest", tips=["ctrl-x: toggle mode"], ) - prompt_session._background_task_count_provider = lambda: 2 + prompt_session._background_task_count_provider = lambda: BgTaskCounts(bash=2, agent=0) lines = _render_toolbar_lines( prompt_session, @@ -352,6 +353,46 @@ def test_bottom_toolbar_narrow_terminal_with_full_decoration(width: int, monkeyp ) +def test_bottom_toolbar_shows_bash_and_agent_badges_together(monkeypatch: Any) -> None: + prompt_session = _make_toolbar_session(tips=[]) + prompt_session._background_task_count_provider = lambda: BgTaskCounts(bash=3, agent=1) + + lines = _render_toolbar_lines(prompt_session, 120, monkeypatch) + + assert "⚙ bash: 3" in lines[1], f"bash badge missing: {lines[1]!r}" + assert "⚙ agent: 1" in lines[1], f"agent badge missing: {lines[1]!r}" + assert lines[1].index("⚙ bash: 3") < lines[1].index("⚙ agent: 1"), ( + f"bash badge must come before agent badge: {lines[1]!r}" + ) + + +def test_bottom_toolbar_shows_agent_badge_alone_when_no_bash(monkeypatch: Any) -> None: + prompt_session = _make_toolbar_session(tips=[]) + prompt_session._background_task_count_provider = lambda: BgTaskCounts(bash=0, agent=2) + + lines = _render_toolbar_lines(prompt_session, 120, monkeypatch) + + assert "⚙ bash" not in lines[1], f"bash badge must not appear when count is 0: {lines[1]!r}" + assert "⚙ agent: 2" in lines[1], f"agent badge missing: {lines[1]!r}" + + +def test_bottom_toolbar_drops_agent_badge_before_bash_when_narrow(monkeypatch: Any) -> None: + # With only ~width budget for one badge after CWD/mode, keeping bash and + # dropping agent is the documented priority. + prompt_session = _make_toolbar_session(tips=[]) + prompt_session._background_task_count_provider = lambda: BgTaskCounts(bash=5, agent=5) + + lines = _render_toolbar_lines(prompt_session, 40, monkeypatch) + + # Must never overflow and the bash badge is preferred over the agent badge. + assert _display_width(lines[1]) <= 40 + if "⚙ agent" in lines[1]: + # Only acceptable if bash also fit — otherwise priority is violated. + assert "⚙ bash" in lines[1], ( + f"agent badge appeared without bash badge at narrow width: {lines[1]!r}" + ) + + def test_mode_shows_full_with_model_name_on_wide_terminal(monkeypatch: Any) -> None: """On a wide terminal the full mode string (with model name and thinking dot) is shown.""" session = _make_toolbar_session(model_name="fast-model")