Skip to content

feat(bots): 建立轻量伙伴核心 - #2829

Draft
zqchris wants to merge 5 commits into
makecindy:mainfrom
zqchris:cindy-bot-mode-mvp
Draft

feat(bots): 建立轻量伙伴核心#2829
zqchris wants to merge 5 commits into
makecindy:mainfrom
zqchris:cindy-bot-mode-mvp

Conversation

@zqchris

@zqchris zqchris commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

这次改了什么

摘要

按产品校准把本 Draft 从 +109k / 559 文件重建为 +54.9k / 365 文件、5 个干净 commit(基于最新 main 重放,历史已重写)。目标一句话:建立轻量伙伴核心——伙伴是永久档案 + 可替换会话卷的数字同事,最懂的三件事是控制自己(记忆/技能/配置)、找别的伙伴协作、把大活开成真正的 Cindy 任务;对用户不暴露任何工程概念。

核心设计修复(相对上一版):

  • 上下文隔离补洞:pi 原生 subagent 扩展对伙伴关闭、pi 传 --no-context-files、codex 置 project_doc_max_bytes=0——伙伴不再吸入项目/全局 AGENTS.md,也看不到 harness 的 worker 面(此前实测泄漏的根因)。
  • 提示词承诺 = 工具面:删掉 Task Control / 定时 / 作品集三段"说了却调不到"的能力说明;显式挂载 docs 工具集时 cindy_docs 真实进入 MCP allowlist。
  • 委派完成信号重做:不再往时间线落机读文本,改为 synthetic-trigger 用户不可见内部指令唤醒请求方接手;可见终态由协作卡实时承载;父卷被每日换卷归档时改投当前卷,不静默丢失。
  • 新增 Bot×Session 委派collaborate_with_bot 增加 start_task——把大活开成一条普通 Cindy 任务(出现在主任务列表、走自己的权限门),与 Bot×Bot 委派同卡、同信号(bot_delegations.target_bot_id 可空即 Cindy 任务)。

变更类型

  • feat 新功能

范围

  • 关联 Issue / 需求:本 Draft 自身的范围重校准(产品负责人 2026-09-02 拍板:force-push 收敛;项目绑定与 IM 挂载退出 v1;docs 作为独立能力项修通映射)。
  • 本 PR 包含:伙伴档案/Home/每日换卷/模型候选链基座;桌面聊天面(名册、对话、最小设置);collaborate_with_bot(status/notify/delegate/start_task)+ 协作卡 + 隐藏完成信号;上下文隔离与能力 allowlist;6 张只增量新表(migration 0101)。
  • 明确不包含(较上一版移除,后续按需单独成 PR):Automation 定时例程、投递 outbox、guardian 心跳、事件收件匣/订阅、群聊(桌面+手机)、作品集/成长气泡、IM 通道挂载与旧 IM 迁移、项目绑定+worktree 租约、8 角色模板/AI 人设生成/头像图、Mobile 全部、离线 demo 种子、行为包导入导出、token 预算层。
  • 用户可见变化:侧栏新增「伙伴」入口——创建伙伴(名字+自动色相头像+人设文本)、与它对话、看它把活委派出去的实时协作卡。
  • 是否存在 breaking change:无(全部为新增面;bot 表只增量,未发布过任何旧版本)。

规模构成:生产代码约 +27.4k,测试 + drizzle 生成快照约 +23.8k,五语言 i18n 约 +3.7k。入口规模闸三问自查通过(单一完整产品目标;档案基座/聊天面/委派卡三者拆开任一都不可用;按 2026-08-11「大工程用长 Draft 累积」裁决继续以本 Draft 承载)。

UI 变化

伙伴名册/聊天/设置/协作卡均为新增界面,Light/Dark 双模式全部走语义 token。截图待真实验收时补(见「未执行的验证」)。

  • 引用的设计规范:DESIGN.md §10 窄作用域 token(--bot-avatar-*-bg 九色相、--bot-unread-bg/fg 未读徽章,本 PR 一并登记);「Reserved artwork namespace — Bot avatar sentinels」(头像图收敛为保留命名空间 + 中性首字母兜底);间距/圆角/零阴影沿用共享 8px/12px/pill 基线。无硬编码颜色(已扫描)。

怎么验证的

自动验证

