work-ally 0.2.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/AGENTS.md +110 -0
  2. package/DASHBOARD.md +160 -0
  3. package/PRODUCT.md +113 -0
  4. package/README.md +403 -0
  5. package/ally.sh +171 -0
  6. package/bridge/src/approval-rules.ts +360 -0
  7. package/bridge/src/channel-delivery.ts +207 -0
  8. package/bridge/src/channel-types.ts +22 -0
  9. package/bridge/src/channels/fake/adapter.ts +31 -0
  10. package/bridge/src/channels/feishu/adapter.ts +411 -0
  11. package/bridge/src/channels/feishu/approvals.ts +6 -0
  12. package/bridge/src/channels/feishu/formatter.ts +276 -0
  13. package/bridge/src/channels/feishu/normalize.ts +368 -0
  14. package/bridge/src/codex-config.ts +52 -0
  15. package/bridge/src/config.ts +240 -0
  16. package/bridge/src/fake-runtime-client.ts +505 -0
  17. package/bridge/src/handoff-service.ts +494 -0
  18. package/bridge/src/logger.ts +194 -0
  19. package/bridge/src/memory-digest.ts +186 -0
  20. package/bridge/src/receiver-approval-autonomy.ts +158 -0
  21. package/bridge/src/receiver-control-core.ts +140 -0
  22. package/bridge/src/receiver-control-work-session.ts +218 -0
  23. package/bridge/src/receiver-control.ts +83 -0
  24. package/bridge/src/receiver-delivery.ts +136 -0
  25. package/bridge/src/receiver-helpers.ts +96 -0
  26. package/bridge/src/receiver-human-gate.ts +333 -0
  27. package/bridge/src/receiver-inbound-preflight.ts +162 -0
  28. package/bridge/src/receiver-recovery.ts +236 -0
  29. package/bridge/src/receiver-runtime-callbacks.ts +367 -0
  30. package/bridge/src/receiver-runtime-policy.ts +132 -0
  31. package/bridge/src/receiver-runtime-state.ts +124 -0
  32. package/bridge/src/receiver-support-actions.ts +189 -0
  33. package/bridge/src/receiver-thread-start.ts +57 -0
  34. package/bridge/src/receiver-turn-coordination.ts +94 -0
  35. package/bridge/src/receiver-turn-execution.ts +257 -0
  36. package/bridge/src/receiver-turn-failure.ts +143 -0
  37. package/bridge/src/receiver-turn-result.ts +185 -0
  38. package/bridge/src/receiver-turn-steer.ts +70 -0
  39. package/bridge/src/receiver-work-session.ts +76 -0
  40. package/bridge/src/receiver.ts +329 -0
  41. package/bridge/src/router.ts +62 -0
  42. package/bridge/src/runtime-client-agent-messages.ts +150 -0
  43. package/bridge/src/runtime-client-message-dispatch.ts +176 -0
  44. package/bridge/src/runtime-client-protocol.ts +411 -0
  45. package/bridge/src/runtime-client-request-ops.ts +56 -0
  46. package/bridge/src/runtime-client-run-turn.ts +158 -0
  47. package/bridge/src/runtime-client-thread-ops.ts +270 -0
  48. package/bridge/src/runtime-client-transport.ts +309 -0
  49. package/bridge/src/runtime-client-turn-poll.ts +224 -0
  50. package/bridge/src/runtime-client-turn-read.ts +185 -0
  51. package/bridge/src/runtime-client-turn-state.ts +105 -0
  52. package/bridge/src/runtime-client.ts +344 -0
  53. package/bridge/src/runtime-user-input.ts +403 -0
  54. package/bridge/src/scheduler.ts +239 -0
  55. package/bridge/src/server-handoff-command.ts +364 -0
  56. package/bridge/src/server-main.ts +80 -0
  57. package/bridge/src/server-routine-command.ts +60 -0
  58. package/bridge/src/server-routine-execution.ts +222 -0
  59. package/bridge/src/server-runtime-app-support.ts +107 -0
  60. package/bridge/src/server-runtime-app.ts +238 -0
  61. package/bridge/src/server-thread-sync-command.ts +63 -0
  62. package/bridge/src/server.ts +17 -0
  63. package/bridge/src/session-store-delivery.ts +220 -0
  64. package/bridge/src/session-store-human-gate.ts +380 -0
  65. package/bridge/src/session-store-inbound-acceptance.ts +66 -0
  66. package/bridge/src/session-store-meta.ts +134 -0
  67. package/bridge/src/session-store-turn-ledger.ts +272 -0
  68. package/bridge/src/session-store.ts +380 -0
  69. package/bridge/src/system-notify.ts +220 -0
  70. package/bridge/src/thread-sync.ts +200 -0
  71. package/bridge/src/translator.ts +494 -0
  72. package/bridge/src/types.ts +289 -0
  73. package/bridge/src/utils.ts +104 -0
  74. package/bridge/src/work-session-store.ts +471 -0
  75. package/docs/.gitkeep +0 -0
  76. package/docs/architecture/codex-feishu-bridge-proposal.md +2742 -0
  77. package/docs/completed/FEATURE-feishu-markdown-and-reply-support.md +327 -0
  78. package/docs/completed/README.md +21 -0
  79. package/docs/completed/SPEC-approval-autonomy-and-safe-defaults.md +205 -0
  80. package/docs/completed/SPEC-approval-batch-and-strict-reply-shortcuts.md +153 -0
  81. package/docs/completed/SPEC-conversation-noise-reduction-and-busy-input-gate.md +538 -0
  82. package/docs/completed/SPEC-engineering-sop-skillization.md +190 -0
  83. package/docs/completed/SPEC-faithful-bridge-core-thinning-v2.md +376 -0
  84. package/docs/completed/SPEC-faithful-bridge-core-thinning.md +1071 -0
  85. package/docs/completed/SPEC-group-chat-sender-identity.md +301 -0
  86. package/docs/completed/SPEC-middleware-exception-visibility.md +227 -0
  87. package/docs/completed/SPEC-nightly-memory-digest-visibility.md +121 -0
  88. package/docs/completed/SPEC-project-group-chat-human-centered-conversation-mapping.md +326 -0
  89. package/docs/completed/SPEC-remove-cli-persona-bootstrap.md +201 -0
  90. package/docs/developer-workflow.md +49 -0
  91. package/docs/implementation/SPEC-codex-same-machine-session-handoff-implementation.md +239 -0
  92. package/docs/implementation/test-coverage-map.md +363 -0
  93. package/docs/implementation/work-ally-implementation-guide.md +790 -0
  94. package/docs/issues/README.md +10 -0
  95. package/docs/issues/pending/ANALYSIS-ally-premature-recovery-notice-and-task-state-semantics-2026-03-18.md +295 -0
  96. package/docs/issues/resolved/ANALYSIS-approval-waiting-visible-but-approval-artifact-missing-2026-03-16.md +466 -0
  97. package/docs/issues/resolved/ANALYSIS-blocking-state-visible-without-user-actionable-artifact-2026-03-16.md +261 -0
  98. package/docs/issues/resolved/ANALYSIS-codex-app-server-transport-disconnect-semantics-2026-03-14.md +606 -0
  99. package/docs/issues/resolved/ANALYSIS-premature-terminalization-on-fresh-thread-poll-and-object-error-leak-2026-03-16.md +348 -0
  100. package/docs/issues/resolved/ANALYSIS-runtime-turn-delivery-and-recovery-2026-03-14.md +603 -0
  101. package/docs/issues/resolved/ANALYSIS-self-test-gap-approval-waiting-visible-but-approval-artifact-missing-2026-03-16.md +166 -0
  102. package/docs/issues/resolved/ANALYSIS-self-test-gap-blocking-state-visible-without-user-actionable-artifact-2026-03-16.md +186 -0
  103. package/docs/issues/resolved/ANALYSIS-self-test-gap-premature-terminalization-on-fresh-thread-poll-and-object-error-leak-2026-03-16.md +166 -0
  104. package/docs/issues/resolved/REPORT-ally-runtime-turn-delivery-3b42fb8-2026-03-15.md +373 -0
  105. package/docs/manual-acceptance.md +127 -0
  106. package/docs/ops-runbook.md +44 -0
  107. package/docs/planning/FEATURE-memory-system.md +748 -0
  108. package/docs/planning/SPEC-active-turn-steer-and-context-compaction-visibility.md +269 -0
  109. package/docs/planning/SPEC-approval-rules-inheritance-and-local-validation-lane.md +450 -0
  110. package/docs/planning/SPEC-assistant-persona-bootstrap.md +199 -0
  111. package/docs/planning/SPEC-assistant-rename.md +610 -0
  112. package/docs/planning/SPEC-bridge-app-server-protocol-alignment.md +667 -0
  113. package/docs/planning/SPEC-claude-runtime-host-for-work-ally.md +434 -0
  114. package/docs/planning/SPEC-cli-feishu-codex-session-unification.md +236 -0
  115. package/docs/planning/SPEC-codex-same-machine-session-handoff.md +873 -0
  116. package/docs/planning/SPEC-feishu-reaction-shortcuts.md +282 -0
  117. package/docs/planning/SPEC-local-stable-release-boundary.md +166 -0
  118. package/docs/planning/SPEC-managed-thread-entry-and-surface-mobility.md +862 -0
  119. package/docs/planning/SPEC-minimal-bridge-semantics-and-user-visible-surface.md +362 -0
  120. package/docs/planning/SPEC-npm-alpha-distribution-and-install-first-release.md +222 -0
  121. package/docs/planning/SPEC-remove-websocket-runtime-transport.md +364 -0
  122. package/docs/planning/SPEC-runtime-abstraction-phase-1.md +424 -0
  123. package/docs/planning/SPEC-runtime-connection-and-turn-recovery-semantics.md +274 -0
  124. package/docs/planning/SPEC-session-presence-and-state-visibility.md +397 -0
  125. package/docs/planning/SPEC-skill-first-capability-packaging.md +338 -0
  126. package/docs/planning/SPEC-stable-archive-contract.md +456 -0
  127. package/docs/planning/SPEC-supervised-start-boundary.md +127 -0
  128. package/docs/planning/SPEC-user-barrier-reduction-and-activation.md +832 -0
  129. package/docs/planning/ally-next.md +1278 -0
  130. package/docs/planning/assistant-workbench-spec.md +725 -0
  131. package/docs/planning/product-workbench.md +283 -0
  132. package/docs/product-onboarding.md +227 -0
  133. package/docs/product-spec-standard.md +528 -0
  134. package/docs/troubleshooting.md +45 -0
  135. package/docs/user-quickstart.md +46 -0
  136. package/internal/dispatch.sh +95 -0
  137. package/internal/lib/common.sh +1450 -0
  138. package/internal/modules/assistant/manage.sh +1312 -0
  139. package/internal/modules/bootstrap/setup.sh +144 -0
  140. package/internal/modules/config/init-env.sh +10 -0
  141. package/internal/modules/global/manage.sh +154 -0
  142. package/internal/modules/handoff/manage.sh +54 -0
  143. package/internal/modules/mcp/manage.sh +83 -0
  144. package/internal/modules/ops/logs.sh +76 -0
  145. package/internal/modules/routines/manage.sh +55 -0
  146. package/internal/modules/runtime/assistant-autosave.sh +26 -0
  147. package/internal/modules/runtime/restart.sh +6 -0
  148. package/internal/modules/runtime/start.sh +283 -0
  149. package/internal/modules/runtime/status.sh +194 -0
  150. package/internal/modules/runtime/stop.sh +55 -0
  151. package/internal/modules/runtime/supervisor.sh +216 -0
  152. package/internal/modules/runtime/update.sh +26 -0
  153. package/package.json +41 -0
  154. package/runtime/config/.gitkeep +0 -0
  155. package/runtime/host/.gitkeep +0 -0
  156. package/runtime/host/healthcheck-codex-app-server.ts +22 -0
  157. package/runtime/host/ping-pong-codex-app-server.ts +66 -0
  158. package/runtime/host/probe-codex-app-server.ts +115 -0
  159. package/skills/archive-reader/SKILL.md +9 -0
  160. package/skills/feishu-production-debug/SKILL.md +37 -0
  161. package/skills/feishu-production-debug/references/feishu-debug-order.md +49 -0
  162. package/skills/feishu-production-debug/references/platform-permission-baseline.md +23 -0
  163. package/skills/issue-to-spec-triage/SKILL.md +44 -0
  164. package/skills/issue-to-spec-triage/references/triage-rules.md +66 -0
  165. package/skills/memory-digest/SKILL.md +9 -0
  166. package/skills/post-implementation-closure/SKILL.md +39 -0
  167. package/skills/post-implementation-closure/references/closure-checklist.md +45 -0
  168. package/skills/post-implementation-closure/references/doc-drift-map.md +49 -0
  169. package/skills/product-spec/SKILL.md +244 -0
  170. package/templates/env.example +5 -0
  171. package/templates/routines/nightly-memory-digest.yaml +10 -0
  172. package/templates/workspace/AGENTS.md +26 -0
