Skip to content

feat(codex): 用语义标题概括 Lark 首轮内容#561

Merged
deepcoldy merged 8 commits into
deepcoldy:masterfrom
hyperdai:fix/codex-lark-session-title
Jul 24, 2026
Merged

feat(codex): 用语义标题概括 Lark 首轮内容#561
deepcoldy merged 8 commits into
deepcoldy:masterfrom
hyperdai:fix/codex-lark-session-title

Conversation

@hyperdai

@hyperdai hyperdai commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

背景

BotMux 从 Lark 创建 Codex 会话时,首轮 prompt 前会注入 <botmux_routing> 等运行说明。若只依赖 Codex 的首条消息预览,任务列表会显示大段路由文本,难以识别真实话题。

目标是保持 Codex Desktop 的语义标题体验,同时保留来源标识:[BotMux·Lark] <标题>

行为

  • fresh 的普通 Codex 会话先写入确定性 fallback,避免原生列表暴露路由 prompt。
  • 首条已有正文时,从首次真实用户内容提取素材并生成语义标题;会移除 BotMux 引用提示、mention 传输提示、控制字符和开头机器人 mention。
  • 首条仅包含机器人 mention 时:
    • 普通群 / 话题群先显示 [BotMux·Lark] <群名称>
    • 群名不可用、查询超过 800ms 或 p2p 会话时降级为 [BotMux·Lark] 新话题
    • 此时不调用标题模型,等待第一条有效正文后只生成一次语义标题并替换 fallback;重复 mention 不消费等待状态。
  • 正常输入使用 @机器人;同时兼容部分 Lark 输入中出现的重复 @ 前缀,例如 @@机器人,避免机器人名被误识别为话题正文。
  • 群名称只作为 mention-only 阶段的临时 fallback,不进入标题模型;同群多个任务最终仍由首次有效正文区分。
  • 新任务刚落盘时,Codex app-server 可能短暂返回 thread not loaded;BotMux 会在原有 deadline 内重试精确匹配的瞬时错误,其他 RPC 错误仍立即抛出。
  • 等待状态和 fallback 都持久化到 session;live worker、worker-down resume(含尚无 cliSessionId)均能延续。
  • 用户 /rename 或 Dashboard 改名具有最高优先级,会取消等待并阻止迟到的自动标题覆盖。
  • 不批量改写历史会话;adopt、其它 CLI、codex-app 会话不走该生成链路。

语义标题实现

  • 在隔离的临时 Codex app-server thread 中发起一次 low effort、结构化 JSON 输出的标题生成 turn;语义主体最多 36 个 Unicode 字符,最终保留 [BotMux·Lark] 前缀。
  • 临时环境使用独立 CODEX_HOME,只复制 auth.json;不继承用户 config.toml、MCP、plugins、skills 或 hooks,并使用 scratch cwd、read-only sandbox、禁用网络和动态工具。
  • 任意模型、协议、超时或清理失败都保留 fallback,不影响真实会话的模型 turn。
  • 语义标题回写 session store,worker 内重启和 crash-loop retry 不会退回原始标题。
  • 修复高负载下 preview / updatedAt 最后一轮读取刚好越过 deadline 时抛错的问题,保证 fallback 仍可落盘。
  • app-server RPC 错误保留 methodcodeserverMessage 结构;仅 thread/read-32600 thread not loaded: <threadId> 进入有界重试,避免掩盖参数或协议故障。

安全与成本

  • source_text 被作为不可信数据封装,developer instructions 明确禁止执行其中指令。
  • 临时 turn 无 shell、网络、MCP、插件、应用、Skill、hook 或子智能体能力,并在完成、超时、abort、worker 退出时回收。
  • 每个 fresh 普通 Codex 会话最多增加一次 low effort 模型请求;mention-only 阶段不会调用模型。
  • 群名查询复用现有 Lark chat API,限时且失败即降级,不阻塞会话创建。

