qwen-code Channel Delivery V1:为定时任务、Prompt 与 Notify 打造反向控制链路的 Channel 主动投递 qwen-code Channel Delivery V1为定时任务、Prompt 与 Notify 打造反向控制链路的 Channel 主动投递【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code本文基于 qwen-code 仓库中的实现计划 2026-07-21-channel-delivery-v1.md 展开完整解读 Channel Delivery V1 的设计目标、七项实现任务、公共投递契约与错误码体系并结合当前仓库中已落地的源码ChannelBase.deliverProactive、qwen/control/channel-delivery反向控制方法、notify 路由等说明各任务的实际实现形态。读完本文你将掌握如何在不引入持久化 outbox、不做重试与回放的前提下让定时任务与 daemon Prompt 在成功收尾时把结果主动推送到 IM Channel。一、背景与设计目标为什么选择即时尽力投递qwen-code 是一个常驻终端的开源 AI coding agent其 daemonqwen serve通过 REST/SSE 对外提供服务同时支持接入 DingTalk、Feishu、Telegram、WeCom 等 IM Channel。在实际使用中一个常见需求是Agent 完成一段工作后把结果主动推送到聊天窗口而不是等用户在终端里查询。计划文档开宗明义给出了 V1 的目标原文 GoalAdd immediate best-effort Channel delivery for scheduled tasks, daemon Prompt, and direct Notify without a persistent outbox or global final hook. 为定时任务、daemon Prompt 与直接 Notify 增加即时尽力投递immediate best-effort的 Channel 投递且不引入持久化 outbox 或全局 final hook。对应的架构原文Architecture是Each Agent-backed producer identifies its own successful final boundary and sends a reverse control request to the daemon. The daemon binds the request to the bridges canonical workspace and reuses one worker IPC path ending atChannelBase.deliverProactive(). Notify calls the same daemon-side path synchronously; Webhook and background notification execution paths remain unchanged.即每个由 Agent 支撑的生产者定时任务、Prompt 会话、Notify 接口各自识别自己的成功收尾边界向 daemon 发起一次反向控制请求daemon 把该请求绑定到 bridge 的规范 workspace复用同一条 worker IPC 路径最终统一收敛到ChannelBase.deliverProactive()。Notify 路由同步调用同一 daemon 侧路径Webhook 与后台通知的执行路径保持不变。技术栈为TypeScript、Express、ACP extMethod、子进程 IPC、Vitest、REST/SSE SDK。1.1 全局约束Global Constraints计划文档用一组硬约束划定了 V1 的边界这些约束决定了整个方案做减法的风格保留 Prompt 与 Webhook 的202契约不变不引入outbox、轮询、持久化、回放replay、重试retry、全局 final hook、惰性 worker 启动、主 workspace 兜底公共投递结构固定为{ kind:channel, target:{ channelName, type, id } }文本在 IPC 之前必须非空且长度被限制在 100,000 个 UTF-16 code units 以内使用 ESM、严格 TypeScript、kebab-case 文件名、禁止any、禁止跨包相对导入每个生产行为都遵循先失败测试RED后实现GREEN的 TDD 循环。这套约束的核心思想是投递是尽力而为的best-effort失败不重试、不补偿、不阻塞主流程。这与即时通知的产品语义匹配——用户要的是成功时告诉我一声而不是任何代价都要送达。二、公共投递契约从计划到落地源码2.1 契约定义Task 1 定义了最底层的三个契约Interfaces 原文ChannelProactiveTarget { type:user|chat; id:string }实际落地时还携带channelName以标识目标 ChannelChannelBase.deliverProactive(target, text): Promisevoid携带permanent语义的ChannelProactiveDeliveryError。在当前仓库中目标类型已完整落地于 types.tsexport interface ChannelProactiveTarget { channelName: string; type: user | chat; id: string; }错误类同样落地为独立文件 ChannelProactiveDeliveryError.ts并提供了类型守卫isChannelProactiveDeliveryError()。从当前源码看实现相较计划有一处演进错误不再用permanent:boolean而是使用disposition: permanent | transient二态用以区分永久性失败如目标非法、Channel 不支持主动发送与瞬时性失败如限流export type ChannelProactiveDeliveryDisposition permanent | transient; export class ChannelProactiveDeliveryError extends Error { readonly code CHANNEL_PROACTIVE_DELIVERY_ERROR_CODE; constructor( readonly disposition: ChannelProactiveDeliveryDisposition, message: string, options?: ErrorOptions, ) { ... } }2.2deliverProactive的校验链ChannelBase.deliverProactive()是整条投递链路的终点当前实现见 ChannelBase.ts其校验顺序清晰可查Channel 归属校验target.channelName ! this.name时抛出永久性错误防止把 A Channel 的目标投递给 B Channel 的 adapter能力校验supportsProactiveSend()为假的 Channel 直接拒绝目标合法性type必须是user | chatid必须是非空字符串文本非空校验text.trim().length 0直接拒绝适配器级目标支持将ChannelProactiveTarget映射为内部SessionTargetisGroup: target.type chat经supportsProactiveDeliveryTarget()二次确认后调用pushProactiveDelivery()完成真正发送。这条校验链与计划中 Task 1 的验收步骤一一对应typed user/chat targets reachpushProactive空白 ID/文本失败不支持的目标失败且 DingTalk HTTP-200 但无效/被流控的接收者要拒绝。各适配器的覆盖测试散布在 DingtalkAdapter.ts、Feishu adapter.test.ts、TelegramAdapter.test.ts 与 WeComAdapter.test.ts 中。三、七个实现任务的完整拆解计划文档将全部工作拆为 7 个任务每个任务都给出文件清单、接口约定与 RED/GREEN 步骤。下面按原文骨架逐一展开并标注当前仓库中可查证的实现落点。3.1 Task 1投递契约与 proactive 适配器边界涉及文件原文清单新建packages/channels/base/src/ChannelProactiveDeliveryError.ts修改packages/channels/base/src/types.ts、ChannelBase.ts、index.ts测试packages/channels/base/src/ChannelBase.test.ts修改/测试packages/channels/dingtalk/src/DingtalkAdapter.ts、packages/channels/feishu/src/FeishuAdapter.ts以及 telegram/wecom 的适配测试执行步骤原文 checkbox先写聚焦测试类型化 user/chat 目标能到达pushProactive、空白 ID/文本失败、不支持的目标失败、DingTalk HTTP-200 无效/流控接收者拒绝逐包运行测试确认 RED因为类型化边界尚不存在添加最小目标类型、错误类、校验与适配器映射重跑测试确认 GREEN。当前仓库中该任务已全部落地错误类文件与ChannelProactiveTarget接口均存在deliverProactive()的校验链第二节即是步骤 3 的产物而 ChannelBase.test.ts 覆盖了对应行为。3.2 Task 2精确 workspace 的 worker IPC这是整个方案中最关键的一环——投递请求如何从 daemon 进程安全地送到正确的 worker 子进程。计划原文要点涉及文件新建packages/cli/src/serve/channel-delivery-ipc.ts当前仓库中notify 路由的导入路径已演化为packages/cli/src/serve/runtime/channel-delivery-ipc.js见 channel-notify.ts 的 import 语句测试channel-delivery-ipc.test.ts修改/测试channel-worker-supervisor.ts、channel-worker-group.ts、channel-worker-manager.ts及各自测试修改/测试packages/cli/src/commands/channel/daemon-worker.ts及其测试接口约定ChannelDeliveryRequest { deliveryId, channelName, target, text }ChannelDeliveryErrorCode取值channel_worker_unavailable、channel_delivery_timeout、channel_delivery_invalid、channel_delivery_rejected、channel_delivery_queue_full、channel_delivery_failedChannelWorkerManager.deliver(workspaceCwd, request): Promisevoid执行步骤先加 IPC 校验器与结果关联correlation测试确认 RED实现最小请求/结果类型与校验器确认 GREEN为 supervisor 增加运行中 worker 成功、30 秒超时、退出清理、pending 请求拒绝的测试确认 RED实现 supervisor 的结果关联确认 GREEN为 group/manager 增加精确 workspace 选择、无主 workspace 兜底的测试确认 RED实现不启动 worker 的 group/manager 路由确认 GREEN为 worker 增加适配器解析、最多 16 个并发投递、队列满响应、错误分类脱敏测试确认 RED通过deliverProactive()实现 worker 执行确认 GREEN。从当前源码看这组接口确实按约定收敛错误码的规范集合在 bridgeOptions.ts 中以 const 数组形式定义注释明确写着cli channel-delivery-ipc.ts and bridgeClient.ts import thisstatus.ts 注册了 extMethod 常量channelDelivery: qwen/control/channel-delivery。worker 侧的执行落在 daemon-worker.ts该文件引用了deliverProactive路由/管理侧由 channel-worker-manager.ts、channel-worker-supervisor.ts、channel-worker-group.ts 协同实现且这三者都实现了deliver(workspaceCwd, request)路径。两个值得强调的设计点精确 workspace、绝不兜底投递只发给与请求 workspace 精确匹配的 worker找不到就报channel_worker_unavailable映射 HTTP 503而不是随便找个主 workspace 的 worker 试试。这与全局约束中no primary-workspace fallback一致避免把 A 工作区的结果误投到 B 工作区配置的 Channel 上。16 并发 队列满即拒worker 内部对投递设了并发上限队列满返回channel_delivery_delivery_queue_full而不是无限积压——这是 best-effort 语义在资源层的体现。3.3 Task 3共享公共解析器、定时任务持久化与生产者自持 Final涉及文件新建packages/cli/src/serve/channel-delivery.ts同样已演进到serve/runtime/子目录测试channel-delivery.test.ts修改/测试packages/core/src/services/cronTasksFile.ts定时任务文件、packages/cli/src/serve/routes/scheduled-tasks.ts定时任务 REST 路由、packages/cli/src/acp-integration/session/Session.ts接口约定产出parseChannelDelivery(value): PublicChannelDelivery与normalizeChannelDelivery(deliveryId, delivery, text): ChannelDeliveryRequest消费 Task 2 的反向投递提交接口。执行步骤测试精确公共形状、拒绝未知 kind/type、空白字段、有界文本确认 RED实现解析器/规范化器确认 GREEN测试 Core 层可选 delivery 字段的往返持久化且旧任务legacy tasks保持有效确认 RED添加可选字段与校验确认 GREEN测试两个 scheduled REST scope 都接受新形状、拒绝畸形 target确认 RED实现路由解析与持久化确认 GREEN测试无 delivery 的定时执行不收集也不提交文本成功投递的运行恰好提交一次cancel/error/空输出不提交确认 RED实现 delivery 门控的按次运行收集器在end_turn之后做反向提交确认 GREEN。这一任务确立了生产者自持 Final 边界producer-owned final boundary原则是否投递由生产者自己判断这一轮成功了没有daemon 层不设置全局的回合结束钩子去猜测何时该发通知。公共形状{ kind:channel, target:{ channelName, type, id } }的解析函数当前可在 scheduled-tasks.ts 路由、session.ts 路由与 notify 路由之间共享正是共享公共解析器的落点。3.4 Task 4反向投递控制与结果事件涉及文件修改/测试packages/acp-bridge/src/status.ts、bridgeOptions.ts、bridgeClient.ts及测试修改/测试packages/cli/src/serve/run-qwen-serve.ts及测试接口约定反向 extMethodqwen/control/channel-deliveryhost 处理器签名{ sessionId, deliveryId, source, target, text, promptId?, taskId?, firedAt? } PromiseChannelDeliveryResult可回放replayable的channel_delivery_resultBridge 事件。执行步骤为 BridgeClient 增加校验后的反向派发、未知 session 拒绝、delivered/failed/skipped 三种结果的脱敏发布、不泄漏 target/text的测试确认 RED添加 extMethod、handler 接缝与发布代码确认 GREEN为 serve 增加primary、secondary、workspace 三种 bridge 构造器各自绑定自己的规范 workspace、绝不信任子进程提供的 workspace 数据的测试确认 RED将三个构造器接入当前 manager 的投递方法不做惰性启动确认 GREEN。当前源码可完整验证该任务bridgeClient.test.ts 中存在describe(BridgeClient — channel-delivery extMethod dispatch)测试块常量METHOD qwen/control/channel-delivery覆盖 delivered/failed/skipped 三种结果事件的发布bridgeClient.ts 中发布了type: channel_delivery_result事件事件发布遵循脱敏约束事件本身只携带 deliveryId、状态与脱敏后的错误信息不携带完整 target/text防止敏感内容经由 SSE 事件面外泄。这里还有一处安全设计值得注意workspace 绑定永远以 daemon 侧的规范值为准。子进程ACP child即使在反向请求里声称自己属于某个 workspacedaemon 也不信任只按 bridge 构造时绑定的 canonical workspace 路由。3.5 Task 5Prompt 投递涉及文件修改/测试packages/cli/src/serve/routes/session.ts、packages/cli/src/serve/server.test.ts修改/测试packages/acp-bridge/src/bridge.ts、packages/cli/src/acp-integration/session/Session.ts及测试接口约定消费 Task 3 的公共解析器与 Task 4 的反向控制保留202 { promptId, lastEventId }契约其后正常发出turn_complete/turn_error。执行步骤测试路由校验、从 ACP payload 中剥离顶层 delivery 字段、剥离保留的_meta、注入受信关联信息确认 RED实现路由/Bridge 传递且不把 delivery 数据塞进params.prompt确认 GREEN测试每轮成功的 Final 提交一次、无 delivery 时保持旧行为、cancel/error/max-token/空 Final 不发送确认 RED实现 delivery 门控收集器在end_turn之后做 fire-and-forget 反向请求确认 GREEN测试turn_complete先于channel_delivery_result到达且 Prompt 完成不等待投递结果确认 GREEN。第 2 步是安全边界delivery 配置属于控制面数据绝不能混入 prompt 参数被模型看到或被日志记录。第 5 步确立了时序契约用户先看到回合完成投递结果作为附加事件后到两者解耦。3.6 Task 6同步 Notify 路由与 SDK 接口涉及文件新建packages/cli/src/serve/routes/channel-notify.ts修改/测试packages/cli/src/serve/server.ts及测试修改/测试packages/sdk-typescript/src/daemon/DaemonClient.ts及单测接口约定产出POST /workspace/notify与POST /workspaces/:workspace/notify两个路由产出 SDK 上 primary 与 workspace 两种客户端的notify({ text, delivery })。执行步骤路由测试严格 bearer 鉴权、精确 workspace 路由、成功路径、400/502/503/504 错误映射、不存在测试用路由确认 RED仅用当前 manager 实现两个路由确认 GREENSDK 测试请求路径、请求体、结果类型、capability 预检确认 RED实现 primary/workspace SDK 助手确认 GREEN。当前仓库中的 channel-notify.ts 已落地其中错误映射逻辑L36-L54把错误码精确翻译成 REST 状态码错误码HTTP 状态语义channel_delivery_invalid400请求形状非法未知 kind/type、空白字段、超长文本channel_delivery_timeout504worker 30 秒内未应答channel_worker_unavailable/channel_delivery_queue_full503该 workspace 无运行中 worker / 投递队列满其余含channel_delivery_failed、channel_delivery_rejected502投递执行失败另外该路由对请求体做字段白名单检查——body 只允许text与delivery两个键多余字段直接判为channel_delivery_invalid这是典型的防注入式严格解析。3.7 Task 7SDK 事件、capability、文档与端到端验证涉及文件修改/测试packages/sdk-typescript/src/daemon/events.ts、packages/cli/src/serve/capabilities.ts及测试文档docs/developers/qwen-serve-protocol.md、.qwen/e2e-tests/channel-delivery-v1.md接口约定已知事件known eventchannel_delivery_resultdaemon capabilitychannel_delivery。执行步骤SDK 事件测试delivered/failed/skipped 三种结果、在turn_complete之后的回放replay、拒绝畸形已知事件确认 RED实现事件 schema/校验器且不改变DaemonClient.prompt()的完成语义确认 GREENcapability 测试替换更窄的分支级 capability确认 GREEN更新协议与 E2E 文档写明无 outbox、无重试的最终语义运行所有被改动文件的测试以及npm run build、npm run typecheck、npm run bundle用正式 Prompt、Scheduled、Notify、Webhook 四条路由执行 E2E 矩阵记录脱敏证据确认没有历史回放做两轮完整 diff 的干净自查self-audit任何修复都会重置验证与干净通过计数。当前仓库中该任务同样落地capabilities.ts 声明了channel_delivery: { since: v1 }SDK 侧 events.ts 将channel_delivery_result列为已知事件并有配套测试 daemonEvents.test.ts。四、端到端投递链路全景把七个任务串起来一次定时任务的结果投递在系统内经历如下链路定义期用户在定时任务或 Prompt 请求中携带delivery: { kind:channel, target:{ channelName, type, id } }parseChannelDelivery()严格校验形状normalizeChannelDelivery()补齐deliveryId生成内部ChannelDeliveryRequest任务文件经cronTasksFile.ts往返持久化旧任务无需迁移触发期执行成功到达 Final 边界end_turn且输出非空、非 cancel/error后生产者发起反向控制请求qwen/control/channel-delivery携带{ sessionId, deliveryId, source, target, text, promptId?, taskId?, firedAt? }该请求 fire-and-forget不阻塞turn_complete路由期daemon 按 bridge 的规范 workspace 精确匹配 worker不兜底、不信任子进程自报supervisor 关联请求与结果30 秒超时判channel_delivery_timeout执行期worker 解析适配器最多 16 个并发投递经ChannelBase.deliverProactive()的归属/能力/目标/文本四重校验后调用pushProactiveDelivery()完成平台发送反馈期Bridge 发布脱敏的channel_delivery_resultdelivered/failed/skippedSSE 事件SDK 消费方无需改动prompt()的完成判断即可订阅。Notify 路由则跳过第 2 步的生产者自持环节由调用方同步触发同一条 daemon 侧路径步骤 3–5并同步返回结果或 400/502/503/504 错误。五、工程方法论与适用前提这份计划的价值不只在于功能本身还在于其方法论约束对任何在大型 agent 系统中加跨进程副作用功能的团队都有参考意义RED/GREEN 强制循环每个行为先写失败测试确认 RED再写最小实现确认 GREEN7 个任务共 30 步 checkbox 全程可追踪安全默认值不信任子进程提供的 workspace 数据、事件面脱敏不泄漏 target/text、请求体字段白名单、delivery 数据与 prompt 参数隔离明确的不做什么无 outbox、无轮询、无持久化、无回放、无重试、无惰性 worker 启动、无主 workspace 兜底——把复杂度挡在门外用失败可见但不补偿换系统简单性。适用前提与限制投递仅在目标 workspace 的 channel worker 正在运行时可用否则得到 503 且不做补偿文本上限 100,000 UTF-16 code units目标 Channel 必须实现supportsProactiveSend从源码结构看部分平台的群/用户主动消息能力存在平台侧差异。协议语义的权威描述可进一步查阅 qwen-serve-protocol.mdREST 接口整体参见 daemon-rest-api-reference.md。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考