@@ -0,0 +1,862 @@
1
+ # SPEC: Managed Thread Entry And Surface Mobility
2
+
3
+ 更新时间:2026-03-22
4
+ 状态:Draft / Ready for implementation
5
+ Owner:Ally
6
+ 相关文档:
7
+ - `docs/planning/SPEC-codex-same-machine-session-handoff.md`
8
+ - `docs/implementation/SPEC-codex-same-machine-session-handoff-implementation.md`
9
+ - `docs/planning/SPEC-desktop-phase2-onboarding-and-distribution.md`
10
+ - `docs/planning/SPEC-minimal-bridge-semantics-and-user-visible-surface.md`
11
+ - `docs/architecture/codex-feishu-bridge-proposal.md`
12
+
13
+ ## 1. Summary
14
+
15
+ 这份 spec 要把当前已经成立的 same-machine handoff,升级成一套更完整、可被普通用户稳定理解的“受管线程入口与跨表面连续性”产品合同。
16
+
17
+ 当前已经成立的基础是:
18
+
19
+ - `work_session` 已经是 durable 产品对象
20
+ - Feishu 单聊里已有 `/new`、`/codex`、`/takeover`
21
+ - 本机 CLI 已有 `handoff codex|attach`
22
+ - linked / attached thread 已可在官方 Codex CLI 与渠道侧之间切换
23
+ - assistant 专属 `CODEX_HOME` 已经是 continuity 的真实边界;只有落在这个边界内的 thread 才属于“平滑切换”讨论范围
24
+
25
+ 但当前产品面仍有 5 个关键缺口:
26
+
27
+ 1. `work-ally` 还没有给用户一个正式的“在官方 Codex CLI 一侧新开受管线程”的入口。
28
+ 2. CLI 门面仍然偏实现导向,没有收成 `new / continue / attach` 这类用户意图导向动作。
29
+ 3. 当前 spec 仍默认“单 assistant 只有一个 active 线程”,没有把“同一 assistant 下可存在多条受管线程”时的选择机制收成正式合同。
30
+ 4. 当前产品还没有明确区分“候选集合”和“默认高亮线程”,容易让实现滑回静默猜测。
31
+ 5. 当前产品没有把“某个可见表面稳定绑定到哪一条 thread”写成正式合同,导致实现仍可能把新出现的 thread 误当成当前私聊窗口的新默认目标。
32
+
33
+ 因此这次要收口的,不是一两个命令,而是 6 层产品模型:
34
+
35
+ 1. 线程治理状态:`managed` / `unmanaged`
36
+ 2. 线程选择对象:`thread handle`
37
+ 3. 线程选择规则:`candidate set` / `default pointer`
38
+ 4. 交付表面绑定:`delivery binding`
39
+ 5. 用户动作:`new` / `continue` / `attach` / `threads`
40
+ 6. 工作表面:Feishu / official Codex CLI / Desktop
41
+
42
+ 这份 spec 的核心拍板有 9 个:
43
+
44
+ 1. 正式引入 `managed thread` 与 `unmanaged thread` 的产品区分。
45
+ 2. 正式引入 `thread handle` 作为用户可见的线程选择句柄,而不是让用户直接使用官方底层 thread id。
46
+ 3. 把用户动作收口为 `new / continue / attach / threads` 四类,而不是暴露 `handoff codex` 这类实现术语。
47
+ 4. `continue` 与 `/takeover` 都必须支持“唯一候选时可省参数;多候选时必须显式选 handle”。
48
+ 5. `threads` 是正式合同,不是附属便利功能。
49
+ 6. `default pointer` 只能用于高亮、排序和默认落点,不能在多候选场景下替用户做决定。
50
+ 7. 中间层默认假设“同一 assistant 下会同时存在多条可见 thread”,这不是异常分支,而是默认产品现实。
51
+ 8. `attach` 只改变 thread 是否受管与是否进入候选集合,不得静默改写任何已有 delivery surface 的当前绑定。
52
+ 9. 明确写死一个边界:用户直接在项目目录运行官方 `codex`,默认只有项目 thread 语义,不自动拥有 assistant 身份,也不自动进入 continuity 合同;某个私聊/群聊窗口若已绑定线程,也不会因为另一侧新开 thread 而被静默改绑。
53
+ 10. continuity baseline 只覆盖 assistant 专属 `CODEX_HOME` 下的 thread;裸 `codex` 若不在该 assistant 的 `CODEX_HOME` 边界内,不属于本专题考虑范围。
54
+
55
+ 能力类型判断:这是 `Core product contract`,不是 Skill。
56
+
57
+ 原因:它改变的是线程对象、用户入口、连续性承诺、参数模型、三端门面与长期产品语义,属于内核级产品合同。
58
+
59
+ ## 2. 背景
60
+
61
+ ### 2.1 已落地基线
62
+
63
+ 截至 2026-03-22,`work-ally` 已经具备如下稳定基础:
64
+
65
+ - assistant desk、独立 `CODEX_HOME`、项目绑定与作用域命令已成立
66
+ - same-machine handoff 已通过产品验收
67
+ - `work_session` 已是 durable 产品对象,而不再只是 conversation 到 thread 的临时映射
68
+ - `handoff codex|attach`、`/codex`、linked / attached `/takeover` 已有实现与验证
69
+ - 桌面端已经作为正式包装入口成立
70
+
71
+ 这意味着这次不是从零设计 continuity,而是在已落地主合同之上,把入口、选择机制与用户心智补齐。
72
+
73
+ ### 2.2 当前真正的产品缺口
74
+
75
+ 现在最大的产品缺口,不是“不能续上旧线程”,而是:
76
+
77
+ - 用户还不能从 `work-ally` 的正式入口,直接在官方 Codex CLI 一侧新开一条受管线程
78
+ - 如果用户习惯在 assistant 专属 `CODEX_HOME` 下直接跑 `codex`,仍会不断产生“已在 continuity 边界内、但尚未受管”的 thread
79
+ - 如果用户直接用全局 / 默认 `CODEX_HOME` 裸跑 `codex`,这些 thread 根本不在 continuity baseline 内,产品需要明确把它排除,而不是给出模糊承诺
80
+ - 即便 thread 已经是受管的,当前产品面也还没有把“如何在多条线程之间显式选中一条”收成正式合同
81
+ - 当前实现层已有 active index,但产品层没有把它降格成“默认指针”,仍有被误解为“自动选择依据”的风险
82
+ - 当前也没有正式规定“Feishu 私聊、Feishu 群聊、Desktop、CLI 各自当前绑定的是哪一条 thread,以及什么时候允许改绑”
83
+
84
+ 这会导致四个后果:
85
+
86
+ 1. continuity 仍然像补救能力,而不是默认主路径。
87
+ 2. 一旦同一 assistant 下存在多条受管线程,`continue` / `/takeover` 就会立刻失去清晰语义。
88
+ 3. 即使 spec 表面上补了参数,工程仍可能偷偷使用默认指针自动猜线程。
89
+ 4. 某个已经聊到一半的私聊窗口,可能因为用户在另一处新开 thread 而被静默切到别的 thread,破坏用户对“当前窗口还在聊哪条线”的基本预期。
90
+
91
+ ### 2.3 这次真正要统一的是什么
92
+
93
+ 这次不是补一条命令,而是统一 6 层结构:
94
+
95
+ 1. `线程治理状态`
96
+ - 这条 thread 是受管还是未受管
97
+ 2. `线程选择对象`
98
+ - 用户到底在选哪一条 thread
99
+ 3. `线程选择规则`
100
+ - 当前动作的候选集合是什么,默认指针能做什么、不能做什么
101
+ 4. `交付表面绑定`
102
+ - 某个具体窗口当前稳定绑定的是哪一条 thread,什么动作才允许改绑
103
+ 5. `用户动作`
104
+ - 我是要新开、继续、纳管,还是列出候选
105
+ 6. `工作表面`
106
+ - 我现在是在渠道侧、官方 Codex CLI,还是桌面端控制面
107
+
108
+ 只有按这 6 层自上而下收口,整份 spec 才不会沦为东补一个参数、西补一个命令的拼接文档。
109
+
110
+ ## 3. 问题定义
111
+
112
+ 当前主要有 8 个问题。
113
+
114
+ ### 3.1 continuity 当前更像“切换出口”,不是“线程入口体系”
115
+
116
+ 当前产品面强调的是:
117
+
118
+ - `/codex`
119
+ - `/takeover`
120
+ - `handoff codex`
121
+ - `handoff attach`
122
+
123
+ 这套能力已经能工作,但仍偏向“当你已经有一条线程时,怎么切过去或接回来”。
124
+
125
+ 它还没有回答清楚:
126
+
127
+ - 一条新的 assistant 工作线程,标准上应该从哪里开始
128
+ - 用户在电脑前如果想开新活,为什么要先裸跑官方 `codex`
129
+ - 什么时候应该走受管入口,什么时候才需要 attach
130
+
131
+ ### 3.2 continuity 的真实边界是 assistant 专属 `CODEX_HOME`
132
+
133
+ 这条边界必须明确:
134
+
135
+ - 用户若在全局 / 默认 `CODEX_HOME` 下直接运行 `codex`,官方 CLI 只看到项目和它自己的线程
136
+ - 这类 thread 不属于本专题 continuity baseline,`work-ally` 不对其承诺平滑切换
137
+ - 只有在 assistant 专属 `CODEX_HOME` 下启动出来的 thread,才属于“可被纳入 continuity 合同”的真实候选边界
138
+ - 因此,类似 `CODEX_HOME='<assistant-codex-home>' codex` 的启动方式,才是“直接在官方 Codex CLI 一侧工作,但仍落在当前 assistant continuity 范围内”的基准场景
139
+
140
+ 所以如果产品不提供正式的“受管新开入口”,用户很容易长期停留在“assistant-scoped 但未受管”的 thread 世界里工作。
141
+
142
+ ### 3.3 CLI 门面当前仍暴露实现术语
143
+
144
+ `handoff codex` 对实现者是准确的,但对普通用户并不直观。
145
+
146
+ 用户真正想表达的是:
147
+
148
+ - 我要开一条新的工作线程
149
+ - 我要继续上一条工作线程
150
+ - 我要把这条外部线程收进来
151
+ - 我要看看当前有哪些线程可选
152
+
153
+ 而不是:
154
+
155
+ - 我要执行 handoff
156
+ - 我要 handoff 到 codex
157
+ - 我要分辨 `session`、`surface`、`handoff` 哪个词才对
158
+
159
+ ### 3.4 当前 spec 默认“单 assistant 只有一个 active 线程”,这不成立
160
+
161
+ 一旦同一个 assistant 下可能同时存在多条受管线程,下面这些动作都会立即变得不充分:
162
+
163
+ - `/takeover`
164
+ - `continue`
165
+ - 桌面端的 Continue CTA
166
+
167
+ 因为没有参数时,系统根本不知道你要接哪一条。
168
+
169
+ ### 3.5 不能要求用户直接抄官方底层 thread id
170
+
171
+ 即便官方 Codex CLI 历史里有 thread id,这个 id 也不适合直接作为产品主参数。原因包括:
172
+
173
+ - `work-ally` 内部本来就区分 `runtimeThreadId` 与 `cliResumeRef`
174
+ - 用户真正想选择的是“哪条受管工作线程”,而不是底层 runtime 主键
175
+ - Feishu 用户也不方便去 CLI 历史里抄内部 id
176
+
177
+ 所以产品必须定义自己的用户可见选择句柄。
178
+
179
+ ### 3.6 当前缺少正式的线程列表能力
180
+
181
+ 既然多线程场景下需要显式选择,就必须有正式的列表入口。
182
+
183
+ 如果没有:
184
+
185
+ - 用户拿不到 handle
186
+ - `/takeover <handle>` 只是纸面命令
187
+ - CLI 和 Desktop 也无法提供稳定选择路径
188
+
189
+ 所以 `threads` 必须被视为主合同组成,而不是附属便利功能。
190
+
191
+ ### 3.7 当前没有写死“默认指针不能替用户选”
192
+
193
+ 当前实现层已经有 assistant 作用域下的 active index 或等价指针。
194
+
195
+ 如果 spec 不明确它的边界,工程很容易滑回:
196
+
197
+ - “那就默认选 activeWorkSessionId 吧”
198
+ - “那就默认选最近线程吧”
199
+
200
+ 这会让 spec 表面上有 `thread handle`,实际产品仍在偷偷猜。
201
+
202
+ ### 3.8 当前没有把 delivery surface 的稳定绑定写成正式合同
203
+
204
+ 这次调查已经说明,真正出错的不只是“多线程选择不清楚”,而是:
205
+
206
+ - 中间层实际上能看到多条 thread
207
+ - 这些 thread 可能分别来自 Feishu 私聊、Feishu 群聊、Desktop,或者 assistant 对应 `CODEX_HOME` 下新开的官方 CLI thread
208
+ - 但产品层没有正式规定“某个具体聊天窗口当前绑定哪条 thread,以及什么动作才允许改绑”
209
+
210
+ 如果这层合同不写死,工程就会滑回一种错误默认:
211
+
212
+ - 以为 assistant 下始终只会有一条当前 thread
213
+ - 或者把“最近创建 / 最近活跃 / 当前 default pointer”误当成所有 surface 的默认目标
214
+
215
+ 这会直接破坏用户预期:
216
+
217
+ 1. 私聊窗口本来已经在和 thread A 工作,却因为另一处新开了 thread B 而被静默切走。
218
+ 2. 群聊与私聊本应可以并行绑定不同 thread,但实现会错误共享一个“全局当前线程”。
219
+ 3. `attach` 本应只是把外部 thread 纳入管理,却可能被误做成“顺便把当前聊天窗口改绑到它”。
220
+
221
+ ## 4. 目标 / 非目标
222
+
223
+ ### 4.1 目标
224
+
225
+ 1. 定义 `managed thread` 与 `unmanaged thread` 的稳定产品合同。
226
+ 2. 定义 `thread handle` 作为用户可见的线程选择对象。
227
+ 3. 定义 `candidate set` 与 `default pointer` 的稳定产品语义。
228
+ 4. 把用户动作收敛为 `new / continue / attach / threads` 四类,而不是暴露内部实现术语。
229
+ 5. 为 CLI 增加“新开受管 thread”的正式入口,并使其成为电脑前工作的推荐主路径。
230
+ 6. 让 `continue`、`/takeover`、桌面端 Continue 都在多线程场景下仍有明确、可验证的选择规则。
231
+ 7. 让 Feishu、CLI、桌面端围绕同一套受管 thread contract 工作。
232
+ 8. 明确 `attach` 是补救路径,不是默认主路径。
233
+ 9. 保持与当前 same-machine handoff 主合同兼容,不推翻已落地对象与存储。
234
+
235
+ ### 4.2 非目标
236
+
237
+ 1. 不重做 `work_session` 底层对象。
238
+ 2. 不把 V1 扩到 desktop app、IDE、第三方包装层的全表面接续承诺。
239
+ 3. 不把“在 assistant 专属 `CODEX_HOME` 之外直接打开的官方 `codex`”自动解释成 assistant continuity 范围内线程。
240
+ 4. 不做 transcript 全量镜像。
241
+ 5. 不在 V1 一次性把 Feishu 群聊的 continuity 命令面做成与单聊完全对齐;本专题先写死它与单聊一致遵守 stable delivery binding 语义。
242
+ 6. 不要求三端命令词必须完全一致。
243
+ 7. 不让用户直接以 `runtimeThreadId` 作为正式主心智。
244
+
245
+ ## 5. 产品定义
246
+
247
+ ### 5.1 一句话定义
248
+
249
+ > `work-ally` 把一条工作线程定义成可受管对象,并通过统一的 `thread handle + candidate selection + new / continue / attach / threads` 合同,让用户能在渠道侧与官方 Codex CLI 之间创建、选中、续上或纳管这条线程。
250
+
251
+ ### 5.2 核心对象
252
+
253
+ #### A. `managed thread`
254
+
255
+ 满足以下条件之一的 thread:
256
+
257
+ - 由 `work-ally` 正式入口创建
258
+ - 或者由 `work-ally` 受管 surface 的首条自然语言消息自动 materialize
259
+ - 或者已通过 `attach` 被纳入 `work-ally` 管理
260
+
261
+ 其产品特征包括:
262
+
263
+ - 绑定 assistant
264
+ - 绑定 workspace root
265
+ - 绑定 assistant 的 `CODEX_HOME`
266
+ - 存在正式 `work_session` durable object
267
+ - 可参与 continuity 合同
268
+ - 有用户可见的 `thread handle`
269
+
270
+ #### B. `assistant-scoped unmanaged thread`
271
+
272
+ 指用户在某个 assistant 的专属 `CODEX_HOME` 下直接运行官方 `codex` 后产生、但尚未被 `work-ally` 纳管的 thread。
273
+
274
+ 其产品特征包括:
275
+
276
+ - 已落在当前 assistant continuity 边界内
277
+ - 尚未拥有正式 `work_session`
278
+ - 不自动参与 managed-thread 选择与切换合同
279
+ - 若要进入 continuity,必须先 `attach`
280
+
281
+ #### C. `out-of-scope external Codex thread`
282
+
283
+ 指不在当前 assistant 专属 `CODEX_HOME` 下产生的官方 Codex thread。
284
+
285
+ 其产品语义明确为:
286
+
287
+ - 不属于本专题 continuity baseline
288
+ - 不要求出现在 `attach / continue / threads / takeover` 的候选集合中
289
+ - 即使用户后来切回某个 assistant surface,也不应被系统隐式认成“这个 assistant 的 thread”
290
+
291
+ #### D. `thread handle`
292
+
293
+ `thread handle` 是用户可见、可输入、可在三端流转的线程句柄。
294
+
295
+ 它的职责是:
296
+
297
+ - 标识“用户想选的那条受管线程”
298
+ - 作为 `continue`、`/takeover` 等显式选择参数
299
+ - 屏蔽底层 `runtimeThreadId` / `cliResumeRef` 的实现差异
300
+
301
+ 产品要求:
302
+
303
+ 1. `thread handle` 必须稳定且可复制。
304
+ 2. `thread handle` 必须由 `work-ally` 生成与展示,不要求用户去官方 Codex 历史里抄底层 id。
305
+ 3. `thread handle` 必须与 assistant 作用域绑定,避免跨 assistant 误用。
306
+ 4. `thread handle` 应短、可读,适合在 Feishu 中输入。
307
+
308
+ V1 在这里直接拍板:
309
+
310
+ - 每条 `work_session` 新增持久化字段 `threadHandle`
311
+ - `threadHandle` 在创建 `work_session` 时一次性生成,并在整个生命周期内不可变
312
+ - `threadHandle` 在 assistant 作用域内唯一,且归档后也不得复用
313
+ - `threadHandle` 使用短、可输入的 ASCII 小写格式,允许在 Feishu 与 CLI 中直接复制粘贴
314
+ - 现有历史 `work_session` 需要做一次性 backfill,把 handle 补写到 durable metadata 中
315
+
316
+ V1 不强制具体编码算法,但强制要求:
317
+
318
+ - 它不是官方底层 thread id 的直接裸透出
319
+ - 它不是用户必须理解的内部字段名
320
+ - 它必须是 persisted field,而不是运行时临时派生值
321
+
322
+ #### E. `candidate set`
323
+
324
+ `candidate set` 指某个动作上下文里,系统判断“当前可供继续或接回”的 managed thread 候选集合。
325
+
326
+ 它是动作相关概念,不等于 assistant 下所有历史线程。
327
+
328
+ 最小要求:
329
+
330
+ 1. 候选必须属于当前 assistant。
331
+ 2. 候选必须是 managed thread。
332
+ 3. 候选不得是已关闭或已归档线程。
333
+ 4. 候选是否可用,由该动作自己的能力约束决定。
334
+
335
+ 例如:
336
+
337
+ - `ally continue` 的候选,需要满足 CLI 继续条件。
338
+ - `/takeover` 的候选,需要满足渠道侧接回条件。
339
+
340
+ #### F. `default pointer`
341
+
342
+ `default pointer` 指 assistant 作用域下系统记录的“最近焦点线程”或“默认高亮线程”。
343
+
344
+ 它的职责仅限于:
345
+
346
+ - 作为列表排序参考
347
+ - 作为 detail 默认落点
348
+ - 作为唯一候选时的自然默认值
349
+
350
+ 它明确不是:
351
+
352
+ - 多候选场景下的静默自动选择依据
353
+ - 用户无需知情的隐式抢答规则
354
+
355
+ 只要当前动作的 `candidate set` 大于 1,`default pointer` 就不能替代用户显式选择。
356
+
357
+ #### G. `delivery binding`
358
+
359
+ `delivery binding` 指某个用户可见交付表面当前稳定绑定到哪一条 managed thread。
360
+
361
+ 这里的交付表面至少包括:
362
+
363
+ - Feishu 私聊窗口
364
+ - Feishu 群聊窗口
365
+ - Desktop 中某个 assistant surface
366
+
367
+ 它的职责是:
368
+
369
+ - 明确“这个窗口现在默认继续的是哪条 thread”
370
+ - 让自然语言 follow-up 在该 surface 上继续同一条工作线
371
+ - 阻止别处新出现的 thread 静默抢走当前窗口
372
+
373
+ 产品要求:
374
+
375
+ 1. 同一 assistant 下默认允许存在多条可见 thread,`delivery binding` 负责把“这个窗口当前是哪一条”固定下来。
376
+ 2. 私聊、群聊是不同 surface,必须允许同时绑定不同 thread。
377
+ 3. 某个 surface 一旦已绑定到 thread A,另一侧新开 / continue / attach thread B,不得自动改写这个 surface 的绑定。
378
+ 4. 改绑必须来自显式动作,例如该 surface 上的 `/new`、`/takeover <handle>`、Desktop picker 明确切换等。
379
+ 5. `attach` 只改变 thread 的治理状态与可选性;它本身不等于改绑。
380
+
381
+ ### 5.3 用户动作模型
382
+
383
+ #### `new`
384
+
385
+ 含义:
386
+
387
+ - 新开一条受管 thread
388
+ - 不复用现有 active thread
389
+ - 从一开始就带 assistant 身份、continuity 资格和 `thread handle`
390
+
391
+ 产品拍板:
392
+
393
+ - `new` 是跨表面的同一类用户动作,而不是 CLI 独占语义
394
+ - 在 CLI 中,`new` 的正式入口是 `ally new --assistant <name>`
395
+ - 在 Feishu 中,`new` 的正式入口是 `/new`
396
+ - 这两个入口都会创建新的 managed thread;不能把 Feishu `/new` 降格成“临时会话控制命令”
397
+
398
+ #### `continue`
399
+
400
+ 含义:
401
+
402
+ - 继续某一条 managed thread
403
+ - 若候选唯一,可省参数
404
+ - 若候选不唯一,必须显式指定 `thread handle`
405
+
406
+ #### `attach`
407
+
408
+ 含义:
409
+
410
+ - 把一条 unmanaged thread 纳管成 managed thread
411
+ - attach 完成后,系统为其生成正式 `thread handle`
412
+ - attach 本身不是 continue,也不是 new
413
+
414
+ #### `threads`
415
+
416
+ 含义:
417
+
418
+ - 展示当前 assistant 作用域下、与当前合同相关的可选 managed thread
419
+ - 为多线程场景提供正式选择入口
420
+
421
+ ## 6. 选择模型与默认规则
422
+
423
+ 这是本专题新增的核心层,优先级高于具体命令面。
424
+
425
+ ### 6.1 统一选择规则
426
+
427
+ 对任何“继续某条线程”的动作,都采用同一套规则:
428
+
429
+ 1. 先计算当前动作上下文下的 `candidate set`。
430
+ 2. 若候选数为 0,则明确失败,并提示下一步动作。
431
+ 3. 若候选数为 1,则允许无参数继续。
432
+ 4. 若候选数大于 1,则必须显式指定 `thread handle`。
433
+
434
+ 这条规则适用于:
435
+
436
+ - `ally continue`
437
+ - Feishu `/takeover`
438
+ - Desktop `Continue Thread`
439
+
440
+ ### 6.2 默认指针规则
441
+
442
+ 系统可以继续保留 assistant 作用域下的默认线程指针,例如当前实现中的 active work-session index,但它的产品语义必须收窄为:
443
+
444
+ 1. 表示“最近焦点线程”或“默认展示线程”。
445
+ 2. 不表示“唯一合法继续目标”。
446
+ 3. 不得在多候选场景下绕过显式选择。
447
+
448
+ 换句话说:
449
+
450
+ - `default pointer` 可以影响展示顺序
451
+ - 不能决定 `continue / takeover` 在多候选场景下替用户选哪一条
452
+
453
+ ### 6.3 列表能力
454
+
455
+ 既然多线程场景下需要显式选择,就必须提供正式的“可选线程列表”能力。
456
+
457
+ 因此产品必须提供:
458
+
459
+ - CLI:`ally threads --assistant <name>`
460
+ - Feishu:`/threads`
461
+ - Desktop:assistant detail 中的 thread 列表或 picker
462
+
463
+ 这三个入口展示的是同一类对象:当前 assistant 下、在当前产品合同里可被 continue / takeover 的 managed threads。
464
+
465
+ 每条列表项至少应包含:
466
+
467
+ - `thread handle`
468
+ - 简短标题或最近 prompt 摘要
469
+ - 最近活跃时间
470
+ - 当前 active surface
471
+ - 是否来自 attach
472
+ - 是否为当前 default pointer
473
+
474
+ ### 6.4 delivery binding 规则
475
+
476
+ 中间层对多线程的默认认识必须是:
477
+
478
+ 1. 同一 assistant 下会同时存在多条可见 thread,这不是例外。
479
+ 2. Feishu 私聊、Feishu 群聊、Desktop、CLI 都可能同时观察到这些 thread,但“可见”不等于“当前表面就要改绑到它”。
480
+ 3. 某个 delivery surface 若已绑定 thread A,则普通自然语言 follow-up 默认继续 thread A。
481
+ 4. 另一处创建、继续、attach 或恢复了 thread B,只会改变候选集合与可见性,不会自动改写当前 surface 的绑定。
482
+ 5. surface 改绑只能来自显式动作,而不是 recent / active / default pointer 推断。
483
+
484
+ 对 Feishu 来说,这条规则尤其要写死:
485
+
486
+ - 私聊窗口绑定的 thread 在没有显式 `/new`、`/takeover <handle>` 或同等级切换动作前,不得变化。
487
+ - 群聊窗口也遵守同样规则,但它和私聊窗口各自独立。
488
+
489
+ ### 6.5 参数来源规则
490
+
491
+ `thread handle` 的来源必须是 `work-ally` 自己提供,而不是要求用户去官方 Codex 历史里抄参数。
492
+
493
+ 正式来源包括:
494
+
495
+ 1. `new` 完成后的回显
496
+ 2. `attach` 完成后的回显
497
+ 3. `threads` 列表
498
+ 4. Desktop thread picker
499
+
500
+ ## 7. 三端门面原则
501
+
502
+ ### 7.1 CLI
503
+
504
+ CLI 以“用户意图”命名。
505
+
506
+ 推荐正式入口:
507
+
508
+ - `ally new --assistant <name>`
509
+ - `ally continue --assistant <name> [--thread <handle>]`
510
+ - `ally attach --assistant <name> --last|--thread ...|--resume-ref ...`
511
+ - `ally threads --assistant <name>`
512
+
513
+ 理由:
514
+
515
+ - 在本机 CLI 场景里,用户执行这组命令的默认目标本来就是官方 Codex CLI
516
+ - 因此不必让用户再额外输入 `codex`
517
+ - 不暴露 `handoff`、`surface`、`session` 等内部实现术语
518
+
519
+ ### 7.2 Feishu
520
+
521
+ Feishu 仍以“当前在这里,接下来切到哪里”命名。
522
+
523
+ 当前单聊正式保留并扩展为:
524
+
525
+ - `/new`
526
+ - `/codex`
527
+ - `/takeover [thread-handle]`
528
+ - `/threads`
529
+
530
+ 群聊在 V1 不要求与单聊一次性补齐同样的命令面,但必须遵守相同的 surface binding 合同。
531
+
532
+ 规则:
533
+
534
+ - `/takeover` 无参数时,只在唯一候选下成立
535
+ - 多候选时,系统返回候选列表摘要,并提示先 `/threads` 再 `/takeover <handle>`
536
+ - `/codex` 始终只导出“当前 Feishu surface 已绑定的那条 managed thread”
537
+ - `/codex` 不读取 assistant default pointer,也不对多条 managed thread 做候选猜测
538
+ - 若当前 Feishu surface 尚未绑定任何 managed thread,则 `/codex` 明确失败,并提示先通过首条自然语言消息、`/new` 或 `/takeover <handle>` 让该 surface 获得绑定
539
+ - Feishu 私聊和群聊是不同 delivery surface;即便它们看到的是同一 assistant,也必须允许分别绑定不同 thread
540
+ - 在官方 Codex CLI 或其他 surface 新开 / continue / attach 了一条 thread,不得静默改写当前 Feishu 会话的绑定
541
+ - Feishu 会话的改绑必须来自当前会话里的显式动作,例如 `/new` 或 `/takeover <handle>`
542
+
543
+ ### 7.3 Desktop
544
+
545
+ 桌面端以按钮 CTA + picker 命名。
546
+
547
+ 推荐:
548
+
549
+ - `New Thread`
550
+ - `Continue Thread`
551
+ - `Attach Existing Thread`
552
+ - `Threads`
553
+
554
+ 说明:
555
+
556
+ - 若当前只有唯一可继续候选,Continue 可直接执行
557
+ - 若有多个候选,Continue 应先弹出 picker,而不是猜
558
+
559
+ ## 8. 关键用户路径
560
+
561
+ ### 8.1 路径 A:在 CLI 中新开一条受管 thread
562
+
563
+ 1. 用户执行 `ally new --assistant pm`。
564
+ 2. 系统使用该 assistant 的 desk、workspace 与 `CODEX_HOME`。
565
+ 3. 系统创建新的 `work_session`。
566
+ 4. 系统生成并回显该线程的 `thread handle`。
567
+ 5. 官方 Codex CLI 进入新的 thread。
568
+ 6. 后续若用户回到 Feishu,可通过 `/takeover <handle>` 或在唯一候选时直接 `/takeover` 接回。
569
+
570
+ ### 8.2 路径 B:在 CLI 中继续一条受管 thread
571
+
572
+ 1. 用户执行 `ally continue --assistant pm`。
573
+ 2. 系统先计算 CLI 场景下的 `candidate set`。
574
+ 3. 若候选唯一,系统直接进入该 thread。
575
+ 4. 若候选不唯一,系统拒绝并提示先执行 `ally threads`,再用 `--thread <handle>` 重试。
576
+
577
+ ### 8.3 路径 C:从 CLI 出门后,在 Feishu 中接回对应 thread
578
+
579
+ 1. 用户此前通过受管入口在官方 Codex CLI 一侧工作。
580
+ 2. 系统先计算 Feishu `/takeover` 上下文下的 `candidate set`。
581
+ 3. 若候选唯一,用户发 `/takeover`,系统直接接回。
582
+ 4. 若存在多条候选,用户先发 `/threads` 查看 handles。
583
+ 5. 用户再发 `/takeover <handle>`。
584
+ 6. 系统接回该 handle 对应的同一条 managed thread。
585
+
586
+ ### 8.4 路径 D:先裸跑官方 Codex,再纳管
587
+
588
+ 1. 用户在 assistant 对应的专属 `CODEX_HOME` 下直接运行官方 `codex`。
589
+ 2. 产生一条 assistant-scoped unmanaged thread。
590
+ 3. 用户执行 `ally attach --assistant pm --last` 或显式 selector。
591
+ 4. 系统纳管后创建正式 `work_session`,并生成 `thread handle`。
592
+ 5. 之后用户可 `ally continue --thread <handle>`,也可在 Feishu 里 `/takeover <handle>`。
593
+
594
+ ### 8.5 路径 E:Feishu 首次自然语言开聊,之后再 `/codex`
595
+
596
+ 1. 用户在 Feishu 私聊窗口里直接发第一条普通自然语言消息,没有先 `/new`。
597
+ 2. 系统在 assistant 对应 runtime 中启动 thread,并立刻为该 surface materialize 一条正式 `work_session`。
598
+ 3. 该 surface 的 `delivery binding` 立即指向这条新 thread。
599
+ 4. 之后用户发送 `/codex` 时,系统直接导出这条已绑定 thread 的接续命令。
600
+ 5. 这个场景不要求用户先补一次 `/new` 才能进入 continuity。
601
+
602
+ ### 8.6 路径 F:Feishu 私聊已绑定 thread A,用户又在 CLI 新开 thread B
603
+
604
+ 1. 用户此前已经在 Feishu 私聊窗口中与 thread A 连续工作。
605
+ 2. 用户随后在 assistant 对应的 `CODEX_HOME` 下,通过正式入口或 attach 让 thread B 进入 managed world。
606
+ 3. 中间层此时能看见 A 与 B 两条 thread。
607
+ 4. 但这个私聊窗口的 `delivery binding` 仍然指向 A。
608
+ 5. 用户若直接在该私聊窗口里继续发自然语言,系统默认继续 A,而不是因为 B 更新更近就静默切过去。
609
+ 6. 只有当用户在这个私聊窗口里显式执行 `/takeover <handle-of-B>`、`/new` 或其他正式切换动作时,绑定才允许从 A 改到 B。
610
+
611
+ ## 9. 机制设计
612
+
613
+ ### 9.1 真相源分工
614
+
615
+ #### 官方 Codex 持有
616
+
617
+ - 底层 thread 生命周期
618
+ - thread 历史与 runtime 状态
619
+
620
+ #### `work-ally` 持有
621
+
622
+ - `work_session`
623
+ - 线程是否受管
624
+ - `thread handle`
625
+ - `candidate set` 的计算规则
626
+ - assistant / workspace / delivery conversation 绑定
627
+ - delivery surface 当前绑定到哪条 thread
628
+ - active surface
629
+ - attach / continue / export / listing 可用性
630
+
631
+ ### 9.2 数据模型拍板
632
+
633
+ 当前不新增新的平行对象层。
634
+
635
+ 继续沿用:
636
+
637
+ - `work_session` 作为 durable 产品对象
638
+ - `activeSurface`、`ownershipSource`、`runtimeThreadId`、`cliResumeRef` 等现有字段合同
639
+
640
+ 但必须在产品与实现层补齐并拍板:
641
+
642
+ 1. `threadHandle` 作为 `work_session` 的持久化字段
643
+ 2. assistant 作用域下的可列出线程索引
644
+ 3. continue / takeover 的多候选判定与拒绝逻辑
645
+ 4. assistant 作用域 default pointer 的语义收窄
646
+ 5. delivery surface 到 managed thread 的稳定绑定索引
647
+
648
+ 这里明确两条实现边界:
649
+
650
+ - 当前 assistant index 仍可保留 `activeWorkSessionId` 或等价字段
651
+ - 但它在本专题里只代表 default pointer,不再代表“多线程场景下唯一自动选中的线程”
652
+ - delivery surface binding 与 assistant default pointer 是两套不同语义;自然语言在某个已绑定 surface 上的去向,以该 surface 的 binding 为准,而不是以 assistant 作用域最近线程为准
653
+ - `delivery binding` 索引继续使用独立 surface / conversation 维度记录,不与 assistant default pointer 混用;这不是待定项
654
+
655
+ ### 9.3 CLI 正式入口合同
656
+
657
+ 新增顶层命令:
658
+
659
+ - `ally new --assistant <name>`
660
+ - `ally continue --assistant <name> [--thread <handle>]`
661
+ - `ally attach --assistant <name> ...`
662
+ - `ally threads --assistant <name>`
663
+
664
+ 合同要求:
665
+
666
+ 1. 这些入口必须是正式用户入口,而不是仅在文档里提 alias。
667
+ 2. 它们可以在实现层复用现有 `handoff` service 或 shell module。
668
+ 3. `handoff codex|attach` 可保留为内部兼容层,但不再作为首推门面。
669
+ 4. `ally continue` 不可在多候选时偷猜,也不可在零候选时偷新开。
670
+
671
+ ### 9.4 `new` 的机制要求
672
+
673
+ `new` 在不同表面上词面可以不同,但机制合同一致。
674
+
675
+ CLI 侧的 `ally new --assistant <name>` 与 Feishu 侧的 `/new` 都必须:
676
+
677
+ 1. 在 assistant 对应的 `CODEX_HOME` 下启动官方 Codex CLI。
678
+ 2. 以 assistant 当前绑定的 workspace 作为工作目录。
679
+ 3. 创建新的正式 `work_session`。
680
+ 4. 生成 `thread handle` 并立即回显。
681
+ 5. 使该线程后续可被 `/takeover` 正确接回。
682
+
683
+ 额外约束:
684
+
685
+ 6. Feishu `/new` 与 CLI `ally new` 在产品语义上等价,差别只在当前用户所处 surface,不在于线程是否“正式受管”。
686
+
687
+ ### 9.5 `continue` 的机制要求
688
+
689
+ `ally continue --assistant <name> [--thread <handle>]` 必须:
690
+
691
+ 1. 无参数时先计算 `candidate set`,再按统一选择规则处理。
692
+ 2. 有参数时只解析对应 handle。
693
+ 3. 找不到 handle 时返回明确错误。
694
+ 4. 不使用“复制摘要新开 thread”作为 fallback。
695
+ 5. 不允许在多候选场景下用 `default pointer` 偷代用户选择。
696
+
697
+ ### 9.6 `attach` 的机制要求
698
+
699
+ 保持当前 attach 主合同,并补充:
700
+
701
+ 1. attach 成功后必须生成正式 `thread handle`。
702
+ 2. attach 结果必须回显这个 handle。
703
+ 3. attach 仍只允许在 assistant 对应 `CODEX_HOME` 边界内发现候选。
704
+ 4. assistant 专属 `CODEX_HOME` 之外的官方 Codex thread 不在候选范围内。
705
+ 5. 仍必须做 workspace 精确匹配与歧义拒绝。
706
+
707
+ ### 9.7 Feishu 首条自然语言的纳管要求
708
+
709
+ 对 Feishu 私聊这类受管 delivery surface,系统必须满足:
710
+
711
+ 1. 若该 surface 尚未绑定任何 managed thread,首条普通自然语言消息一旦触发 thread 启动,就必须立即 materialize 正式 `work_session`。
712
+ 2. 该 `work_session` 从创建开始就拥有 continuity 资格,不要求用户先补 `/new`。
713
+ 3. 该 surface 的 `delivery binding` 必须在同一时刻建立,而不是把“能聊天”与“能 continuity”分裂成两套状态。
714
+ 4. 后续 `/codex` 必须直接针对这条绑定 thread 工作。
715
+
716
+ ### 9.8 `/codex` 的机制要求
717
+
718
+ `/codex` 在多线程模型里的合同必须写死为:
719
+
720
+ 1. `/codex` 只针对当前 Feishu surface 已绑定的 managed thread 导出 CLI 接续方式。
721
+ 2. `/codex` 不参与 candidate-set 选线程,也不支持“按最近线程猜一个”。
722
+ 3. 若当前 surface 没有 binding,则明确失败;失败文案要告诉用户先让当前 surface 获得 binding。
723
+ 4. `/codex` 的成功不改变这个 surface 的 binding;真正切到官方 Codex CLI 写入面,发生在用户执行导出命令之后。
724
+
725
+ ### 9.9 `threads` 的机制要求
726
+
727
+ `threads` 不是附属便利功能,而是多线程场景下的正式合同组成。
728
+
729
+ 它必须:
730
+
731
+ 1. 只列当前 assistant 作用域下的 managed threads。
732
+ 2. 默认按最近活动时间排序。
733
+ 3. 明确标出 active surface。
734
+ 4. 明确标出归档或不可继续状态。
735
+ 5. 默认把 current default pointer 对应线程高亮,但不暗示“系统将自动替你选它”。
736
+
737
+ ### 9.10 delivery binding 与 owner 切换的机制要求
738
+
739
+ 系统必须把“surface 当前绑定哪条 thread”当成正式真相,而不是派生猜测。
740
+
741
+ 它必须满足:
742
+
743
+ 1. Feishu 私聊、Feishu 群聊等 surface 的 binding 分别记录,互不覆盖。
744
+ 2. 只有显式切换动作才能改写 binding。
745
+ 3. `attach` 只把 thread 纳入管理与候选集合,不自动改写任何既有 surface binding。
746
+ 4. 官方 CLI 一侧新开 / continue 某条 thread 后,其他 surface 若未显式切换,仍保持原绑定。
747
+ 5. 对一个已绑定 surface 的普通自然语言 inbound,不允许回退到 assistant default pointer 重新选 thread。
748
+ 6. `binding` 与“当前写入口 owner”是两个层次:binding 表示这个 surface 默认面向哪条 thread,owner 表示当前哪一侧可写。
749
+ 7. 当用户从 Feishu 侧 `/codex` 切到官方 Codex CLI,或从 Feishu `/takeover <handle>` 显式接回时,线程的 owner 可以变化,但 source surface 的 binding 不因 owner 变化而被清空。
750
+ 8. 某个 surface 若仍绑定 thread A,但 A 的 owner 已切到别的 surface,则该 surface 的普通自然语言 follow-up 不得静默抢写;必须提示用户先 `/takeover <handle-of-A>` 或 `/new`。
751
+ 9. 只有显式切换动作才允许同时改写 binding 与 owner;显式切换之外,不得出现“owner 变了,binding 也顺手变了”的隐式连带更新。
752
+
753
+ ## 10. 用户可见文案原则
754
+
755
+ ### 10.1 对用户说“线程是否受管”,不要说“ownershipSource”
756
+
757
+ 用户侧文案应优先使用:
758
+
759
+ - 受管线程
760
+ - 当前没有可继续的线程
761
+ - 当前有多条可继续线程,请先选择
762
+ - 这条线程还未纳入当前 assistant 管理
763
+ - 可先 attach 再继续
764
+ - 系统已为你高亮最近线程,但仍需你显式确认
765
+
766
+ ### 10.2 对用户说 `thread handle`,不要说底层 runtime id
767
+
768
+ 用户看到和输入的参数,应是:
769
+
770
+ - `thread handle`
771
+
772
+ 而不是:
773
+
774
+ - `runtimeThreadId`
775
+ - `cliResumeRef`
776
+ - 官方历史里的内部主键
777
+
778
+ ### 10.3 对 CLI 用户说动作,不说内部模型
779
+
780
+ CLI help 和 quick start 应优先讲:
781
+
782
+ - `new`:新开
783
+ - `continue`:继续
784
+ - `attach`:纳管
785
+ - `threads`:列出可继续线程
786
+
787
+ ## 11. 验收标准
788
+
789
+ ### 11.1 CLI `new`
790
+
791
+ 1. `ally new --assistant <name>` 能直接进入官方 Codex CLI,并新开一条 managed thread。
792
+ 2. 该线程从创建开始就绑定正确的 assistant `CODEX_HOME` 与 workspace。
793
+ 3. 系统会立即回显 `thread handle`。
794
+ 4. 后续用户可在 Feishu 单聊中通过 `/takeover <handle>` 或唯一候选时直接 `/takeover` 接回。
795
+
796
+ ### 11.2 CLI `continue`
797
+
798
+ 1. `ally continue --assistant <name>` 在唯一候选时可直接进入对应 thread。
799
+ 2. 在零候选时明确失败,不自动新开。
800
+ 3. 在多候选时明确失败,并提示先看 `ally threads`。
801
+ 4. `ally continue --thread <handle>` 能精确进入对应 thread。
802
+ 5. 即使 assistant index 中存在 default pointer,多候选时也不得静默按该指针自动继续。
803
+
804
+ ### 11.3 Feishu `/takeover`
805
+
806
+ 1. `/takeover` 在唯一候选时可直接接回对应 thread。
807
+ 2. 多候选时必须拒绝猜测,并提示使用 `/threads` 与 `/takeover <handle>`。
808
+ 3. `/takeover <handle>` 能精确接回对应 thread。
809
+ 4. 即使系统能识别“最近线程”,多候选时也不得静默按最近线程自动接回。
810
+
811
+ ### 11.4 Feishu `/codex`
812
+
813
+ 1. `/codex` 只导出当前 Feishu surface 已绑定 thread 的 CLI 接续命令。
814
+ 2. 若当前 surface 没有 binding,则 `/codex` 明确失败,不按 assistant default pointer 或最近线程做猜测。
815
+ 3. 对“Feishu 首次自然语言开聊、从未 `/new`”创建的线程,后续 `/codex` 仍必须可用。
816
+ 4. 多线程存在时,只要当前 surface 的 binding 是明确的,`/codex` 仍然导出这条绑定 thread,而不是要求用户重新选候选。
817
+
818
+ ### 11.5 `attach`
819
+
820
+ 1. `ally attach --assistant <name> --last` 在候选唯一时能成功创建正式 `work_session`。
821
+ 2. attach 成功后会生成并返回 `thread handle`。
822
+ 3. attach 后,该线程可被 `ally continue --thread <handle>` 与 `/takeover <handle>` 使用。
823
+ 4. assistant 专属 `CODEX_HOME` 之外启动的官方 Codex thread,不属于 attach 候选范围。
824
+
825
+ ### 11.6 `threads`
826
+
827
+ 1. CLI、Feishu、Desktop 都存在正式的线程列表能力。
828
+ 2. 列表中的 handle、标题、最近活跃时间与 active surface 对用户足够判断下一步选择。
829
+ 3. 列表可高亮默认线程,但这种高亮不构成自动选择承诺。
830
+
831
+ ### 11.7 Stable Delivery Binding
832
+
833
+ 1. Feishu 私聊窗口若已绑定 thread A,则在官方 Codex CLI 或其他 surface 新开 / continue / attach 出 thread B 后,该私聊窗口的普通自然语言 follow-up 仍继续 A。
834
+ 2. Feishu 群聊与私聊可以同时绑定不同 thread,且互不覆盖。
835
+ 3. `attach` 只让 thread B 进入受管候选集合;它本身不改变任何现有 Feishu 会话的绑定。
836
+ 4. 用户只有在当前会话里显式执行 `/takeover <handle-of-B>`、`/new` 或等价切换动作后,这个会话的 binding 才允许从 A 改到 B。
837
+ 5. 若当前会话仍绑定 thread A,但 A 的 owner 已切到官方 Codex CLI,则该会话的普通自然语言 follow-up 必须被显式挡回,不得静默重抢 owner。
838
+
839
+ ## 12. 风险 / 假设 / 待决问题
840
+
841
+ ### 12.1 风险
842
+
843
+ 1. 官方 Codex CLI 的新建线程完成时点,可能比现有 continue/attach 路径更难观测;实现时必须先验证 thread 创建后的 durable 记录时机。
844
+ 2. `thread handle` 的生成与历史 backfill 若处理不好,可能导致 rename、archive、恢复路径的兼容性问题。
845
+ 3. 顶层 CLI 命令新增后,README、quick start、桌面端文案若不同步,用户会同时看到两套入口,反而更乱。
846
+
847
+ ### 12.2 假设
848
+
849
+ 1. 当前 same-machine handoff 的底层 `work_session`、assistant index 与 delivery binding 机制足以承接本专题,不需要重做存储模型。
850
+ 2. 官方 Codex CLI 在 assistant `CODEX_HOME` 下启动时,仍会遵守当前 assistant desk 引导的规则链。
851
+ 3. Feishu 侧现有 `/new`、`/codex`、`/takeover` 词面足够稳定,本专题只补参数与列表能力,不需要整体改名。
852
+
853
+ ### 12.3 待决问题
854
+
855
+ 1. `threadHandle` 的具体编码算法选“短化 workSessionId”还是“单独随机短 id + 冲突回退”,仍可在实现说明中拍板,但不影响产品合同。
856
+ 2. Desktop 第一版是否要一次性同时上 `New Thread / Continue Thread / Attach Existing Thread / Threads` 四个显式入口,还是先把 `Threads` 折进 Continue picker。
857
+
858
+ ## 13. 实施建议
859
+
860
+ 1. 先复用现有 `handoff-service` 和 `work_session` 存储,不新造第二套对象层。
861
+ 2. 优先先落 `thread handle + threads list + multi-candidate rejection`,再补 CLI 新门面。
862
+ 3. 文档回写时,把 `same-machine handoff` 重新表述为“managed thread continuity baseline”,避免让后续误以为只有 handoff 没有 entry,也没有 selection model。