

























@@ -0,0 +1,161 @@
1+---
2+title: "Channel route unification refactor"
3+sidebarTitle: "Channel route unification"
4+---
5+6+# Channel route unification refactor
7+8+This is a temporary implementation plan. Delete this file before merging the
9+refactor PR after the code, tests, and PR description prove the plan is
10+complete.
11+12+## Problem
13+14+Channel routing is represented several times:
15+16+- `ChannelRouteRef` is the SDK route identity shape.
17+- `ChannelOutboundSessionRoute` is the executable session route returned by
18+ channel messaging adapters.
19+- `ConversationRef` and `SessionBindingRecord` identify bound conversations.
20+- `DeliveryContext` mirrors route fields for sessions, sentinels, tools, and
21+ protocol compatibility.
22+- `SessionEntry` stores `deliveryContext`, `lastChannel`, `lastTo`,
23+`lastAccountId`, and `lastThreadId` as overlapping last-route fields.
24+25+The Discord subagent thread bug happened because a plugin hook had to return a
26+second ad hoc route object after core already knew a binding existed. TypeScript
27+could not protect that path because the hook result made `deliveryOrigin`
28+optional and core treated it as a best-effort delivery hint.
29+30+## Goals
31+32+- Make `ChannelRouteRef` the canonical route metadata shape inside core.
33+- Keep `ChannelOutboundSessionRoute` as the channel-owned executable session
34+ route.
35+- Keep `ConversationRef` as binding identity, not as a send target.
36+- Treat `DeliveryContext` as compatibility projection, not core routing truth.
37+- Share binding-to-route projection between subagent and ACP spawn paths.
38+- Keep sends in the durable message pipeline with `threadId` and `replyToId`.
39+- Deprecate ad hoc plugin hook fields once bundled callers use core projection.
40+41+## Non-goals
42+43+- Do not add a new route type family.
44+- Do not make core create provider-native threads directly.
45+- Do not infer child-thread support from a provider having any thread concept.
46+- Do not remove persisted session compatibility fields in the first pass.
47+- Do not bypass channel message adapters or durable final delivery.
48+49+## Existing contracts to keep
50+51+### `ChannelRouteRef`
52+53+`src/plugin-sdk/channel-route.ts` owns route normalization and matching. Extend
54+helpers here only when the concept belongs in the SDK surface. Core-only
55+projection helpers should live outside the SDK.
56+57+### `ChannelOutboundSessionRoute`
58+59+`src/channels/plugins/types.core.ts` owns the route returned from
60+`messaging.resolveOutboundSessionRoute`. Plugins keep provider-native parsing
61+rules here and should use `buildChannelOutboundSessionRoute` or
62+`buildThreadAwareOutboundSessionRoute`.
63+64+### `ConversationRef`
65+66+`src/infra/outbound/session-binding.types.ts` owns binding identity. A
67+conversation can require plugin resolution before it is routable.
68+69+### `DeliveryContext`
70+71+`src/utils/delivery-context.types.ts` remains for protocol, session, sentinel,
72+and tool compatibility. New core code should convert it to `ChannelRouteRef`
73+before comparing or carrying route state.
74+75+## Implementation phases
76+77+### Phase 1: route projection helpers
78+79+Add a core route projection module with helpers that:
80+81+- narrow a `ChannelRouteRef` to a routable route with `channel` and `target.to`;
82+- project `DeliveryContext` to and from `ChannelRouteRef`;
83+- project `SessionEntry` route fields to `ChannelRouteRef`;
84+- project `ConversationRef` to a route through plugin `resolveDeliveryTarget`
85+ with the existing generic `channel:<conversationId>` fallback;
86+- project `SessionBindingRecord` to a route using its `conversation`.
87+88+Tests must cover:
89+90+- normalized channel/account/to/thread fields;
91+- generic fallback routing when a plugin has no delivery-target projection;
92+- parent/child thread projection for Slack-like targets;
93+- same-channel merge without crossing fields between unrelated channels.
94+95+### Phase 2: route-first session compatibility
96+97+Add a canonical optional route field to session entries, then update session
98+delivery helpers to read the route first and derive legacy fields from it. Keep
99+writing legacy fields for compatibility.
100+101+Tests must cover:
102+103+- route wins over legacy fields when present;
104+- old session entries still hydrate;
105+- existing subagent session delivery context is not overwritten by spawn
106+ request params.
107+108+### Phase 3: shared spawn route planner
109+110+Move subagent and ACP binding route construction into a shared planner. The
111+planner returns:
112+113+- requester route;
114+- binding record;
115+- child delivery route when routable;
116+- compatibility delivery context;
117+- whether inline child delivery is allowed.
118+119+Subagent and ACP callers must stop open-coding binding-to-delivery projection.
120+121+Tests must cover:
122+123+- Discord-style child thread delivery;
124+- Slack-style parent channel plus thread id delivery;
125+- current-conversation binding without routable child delivery;
126+- requester origin kept separate from child delivery route.
127+128+### Phase 4: bundled plugin hook deprecation path
129+130+Move bundled plugins toward core binding projection:
131+132+- `subagent_spawning.deliveryOrigin` becomes deprecated compatibility output.
133+- `subagent_spawning.threadBindingReady` becomes deprecated compatibility
134+ readiness.
135+- `subagent_delivery_target` becomes deprecated once core can resolve the bound
136+ delivery route through `resolveDeliveryTarget`.
137+138+Keep public SDK compatibility during the transition and document deprecations in
139+types and PR notes.
140+141+### Phase 5: channel route adapter cleanup
142+143+Normalize bundled channel route builders:
144+145+- Discord and Slack stay on `buildThreadAwareOutboundSessionRoute`.
146+- Feishu, Matrix, and other channel route builders use shared route builders
147+ where possible.
148+- MS Teams keeps normal thread send support separate from child-thread binding
149+ until durable final `thread` capability and binding placement are explicitly
150+ proven.
151+152+## Validation checklist
153+154+- Focused unit tests for route projection.
155+- Subagent thread-binding tests.
156+- ACP spawn route tests.
157+- Slack, Discord, Feishu, Matrix, and MS Teams channel route tests where touched.
158+- `pnpm check:changed` or Testbox equivalent before PR.
159+- Autoreview until no accepted actionable findings remain.
160+- PR description includes deprecations, compatibility behavior, and proof.
161+- Delete this file after the PR is green and the description is complete.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。