pnpm --filter desktop run typecheck                 → 0 错误(全仓)
pnpm --filter desktop db:validate                   → 0000..0101 全通过(journal/snapshot 对齐、无 drift、基线冻结)
pnpm --filter desktop test:migration-replay         → 6/6 + botMigrationReplay 4/4
vitest(desktop bot 全面,低并发定向)               → 173 文件 / 2818 用例全过
vitest(@cindy/maker-core 隔离与策略面)             → 181 过 1 跳过(含「bot 会话无 subagent 扩展 + 有 --no-context-files」断言)
vitest(@cindy/mcps 全量)                           → 698/698
node scripts/check-i18n.mjs                         → 五语言 9126 键全部一致
pnpm check:i18n-glossary                            → 无新增违规
pnpm check:dco                                      → 5/5 commit 已签

未跑全仓 pnpm test:unit/test:unit:related:按 2026-08-07 裁决本地禁止无界全仓并发(实踩卡死机器),以上为低并发定向验证,完整门禁交本 PR 的 CI。

已知与本 PR 无关的主干基线红(在 pristine main 上复验同样失败):messagesWriteReadbackmessagesClearRacesessionAutoTitlePersistorcaTeamStoredrizzle-proxy-perfdrizzle-split.spikesessionsRestoreIfArchived(2 个上游用例)。

手工验证

未执行(见下)。

未执行的验证

真实验收五项仍待产品负责人在场完成(正本见 docs/product-rules/cindy-bots-completion-matrix.md):①伙伴会话上下文无项目 AGENTS.md/无 subagent 工具的实机确认;②delegate 全链(卡片实时更新→隐藏回执→自动接手);③start_task 全链;④docs 工具集挂载后真实做出文档;⑤Desktop Light/Dark 双模式目检。双模式已在实现层全部走语义 token,实机目检未做,按仓库规则如实注明。

风险

风险分类

  • SQLite / migration
  • system prompt

影响与回滚

  • 影响范围:全部为新增面。migration 0101 只创建 6 张 bot 新表 + 1 个 right_sidebar_tabs 局部索引,不触碰任何旧表旧行;旧代码打开含 0101 的库按既有 migration compatibility 守卫失败关闭(不降级)。system prompt 改动仅作用于 bot 会话(botRuntimeProfile 存在时),普通会话零变化——maker-core 既有测试未改动全绿。
  • 回滚 / 降级方式:整体 revert 本 PR 即可;表为纯新增,回滚代码不回滚 migration 亦安全(空表滞留无影响)。

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名(git commit -s,见 DCO
  • UI 改动已在「UI 变化」注明引用的设计规范章节(不涉及 UI 则跳过)
  • 未提交凭证、令牌或授权文件
  • 已补充必要文档
  • 已确认测试结果或说明未执行原因

cursor Bot pushed a commit to zqchris/cindy that referenced this pull request Aug 28, 2026
PR makecindy#2829 QA defect A: a deliver() that never settles pinned the serial
drain loop forever, so every Bot's queue died with it (one completion
receipt wedged global delivery for 64 minutes), and because lease
reclaim only ran inside the drain loop, the 60s sending lease was never
enforced either.

- Race each deliver call against a deliverTimeoutMs watchdog (defaults
  to sendingLeaseMs); a timeout is failed as retryable DELIVERY_TIMEOUT
  and retried with normal backoff. A late-settling adapter call can no
  longer resurrect the row: its writes CAS on status='sending', which no
  longer matches, and late settlement is only logged.
- Move expired-lease reclaim to a dedicated interval sweeper
  (sendingLeaseMs/2, unref'd, dispose'd) that schedules a drain after
  reclaiming, so reclaim no longer depends on drain liveness. The
  reclaim CAS stays safe against a live drain.
- Keep retrySafe=false external-dispatch markers authoritative: a
  timed-out local-adapter dispatch dead-letters as
  DELIVERY_OUTCOME_UNKNOWN instead of retrying.
- Add delivery lifecycle logs (attempt started/delivered/failed/lease
  expired requeued) with id/kind/errorCode only — no message content.

Signed-off-by: nana <823073145@qq.com>
cursor Bot pushed a commit to zqchris/cindy that referenced this pull request Aug 28, 2026
PR makecindy#2829 QA defect B: delegation completion archived the child task
through a generic sessions.setStatus write, which the bot-lifecycle
guard in tx.ts rejects for source='bot' rows — and the error was
swallowed, so the child stayed active forever and the guardian reported
a supervision anomaly on every settle.

bots.finishDelegation already flips bot_session_links to history inside
the terminal transaction; archive sessions.status there too (guarded by
source='bot' AND status='active', so the delegation CAS keeps it
idempotent). Drop the archiveSession dep and its drizzle fallback from
the delegation service and the register.ts wiring: a failure inside the
transaction now fails the delegation settle itself instead of being
silently discarded.