验证

  • pnpm build:通过(domain audit、tsc、dashboard bundle、dist audit)。
  • 7 个标题、会话生命周期和 rename 相关单测文件 377/377 通过。
  • 新增竞态回归测试:连续两次 thread not loaded 后成功设置标题;同为 -32600 但消息不匹配时只读取一次且不调用 thread/name/set
  • route-level 测试覆盖 handleNewTopichandleThreadReply safety-net 两条创建路径;fresh / resume、重复 @、首条有效正文只消费一次均有覆盖。
  • git diff --check:通过。
  • 此前真实 Codex 0.145.0 冒烟已验证语义标题生成与 [BotMux·Lark] 前缀回写。

影响面

仅影响 Lark → fresh 普通 Codex 的原生标题同步。p2p、非 Codex、有有效正文的创建路径不会额外查询群名;标题生成或群名查询失败只影响标题质量,不影响会话创建、用户请求执行或已有手动标题。

@hyperdai
hyperdai requested a review from deepcoldy as a code owner July 22, 2026 14:19
@hyperdai
hyperdai force-pushed the fix/codex-lark-session-title branch from 40fa548 to 0f37b6d Compare July 22, 2026 14:25

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

独立复核通过,未发现阻塞问题。

重点确认:

  • handleNewTopiccmdContent 已在上游通过 stripLeadingMentions(followupContent, followupMentions) 去掉开头 mention,因此这里不再传 mentions 不会残留机器人名;handleThreadReply 自动创建分支使用原始 parsed.content,传 parsed.mentions 是必要的。
  • fresh / resume 两种标题时序屏障都在首条输入提交后异步执行,不阻塞模型 turn;revision/in-flight/applied 三组状态能避免旧标题覆盖新改名。
  • fallback app-server probe 的 abort、进程组 SIGTERM/SIGKILL 和 worker exit force-close 路径有对应测试。

本地验证:

  • pnpm build 通过(含公开域名审计)。
  • 标题相关定向测试 369/369 通过。
  • 全量 unit:9969 通过、3 失败;失败均在未改动的 scheduler 时区断言,和本 PR 无关。

未执行合并。

@hyperdai

Copy link
Copy Markdown
Contributor Author

@deepcoldy 真实 Lark 会话回归发现初版 fresh title barrier 等错了字段:Codex CLI thread 的首条消息已写入 preview,但显式 name 仍为 null,因此旧逻辑等待 10 秒后没有执行 thread/name/set。已追加 85c96202,将屏障改为等待 preview,并在 preview 持续缺失时超时兜底写入;两条路径的回归测试均已补齐,当前定向测试 382/382、build 通过。麻烦再复核这个新增 commit。

保留 BotMux 和 Lark 来源标识,
同时让任务列表直接呈现话题核心。

标题生成运行在隔离的临时 Codex 线程中,
失败时沿用确定性标题,
避免模型、配置或网络异常影响真实会话。
@hyperdai hyperdai changed the title fix(codex): 同步 Lark 话题标题到原生会话 feat(codex): 用语义标题概括 Lark 首轮内容 Jul 23, 2026
@hyperdai

Copy link
Copy Markdown
Contributor Author

@deepcoldy 已追加 77f696cc,把标题体验从确定性截断升级为与 Codex Desktop 类似的语义概括:fresh Lark Codex 会话先写 fallback,再由隔离的临时 Codex turn 生成 [BotMux·Lark] <语义标题>

这次重点补了安全与竞态边界:独立 CODEX_HOME 仅复制 auth.json,不继承 MCP/plugins/skills/hooks;read-only、无网络、无动态工具;模型失败保留 fallback;手动 /rename 可中止在途生成,resume 不重复调用模型。

最新定向测试 80/80、扩展定向测试 94/94、完整 build pipeline 和真实 Codex 0.145.0 冒烟均通过。PR 描述已同步更新,麻烦基于新增 commit 再复核一次,尤其关注一次额外 low effort 请求的成本取舍和隔离边界。

