mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-21 14:46:19 +00:00
* fix(web-shell): rename scheduled tasks "查看历史" to "查看对话" * feat(serve): bind cron_create durable tasks to dedicated sessions via keepalive The cron_create tool (core layer) writes durable tasks to disk without a sessionId because it has no access to the session bridge. The keepalive loop runs in the daemon process where the bridge IS available, so it retroactively binds unbound tasks to dedicated sessions — the same flow POST /scheduled-tasks uses for UI-created tasks. Each unbound task gets: spawnOrAttach(sessionScope:'thread'), named ⏰ prompt, sessionId written back to disk. This makes chat-created tasks show "查看对话" with a clock icon in the session list, matching the UI's "新建定时任务". * feat(serve): watch tasks file for immediate binding of new cron_create tasks The keepalive interval is 2-5 minutes, so a chat-created task could wait that long before being bound to a dedicated session — showing no "查看对话" link until the next tick. Adding a file watcher (same directory-watch + debounce pattern the scheduler uses) triggers an immediate tick when cron_create writes to disk, so the task is bound within ~500ms. * feat(serve): bind cron_create tasks to current session + ⏰ rename via keepalive Switch from creating a separate dedicated session to binding the task to the current chat session (so the first message is already in the transcript). The keepalive then renames that session to ⏰ prompt — the core layer can't rename sessions (no bridge access), but the daemon process can. A Set tracks renamed sessions to avoid repeated updateMetadata calls. Unbound tasks (legacy/CLI) still get new sessions via the existing bind path. * fix(core): keep createDurable() tasks unbound by default Reverts the auto-binding of durable tasks to the current session in createDurable(). Binding to a specific session means only that session can fire the task (#shouldFireDurable), but non-daemon paths (TUI, ACP, headless) have no keepalive to rehydrate the session after exit — making tool-created durable tasks go dormant. The daemon keepalive (bindAndNameSessions) already handles binding unbound tasks to dedicated sessions with ⏰ naming, so daemon-mode tasks get the same UX without the regression. * fix(serve): roll back orphan sessions in keepalive binding + add tests When bindAndNameSessions spawns a dedicated session for an unbound task but the subsequent updateCronTasks write fails (or the task was deleted between read and write), the spawned session was left behind with no owning task — the next tick would see the task still unbound (or spawn more orphans). Add rollback: closeSession + removeSession on failure, matching the POST /scheduled-tasks rollback pattern. Also add positive test coverage for the new binding paths: - unbound task → spawn + name + write sessionId to disk - bound task without ⏰ prefix → named exactly once (renamed Set dedup) - task vanishes before write → spawned session is rolled back * fix(serve): add timeout to spawnOrAttach in keepalive binding + test hardening BZ-D: spawnOrAttach in bindAndNameSessions had no timeout boundary — a hung spawn would keep running=true and stall all subsequent ticks, stopping heartbeats/revives for every scheduled-task session. Wrap with withTimeout (configurable via spawnTimeoutMs, default 30s) and attach a background handler to clean up late-resolved orphans. Also generalized withTimeout error messages to include the operation name, and made spawn timeout configurable for tests. Test improvements (GPT-5 review suggestions): - Assert spawnOrAttach payload (workspaceCwd + sessionScope: thread) - Verify SessionService.removeSession called during rollback - Regression test: createDurable stays unbound after enableDurable - Hung-spawn test: tick completes despite non-abortable spawn hang * fix(serve): keepalive hardening + i18n sync (review suggestions) - i18n: sync English 'View history' → 'View conversation' to match Chinese '查看对话' - Prune renamed Set alongside reviveState when tasks are removed - fs.watch: clarify null filename handling for Linux (treat as match) - updateCronTasks: skip .map() when task not found (no-op optimization) - Add tests: disabled unbound exclusion, naming failure resilience |
||
|---|---|---|
| .. | ||
| ApprovalModeDialog.module.css | ||
| ApprovalModeDialog.test.tsx | ||
| ApprovalModeDialog.tsx | ||
| DaemonStatusDialog.module.css | ||
| DaemonStatusDialog.test.tsx | ||
| DaemonStatusDialog.tsx | ||
| DeleteSessionDialog.test.tsx | ||
| DeleteSessionDialog.tsx | ||
| DialogPrimitives.module.css | ||
| DialogShell.module.css | ||
| DialogShell.test.tsx | ||
| DialogShell.tsx | ||
| dialogStyles.ts | ||
| ExtensionsDialog.tsx | ||
| HelpDialog.module.css | ||
| HelpDialog.tsx | ||
| McpDialog.module.css | ||
| McpDialog.tsx | ||
| ModelDialog.module.css | ||
| ModelDialog.test.tsx | ||
| ModelDialog.tsx | ||
| ReleaseSessionDialog.test.tsx | ||
| ReleaseSessionDialog.tsx | ||
| ResumeDialog.test.tsx | ||
| ResumeDialog.tsx | ||
| RewindDialog.module.css | ||
| RewindDialog.test.tsx | ||
| RewindDialog.tsx | ||
| ScheduledTasksDialog.module.css | ||
| ScheduledTasksDialog.test.tsx | ||
| ScheduledTasksDialog.tsx | ||
| scheduledTasksSchedule.test.ts | ||
| scheduledTasksSchedule.ts | ||
| SessionRow.test.tsx | ||
| SessionRow.tsx | ||
| SvgLineChart.module.css | ||
| SvgLineChart.test.tsx | ||
| SvgLineChart.tsx | ||
| ThemeDialog.test.tsx | ||
| ThemeDialog.tsx | ||
| TokenHeatmap.module.css | ||
| TokenHeatmap.test.tsx | ||
| TokenHeatmap.tsx | ||
| ToolsDialog.test.tsx | ||
| ToolsDialog.tsx | ||
| UsageDashboardTab.module.css | ||
| UsageDashboardTab.test.tsx | ||
| UsageDashboardTab.tsx | ||