Signed-off-by: nana <823073145@qq.com>
cursor Bot pushed a commit to zqchris/cindy that referenced this pull request Aug 28, 2026
PR makecindy#2829 QA defect C: sendToSessionLocks entries were released in the
holder's finally, so one critical section that never settled pinned the
map entry forever — outbox retries and guardian inbox dispatch queued
behind waitPrev eternally while UI direct sends (which bypass the lock)
kept working and masked the deadlock.

Extract the lock into sendToSessionLock.ts with a leak watchdog: warn
after 30s and bail the map entry after 5min, so later senders proceed
while the stuck holder finishes as a zombie. That is the lesser evil —
its writes are DB-backed with clientId dedup and turn-start guards, the
same protections that already tolerate lock-external UI sends. Bail
warnings carry the holder's current lockStage so the original hang
point (queue-restore / live-send / …) is identifiable from logs. Map
entries never reject; late zombie settlement cannot clobber or resurrect
a successor entry.

Signed-off-by: nana <823073145@qq.com>
cursor Bot pushed a commit to zqchris/cindy that referenced this pull request Aug 28, 2026
Bring makecindy#2829 up to current makecindy/cindy main without rebasing
Nanaco/Chris history. Conflict resolution keeps Bot Mode behavior
and folds in main's API/type/contract updates.

- Keep unpublished bot schema intent; regenerate it as 0098 after
  main's frozen 0096/0097 FTS and companion migrations.
- Preserve bot canonical replacement lock, bot deliverable cards,
  workspace read-only Pi flags, and Engine glossary term.
- Take main's review liveness, generated-files incremental stat
  plus turnSealed, hook workspace prefs, and Harness glossary term.

Signed-off-by: Chris <4436110+zqchris@users.noreply.github.com>

Co-authored-by: Chris Zhang <zqchris@users.noreply.github.com>
伙伴是永久 Profile + 可替换物理 Session 的两层结构:SOUL/记忆/自有技能落在
账号隔离的 Bot Home,canonical 卷每日 04:00 CAS 换代并归档,模型走 1-5 项
候选链,只有「候选不可用」才推进。运行时按 allowlist 装配能力:不继承
Cindy 全局 Skill/MCP/项目上下文;pi 关闭原生 subagent 扩展并传
--no-context-files,codex 置 project_doc_max_bytes=0,显式挂载 docs 工具集
时 cindy_docs 才进入 MCP allowlist —— 提示词承诺与工具面同进同退。
数据库为 6 张只增量新表(migration 0101)。

Signed-off-by: zqchris <chrisz83@gmail.com>
协作只有一个直接入口 collaborate_with_bot:status 查空闲,notify 发不追踪
的消息,delegate 委派给另一个伙伴,start_task 把大活开成一条真正的 Cindy
任务(出现在主任务列表、走自己的权限门)。两种委派共用同一张 bot_delegations
表(target_bot_id 为空即 Cindy 任务)、同一张实时协作卡与同一条完成信号。

完成信号重做:不再往时间线落机读文本,改为 synthetic-trigger 隐藏行 ——
对模型是接手指令,对用户不可见,可见终态由协作卡承载;发起方父卷被每日
换卷归档时改投该伙伴当前 canonical 卷,不静默丢失。委派服务不再依赖
automation 上下文、token 预算层、投递 outbox 与 IM 路由。

Signed-off-by: zqchris <chrisz83@gmail.com>
左侧伙伴名册(IM 式未读)、复用真实任务与 ChatInput 的聊天面、@伙伴结构化
引用,以及只剩三页的设置(成长/模型/维护)。创建收敛成「名字 + 自动色相
头像 + 人设文本」,不暴露 Session、压缩、fallback 等工程概念;记忆与自学
技能在设置页可见、可删。五语言 i18n 同步。

Signed-off-by: zqchris <chrisz83@gmail.com>
《Cindy 伙伴运行时》契约与工程完成矩阵按 v1 范围重写:Bot Home 权威边界、
每日换卷、模型候选链、能力 allowlist、委派与完成信号的呈现契约;设计规范
登记伙伴头像色相与未读徽章的窄作用域 token,并把头像图收敛为保留命名空间。

Signed-off-by: zqchris <chrisz83@gmail.com>
worktree 回收器与通用任务生命周期入口跳过 source='bot' 的会话 —— 伙伴卷的
归档与替换只归 Bot Renew 管;崩溃恢复快照剥离 Bot 宿主瞬态引用。

Signed-off-by: zqchris <chrisz83@gmail.com>
@zqchris zqchris changed the title feat(bots): 建立统一 Bot 运行时与会话基座 feat(bots): 建立轻量伙伴核心 Sep 2, 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.

1 participant