
qwen-code 定时任务绑定当前会话Scheduled Tasks 表单与 cron_create 的 Current-session 入口设计解析【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code导读本文基于 qwen-code 仓库中的设计文档 docs/design/2026-08-24-scheduled-task-current-session-entrypoints.md深入讲解 daemon 定时任务Scheduled Tasks如何将新任务绑定到当前正在使用的会话上包括 Web Shell Scheduled Tasks 表单新增的会话选择器、cron_create工具新增的sessionMode: current模式以及两者背后共享的 daemon 控制路径与#9361会话复用契约。读完本文你将理解定时任务从专属会话演进到复用当前会话的完整设计动机、能力协商capability advertising、受控创建链路control path与边界约束并能依据源码路径追溯每一处实现的落点。1. 背景为什么需要 current-session 入口qwen-code 的 daemon 在 PR #9361 中引入了定时任务复用现有会话的能力创建定时任务的请求可以通过携带sessionId复用某个已有会话。daemon 会校验该会话为存活会话、将其标记为 caller-owned调用方所有、保持其常驻内存并在 daemon 重启后恢复。以这种方式绑定的任务即使 Web Shell 切换到另一个会话也依然在原会话中继续执行。然而在设计文档写作时两个面向用户的人口无法请求这种行为Scheduled Tasks 表单从未发送当前会话 id只能创建专属会话任务cron_create工具没有任何当前会话模式创建的持久化任务始终是 unbound无绑定状态。本设计的目标是为这两个入口补齐 current-session 能力同时不改变调度器scheduler、持久化所有权模型也不改变任一入口既有的默认行为。2. 既有基线#9361 的会话复用契约要理解新入口首先必须掌握 #9361 已确立的 REST 契约。这份契约是后续所有设计的事实来源source of truth其关键语义如下请求形态会话语义所有权省略sessionId或传nulldaemon 创建一个专属任务会话task-owned session任务拥有传入sessionId复用所选 workspace 中存活且空闲的会话持久化为sessionOwnedByTask: false即调用方所有契约还包括以下生命周期规则caller-owned 会话不会因为任务被重命名或删除而被重命名或关闭归档archive绑定会话会禁用任务取消归档会恢复任务删除会话会移除任务已启用的绑定会话保持常驻residentdaemon 重启后自动恢复rehydrate一个会话最多只能绑定一个定时任务。这与既有 core 工具行为存在显著差异cron_create创建的持久化任务默认是unbound的——没有sessionId通过既有的共享 per-project lock owner 触发工具本身不铸造专属任务会话。值得注意的既有机制是调度器已经把任务的task.sessionId映射为boundSessionId并且只有会话 id 等于任务boundSessionId的调度器才能触发该任务会话执行层也会把 cron 回合串行化排在活跃用户回合之后。3. 设计目标与非目标3.1 目标允许 Scheduled Tasks 表单把新任务绑定到当前选中的普通会话允许用户通过cron_create显式请求 current-session 绑定保持表单专属会话默认行为与cron_createunbound 持久化默认行为不变完整保留 #9361 的所有权、workspace、容量capacity、生命周期与唯一绑定校验当宿主无法保证 daemon 托管恢复restoration时明确失败。3.2 非目标明确排除通过 PATCH 重新绑定既有任务一个会话绑定多个任务在会话之间迁移任务历史绑定带有parentSessionId、channel、side_task、scheduled_task、显式standalone来源、保留的 Live Voice 来源 id、未知来源值或已归档/非存活non-live的会话改变 Scheduled Tasks 页面既有的 Create via chat 动作该动作有意开启全新会话解决 #9415 追踪的遗留 teardown-vs-reuse 竞态改变 token 限额或 miss-fire 策略。4. 公共行为Public behavior4.1 Scheduled Tasks 表单双选项会话选择器创建表单新增一个两选项会话选择器Dedicated task conversation专属任务会话——默认项省略sessionId保持当前行为Current conversation当前会话——在既有DaemonCreateScheduledTaskRequest.sessionId字段中发送所选会话 id。这里的 current 指外层 Web Shell 连接当前选中的普通会话connection.sessionId——即使mainView是 Scheduled Tasks 页面、聊天面板被遮挡也依然成立可见的聊天面板并非必要条件。没有选中会话时选项被禁用分栏split panes不会替换外层选中 id也不会存在某个分栏隐式胜出非选中分栏中的活动既不会禁用该选项也不会提供sessionId。所选会话只有在满足以下条件时才合格是顶层会话、没有sourceId、且sourceType缺失或为default——这正是普通 Web Shell 会话的元数据形态。表单会拒绝channel、side_task、scheduled_task、显式standalone来源值、任何未知来源值以及default搭配保留的realtime_voice:来源 id 前缀的情况。能力协商capability gatingcurrent-conversation 选项仅在 daemon 宣告新的scheduled_task_session_reuse能力时才显示见 packages/cli/src/serve/capabilities.ts 的能力注册与 packages/cli/src/serve/capabilities.ts 的条件宣告谓词——该标签由AdvertiseFeatureToggles.currentSessionSchedulingAvailable驱动。选项在以下场景禁用并给出原因没有选中的会话选中会话仍有运行中的回合turn或待处理交互选中会话不是合格顶层普通会话表单所选 workspace 与选中会话的 workspace 不一致已加载的任务列表里已存在绑定该会话 id 的任务。这些检查是**建议性advisory**的。daemon 始终是权威方表单会透出它既有的session_busy、session_already_bound、session_workspace_mismatch、session_not_live等相关错误。绑定只在创建期间可选。编辑模式不显示也不发送sessionId。任务卡片保留通用的 View conversation 动作——这对专属会话和 caller-owned 会话都正确。在 Web Shell 实现中这些禁用原因被集中计算为currentSessionDisabledReason见 packages/web-shell/client/components/dialogs/ScheduledTasksDialog.tsx并据此控制选择器的 disabled 状态与提示文案。4.2cron_create新增sessionMode参数CronCreateParams新增可选字段sessionMode?: unbound | current;默认值为unbound。具体语义sessionMode: unbound或省略该字段——走既有路径持久化任务保持 unboundsession-only 任务保持当前进程本地sessionMode: current——仅在durable: true时合法工具描述指示模型仅在用户明确要求把定时工作保留在当前会话中时才使用权限分类器permission-classifier的投影输入中包含sessionMode。该参数与校验逻辑已在源码中落地见 packages/core/src/tools/cron-create.ts 的参数声明、packages/core/src/tools/cron-create.ts 对current 非 durable的拒绝逻辑、packages/core/src/tools/cron-create.ts 的 JSON Schema 描述以及 packages/core/src/tools/cron-create.ts 的分类器投影。因此两个入口共有三种显式结果入口与请求持久化会话绑定执行所有权表单默认RESTsessionId省略daemon 铸造任务专属会话专属任务会话cron_create持久化mode 省略或unbound无sessionId既有共享 per-project lock ownercron_createmodecurrent调用方会话sessionOwnedByTask: falsecaller-owned 当前会话在 daemon 托管的 ACP 会话之外current 模式会返回明确的current_session_scheduling_unavailable错误unbound 持久化任务与 session-only 任务保留既有路径。源码中该错误由 packages/core/src/tools/cron-create.ts 抛出并由 ACP 会话层将 daemon 的 method-not-found-32601映射为同义错误见 packages/cli/src/acp-integration/session/Session.ts。5. 架构为什么 REST 路径不能直接从cron_create调用5.1 根本矛盾session_busy公开的 #9361 端点要求提供的会话必须空闲idle。但cron_create工具调用运行在活跃 prompt 之中它自己的会话必然是 busy 的——直接走 REST 等价调用会得到session_busy。busy 规则必须对普通客户端保持不变任意调用方不得在另一个回合正在修改某个会话时绑定它。因此 current-mode 工具创建只能走 daemon-only 控制路径control path。这条路径信任 daemon 派生的 workspace agent 运行时共享 ACP 连接本身无法证明某个任意的会话 id 属于正在执行的确切回合。控制请求转而把 daemon 拥有的 prompt 状态绑定到在运行时内部盖章stamped的标识符上——这些标识符位于模型可见的工具参数之外。5.2 Daemon 控制路径Core Config 接收一个可选的CurrentSessionScheduledTaskCreator能力注入沿用既有 injected daemon-capability 模式ACP Session 实现将其接到一个新的控制请求上qwen/control/scheduled-task/create-currentcore 创建器输入包含从工具调用上下文捕获的执行中promptId。ACP Session 对象用this.sessionId盖章callerSessionId并转发该 prompt id。这两个标识符都不接受来自CronCreateParams控制请求也不接受单独的 target session id——防止模型伪造绑定目标。bridge 处理器按序执行校验 payload 类型与 REST 路由相同的 prompt 边界验证 bridge 客户端拥有callerSessionId在收到请求的 bridge 中解析该存活会话要求promptId等于该条目的activePromptId当promptActive为 true 时沿用既有external_tool_guard/prepare的绑定模式应用与表单完全相同的 source 白名单无 parent、无sourceId、sourceType缺失或为default委托给仅由管理定时任务会话的qwen serve运行时安装的宿主回调。prompt 匹配的目的防止在拥有多个会话的连接上发生误绑 busy 兄弟会话。这是可信 agent 运行时内部的一致性检查而非声称 ACP 能密码学地认证确切回合。公开 REST 路径从不使用这个例外始终拒绝 busy 提供的会话。如果未安装宿主回调bridge 返回 method-not-found工具将其映射为current_session_scheduling_unavailable。实现落点ACP 会话层在 packages/cli/src/acp-integration/session/Session.ts 注册 creator仅在QWEN_CODE_SERVE_ENV 1时生效调用SERVE_CONTROL_EXT_METHODS.createCurrentSessionScheduledTask并携带{ callerSessionId, promptId, cron, prompt, recurring }——其中promptId优先取getInvocationContext()?.promptId。5.3 共享的 daemon 创建命令宿主回调与 REST 路由共享一个从 #9361 provided-session 分支抽取的聚焦命令createScheduledTaskWithExistingSession见 packages/cli/src/serve/routes/scheduled-tasks.ts。该命令接受内部创建来源type ExistingSessionCreateOptions { source: rest | cron-tool; };关键差异在assertReusableScheduledTaskSession的调用上cron-tool来源仅在 bridge 完成内部盖章的 caller session 与 prompt id 到存活活跃 prompt 的匹配后由私有宿主回调提供两条路径应用相同的selected-runtime 与 workspace 所有权、归档状态、scheduled-task-source、容量、generation 与唯一绑定检查只有那条经过 prompt 匹配的可信路径可以跳过 active-prompt 拒绝pending interactions 仍不合格公开 REST 从不跳过这两项 idle 检查。最终写锁检查保持权威重新校验会话存活且未被任务保留、拒绝并发绑定并以既有字段写入任务{ sessionId: callerSessionId, sessionOwnedByTask: false, }没有引入新的持久化 schema 或迁移。任务创建时间戳与lastFiredAt使用与 REST 路由相同的 creation-minute 锚点源码中即lastFiredAt: now - (now % 60_000)见 packages/cli/src/serve/routes/scheduled-tasks.ts因此任务不可能从仍在创建它的那个回合触发。宿主提交任务后控制响应返回任务 id 与 cron 表达式。创建会话的文件 watcher 加载绑定任务后续cron_list立即保持一致因为持久化列表是file-first的。5.4 执行与会话切换调度器零改动没有任何调度器改动。任务落盘后只有其会话 id 等于任务boundSessionId的调度器才能触发它。若用户回合活跃cron prompt 在该会话既有的串行队列中等待。切换到另一个 Web Shell 会话会分离之前的 UI 客户端但不会关闭该会话keepalive 继续对绑定会话心跳启动恢复boot rehydration在 daemon 重启后恢复它恢复失败时任务保持绑定并沿用既有策略重试——绝不会把工作移动到另一个会话。6. 兼容性与发布策略sessionMode可选默认保持既有 unbound 工具行为既有 REST 与 SDK 调用方无任何变化既有任务文件无需重写一个currentSessionSchedulingEnabled构造期条件要求manageScheduledTaskSessions与 ACP current-session 宿主回调同时满足同一条件负责宣告scheduled_task_session_reuse能力并在主运行时及每个动态创建的 workspace 运行时 bridge 上安装回调。进程不会宣告部分支持——所以选中的 workspace 不可能提供了选择器却在cron_createcurrent 模式返回 method-not-found不支持scheduled_task_session_reuse的 Web 客户端不渲染新选择器防止旧 daemon 静默忽略意图非 daemon 工具调用方收到显式错误而不是创建一个绑定会话无法恢复的持久化任务该特性可以在一个实现 PR 中发布因为能力宣告、UI 使用与 daemon 控制支持是版本同步的versioned together。能力宣告的映射在源码中有明确体现scheduled_task_session_reuse注册于 packages/cli/src/serve/capabilities.ts其条件谓词(toggles) toggles.currentSessionSchedulingAvailable true位于 packages/cli/src/serve/capabilities.ts对应的 toggle 字段currentSessionSchedulingAvailable?: boolean定义在 packages/cli/src/serve/capabilities.ts。7. 测试计划如何验证每个层面设计文档给出了完整的分层测试矩阵可作为实现验收清单Core 工具层见 packages/core/src/tools/cron-create.test.ts省略与显式 unbound 模式保留 session-only 与 unbound 持久化创建不铸造会话current 模式要求durable: true、执行中 prompt id 与注入的宿主能力current 模式转发精确的调度并返回已提交的任务 id权限分类器输入包含sessionMode宿主失败与 method-not-found 被透出且不创建 unbound 兜底任务。Bridge 与 daemon 层控制方法拒绝畸形 payload、未知调用方、以及不属于 bridge 客户端的 caller 会话缺失或过期的 prompt id、以及 owned 兄弟会话上的活跃 prompt 被拒绝且不创建任务可信调用方仅在盖章 prompt id 与该会话activePromptId匹配时才在hasActivePrompt: true下成功用同一 busy 会话走 REST 创建仍返回session_busysource 矩阵只接受无 source id 的顶层 unset/default 会话拒绝 parented、Channel、side-task、scheduled-task、显式 standalone、Live Voice 与未知来源会话workspace 不匹配、归档/非存活会话、容量、generation 关闭与既有绑定均保留 #9361 错误并发 REST/tool 创建恰好提交一个任务提交的任务是 caller-owned任务重命名与删除不会重命名或关闭会话缺少 scheduled-task 会话管理或 ACP 宿主回调时能力不宣告动态创建的 workspace 运行时收到与主运行时相同的回调current-mode 创建路由到所选运行时而非回退主运行时。Web Shell 层见 packages/web-shell/client/components/dialogs/ScheduledTasksDialog.test.tsx专属模式是默认且省略sessionId在 Scheduled Tasks 页面遮挡聊天面板时current 模式仍发送外层选中会话 id能力缺失、无选中会话、选中会话活跃回合、会话来源不合格、workspace 不匹配、既有绑定都会以预期说明禁用选项分栏活动不会禁用或替换空闲的外层选中会话编辑请求绝不修改绑定Create via chat 继续开启全新会话。端到端场景在会话 A 中通过cron_create创建持久化 current-session 任务确认工具回合活跃时创建成功将 Web Shell 切换到会话 B确认调度回合出现在 A 而非 B不打开 A 直接重启 daemon确认 A 被恢复且下一次触发仍出现在 A删除任务确认 A 保持打开且可用在 A 空闲时通过 Scheduled Tasks 表单重复创建确认它复用同一会话而不铸造新会话。8. 被否决的备选方案8.1 对公开端点放宽session_busyREST 既没有可信的 workspace-runtime 上下文也没有控制路径使用的内部盖章 prompt 身份。放宽它会允许任意客户端绑定另一个回合正在修改的会话并削弱 #9361 对每个 API 客户端的保护。8.2 从cron_create直接写任务文件这绕过了 daemon 运行时所有权、容量与 generation 检查并且无法在qwen serve之外安全承诺 keepalive。8.3 延迟到工具回合结束再创建工具将不得不在持久化之前报告成功或者保留一个进程本地延迟操作——而它的失败无法返回给用户。可信控制路径在工具返回之前提交。8.4 先创建专属会话再迁移迁移会拆分 transcript 历史并引入回滚与所有权转换的复杂性既然 #9361 可以直接绑定目标会话这些都不必要。9. 总结与延伸阅读本设计在不触碰调度器与持久化模型的前提下为 daemon 定时任务补全了两个 current-session 入口表单侧通过DaemonCreateScheduledTaskRequest.sessionId绑定外层选中会话工具侧通过sessionMode: current走 daemon-only 控制路径。其核心工程思想值得复用当公共 API 的合法性约束busy 会话不可绑定与内部可信调用冲突时不削弱公共约束而是建立一条携带内部盖章身份callerSessionIdpromptId的私密控制通道并在最终写锁处与公共路径汇合。若想进一步深入可在仓库中阅读以下实现与关联设计核心工具实现packages/core/src/tools/cron-create.ts 与测试 packages/core/src/tools/cron-create.test.tsACP 会话层注册控制 creatorpackages/cli/src/acp-integration/session/Session.tsdaemon REST 共享创建命令packages/cli/src/serve/routes/scheduled-tasks.ts 及其测试 packages/cli/src/serve/routes/scheduled-tasks.test.ts能力注册与宣告packages/cli/src/serve/capabilities.ts任务文件持久化中的所有权字段packages/core/src/services/cronTasksFile.tsWeb Shell 表单选择器packages/web-shell/client/components/dialogs/ScheduledTasksDialog.tsx底层会话复用契约的前置设计2026-08-01-caller-supplied-session-id.md、2026-07-22-daemon-mcp-force-reconnect.md。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考