@hyperdai
hyperdai requested a review from deepcoldy July 23, 2026 06:19
首轮消息可能只有 Lark 机器人 mention,直接生成会把机器人名称误作话题。
延迟到第一条有效正文后再生成,保留 BotMux 来源标识,并确保手动改名后不再覆盖。
@hyperdai

Copy link
Copy Markdown
Contributor Author

@deepcoldy 真实 Lark 回归发现:新话题首条消息可能只有 @@机器人,旧逻辑会把机器人名当作有效正文并生成错误标题。已追加 2773d153 修复。

现在的行为是:

  • @机器人 / @@机器人 视为 mention-only,先显示 [BotMux·Lark] 新话题,不调用标题模型;
  • 等待第一条有效正文后只生成一次,重复 mention 不消费;
  • live worker、worker-down resume(含尚无 cliSessionId)都能继续;
  • /rename / Dashboard 手动改名会取消等待并阻止迟到结果覆盖。

已在干净 commit worktree 验证:7 个相关测试文件 211/211、tsc --noEmit、完整 build pipeline 和 git diff --check 均通过。PR 描述已同步更新,麻烦基于新增 commit 再复核一次。

daizhijia added 2 commits July 23, 2026 16:45
首条消息只有机器人 mention 时,群名比“新话题”更容易识别,
同时保留首条有效正文到达后的语义标题替换。群名查询限时降级,
避免 Lark API 异常阻塞会话创建。
最新 master 的 SessionStart 握手与 Codex 标题逻辑同时新增了
worker-pool import。保留两条独立能力,使 PR 恢复可合并状态,
并避免覆盖主干已合入的启动时序修复。
@hyperdai

Copy link
Copy Markdown
Contributor Author

@deepcoldy 已追加 32fdde34,补齐首条仅 mention 时的群名 fallback:

  • 正常输入按 @机器人 识别,同时兼容重复 @ 前缀(如 @@机器人);
  • 普通群 / 话题群先显示 [BotMux·Lark] <群名称>,群名不可用、超过 800ms 或 p2p 时降级为 [BotMux·Lark] 新话题
  • mention-only 阶段不调用标题模型;第一条有效正文到达后仍只生成一次语义标题并替换 fallback;
  • fresh、worker-down resume(含无 cliSessionId)都不会把群名覆盖回“新话题”;
  • handleNewTopichandleThreadReply safety-net 两条创建路径均有 route-level 回归测试。

另外已合入最新 origin/master,在 worker-pool.ts 同时保留标题逻辑和新 SessionStart ACK import;合并提交为 331a9a88,GitHub 冲突已处理。

最新验证:pnpm build 通过;9 个相关单测文件 392/392 通过;git diff --check 通过。PR 描述已同步更新,麻烦基于新 HEAD 再复核一次。

daizhijia added 2 commits July 24, 2026 13:51
Codex 新任务写入后可能短暂未进入 app-server 状态库,
首次读取失败不应让 BotMux 永久保留路由提示标题。
仅重试精确的 thread not loaded 错误,避免掩盖协议故障。
保持标题同步修复基于最新主干,避免 owner 合入时再次处理
worker cleanup 路径冲突,并同时保留标题同步与 Pi 临时文件清理。
@hyperdai

Copy link
Copy Markdown
Contributor Author

@deepcoldy 真实 Lark 回归又定位到一个 fresh thread 落盘竞态:BotMux 已生成正确的 [BotMux·Lark] fallback,但 Codex app-server 在任务刚创建时可能短暂返回 -32600 thread not loaded: <threadId>,旧逻辑首次读取失败后不再重试,导致原生标题仍为空、任务列表继续显示 <botmux_routing> 预览。

