Skip to content

feat(tui): add Crons panel for scheduled cron jobs#119

Merged
yishuiliunian merged 1 commit intomainfrom
worktree-crispy-wobbling-pillow
Apr 17, 2026
Merged

feat(tui): add Crons panel for scheduled cron jobs#119
yishuiliunian merged 1 commit intomainfrom
worktree-crispy-wobbling-pillow

Conversation

@yishuiliunian
Copy link
Copy Markdown
Contributor

Summary

  • 新增 Crons Panel,与 Agents / Tasks / BgTasks 并列显示在 TUI 面板区末尾,展示 root agent 的 CronScheduler 状态(ID + prompt + 倒计时 next Xm Ys + recurring 标识 [R]
  • Bridge 每 2 秒轮询 scheduler,按 HashSet<(id, prompt, recurring, cron_expr-hash)> 做 diff-skip,TUI 每帧用 Utc::now() 重算倒计时 —— 消除冗余 IPC 事件
  • 同包重构:CronSchedulerMutex<Vec> 升级为 RwLock<Vec> + 两阶段锁;text_width.rs 统一 CJK 宽度计算修复跨 panel 老 bug;agent_handler / agent_setup 拆分 helper 以满足 200 行文件限制

Changes

Protocol: CronJobSnapshot(含 cron_expr + created_at_unix_ms, #[serde(default)])、CronsChanged 事件

Session: cron_state::apply 处理 CronsChanged + SessionResumedtask_state / bg_task_state 同步加 resume 清理;agent_event_helperslet-else unreachable!() 显式契约

Agent-server: cron_bridge(poll + diff-skip + emit);agent_setup_helpers(sub_agent_forwarder / initial_messages / feature_tags);AgentSetupResult.scheduler

Scheduler: RwLock<Vec<ScheduledTask>>tick::survey_tasks + mutate_tasks 两阶段;find_unique_id 可测化 ID 冲突重试

TUI: PanelKind::Crons + CronsPanelProvider(通过 registry 注册)+ views/crons_panel + cron_duration_format + text_width(替换 tasks/bg_tasks 的 chars().count()

ACP: CronsChanged 标为 non-translatable(与 TasksChanged 一致)

Test plan

  • CI passes (bazel test //..., Clippy, Rustfmt)
  • 手动验证:bazel build //:loopal && ./bazel-bin/loopal → 执行 CronCreate → 2 秒内 panel 显示该 cron,Tab 循环可定位 → CronDelete 后 2 秒内消失

Coverage

  • 新增/修改 17 个生产源码文件,新增 14 个测试文件(120+ 测试用例)
  • 覆盖范围:serde 往返 + legacy 兼容、diff-skip 不变式、bridge 错误路径、SessionResumed 清理、CJK 渲染、tick_loop send-err + inner-cancel、find_unique_id 冲突重试、Default::default 激活
  • 所有修改/新建 .rs 文件 ≤ 200 行(符合 CLAUDE.md)

Adds a fourth Panel (Crons) to the TUI panel zone alongside
Agents/Tasks/BgTasks, surfacing the root agent's CronScheduler state
through the existing PanelProvider architecture so users can observe
scheduled prompts without invoking CronList.

Data flow: CronScheduler → cron_bridge (2s poll with HashSet<identity>
diff-skip) → CronsChanged event → SessionState.cron_snapshots →
App cache → CronsPanelProvider. The bridge ignores next_fire and lets
the TUI recompute the countdown from Utc::now() each frame, avoiding
redundant IPC traffic.

Architecture improvements shipped together:
- CronScheduler switched from Mutex<Vec> to RwLock<Vec>; tick_loop
  uses two-phase locking (read-only survey → write-lock mutate only
  when tasks fire/expire) so bridge list() and tool add/remove can
  proceed in parallel with idle ticks.
- CronJobSnapshot carries cron_expr + created_at_unix_ms with
  #[serde(default)], maintaining forward-compatible IPC while leaving
  room for future UI expansion.
- New views/text_width.rs gives all panels a shared UnicodeWidthStr
  column-width helper, fixing CJK truncation bugs in tasks_panel and
  bg_tasks_panel at the same time.
- SessionResumed now clears cron/task/bg panel caches so resumed
  sessions don't display stale data before the bridges re-emit.
- agent_handler and agent_setup each split helpers into a sibling
  module to stay within the 200-line file budget.

Tests: 120+ new/updated unit and integration tests covering serde
round-trips, diff-skip invariants, bridge error paths, CJK rendering,
scheduler concurrency, and the ID-collision retry loop.
@yishuiliunian yishuiliunian merged commit 4673b18 into main Apr 17, 2026
4 checks passed
@yishuiliunian yishuiliunian deleted the worktree-crispy-wobbling-pillow branch April 17, 2026 08:54
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.

1 participant