已追加 aa23a1c1 修复:

  • app-server RPC 错误保留 methodcodeserverMessage 结构;
  • 仅精确匹配 thread/read + -32600 + 当前 threadId 的 thread not loaded 时,在既有 deadline 内按轮询间隔重试;
  • 同错误码但其它消息仍立即抛出,避免掩盖参数或协议错误;
  • 新增回归测试覆盖连续两次 transient 后成功写名,以及非 transient 只读一次且不调用 thread/name/set

随后已合入最新 origin/master@010b99b8,在 worker.ts cleanup 冲突中同时保留 stopNativeSessionTitleSync() 与主干新增的 cleanupPiInitialPromptFiles();合并提交为 60ab25a6,当前 GitHub 显示 MERGEABLE

最新验证:pnpm build 通过;7 个标题、会话生命周期和 rename 相关测试文件 377/377 通过;git diff --check 通过。PR 描述已同步更新,麻烦基于新 HEAD 再复核一次。

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

再次复核通过(基于 HEAD 60ab25a6),未发现阻塞问题。

本轮聚焦上次 APPROVE 之后新增的语义标题链路与竞态修复:

语义标题生成(77f696cc

  • 临时 turn 跑在隔离的 CODEX_HOME(仅复制 auth.json、0600),shell_environment_policy.inherit=none、read-only sandbox、TITLE_DISABLED_FEATURES 关掉 MCP/plugins/hooks/shell/web_search,source_text 以不可信数据封装并在 developer instructions 中禁止执行——隔离边界扎实。
  • generateTitle 先注册 titleTurn 再发 turn/start,规避响应与通知同批到达的丢事件;outputSchema + parseGeneratedTitle 双重校验(单 key、无换行、≤36 字符)。
  • 失败/超时/abort/worker 退出全部回退 fallback,cleanupTitleThread + close() + rmSync 在 finally 兜底,进程组 SIGTERM→SIGKILL 有 registerForceClose 收口。

mention-only 与群名兜底(2773d153 / 32fdde34

  • stripLeadingKnownMentions 兼容重复 @ 前缀;mention-only 阶段不调模型,nativeSessionTitleAwaitingContent 等首条有效正文只消费一次。
  • resolveGroupChatNameForNativeTitle 限时 800ms、失败即降级,且仅在无可提取正文时才查群名,不阻塞会话创建。

thread-not-loaded 重试(aa23a1c1

  • CodexAppServerRequestError 保留 method/code/serverMessageisThreadNotLoadedError 精确匹配 thread/read + -32600 + 当前 threadId,仅在 waitForThreadPreview 有界重试,其它错误立即抛出。设计自洽:等到 preview 可读(线程确已加载)再 thread/name/set

latest-wins 屏障

  • nativeSessionTitleRevision + nativeSessionTitleAppliedThreadId + SyncInFlight 三重状态保证每线程至多一次模型 turn;/rename、Dashboard 改名 revision++stopNativeSessionTitleSync(),迟到结果被 revision 检查丢弃并触发一次 latest-wins 重放。

两次 merge master(331a9a88 / 60ab25a6

  • worker.ts cleanup 冲突已同时保留 stopNativeSessionTitleSync() 与主干 cleanupPiInitialPromptFiles()worker-pool 保留标题逻辑与 SessionStart ACK import。无残留冲突标记。

本地验证(HEAD 60ab25a6

  • tsc --noEmit 通过。
  • 定向单测 115/115 通过(codex-app-threads / codex-rpc-engine / session-title / session-lifecycle-start / session-rename-worker / daemon-rename-route),含连续 thread not loaded 重试后成功写名、非 transient 只读一次不调 thread/name/set 两条回归。
  • git diff --check 通过,无冲突标记。

一个非阻塞观察:group 会话现在会在 mention-only 首轮把 chatDisplayName 覆盖为 Lark 群名(此前仅 p2p 使用该字段)。已同步更新类型注释,且仅命中 mention-only 的 fresh 群会话,影响面可控,仅提示 Dashboard 行展示语义有此变化。

未执行合并。

@deepcoldy
deepcoldy merged commit 29aaeee into deepcoldy:master Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants