本文件是
sandbox/pi-live分支的交接记录:已交付什么、还差什么、每一项怎么接着做 (含具体 file:line 锚点与坑)。配合docs/dev-rules/pi-harness.md(架构、维护不变量、 上线清单)一起看。最后更新:2026-08-01。
本文下方保留历史调研与实现过程,当前非核心缺口已收口:
- Pi pin 升至 v0.83.0;Mac Intel/Apple Silicon、Linux x64/arm64、Windows x64/arm64 六平台资产有官方 digest pin,Forge 按目标平台 provision 完整目录并打包。
- 文件附件、Extra Dirs(结构化写跟随会话权限)、minimal thinking、纯 BYOM 无 Cindy 登录已开放。
- 精确 rewind 由 Pi
fork(entryId)裁剪对话,文件恢复复用 Cindy Git savepoint;与原会话树共用入口。 - Pi Auto Memory 控制面已开放,自动/手动 compaction summary 写 searchable digest;reset 复用 Maker Memory manager。
- 真二进制覆盖 v0.82.1 → v0.83.0 resume、invalid resume CAS、rewind/fork 后 resume;scheduler Pi 无人值守参数有契约测试。剩余的异构 OS 启动和逐真实模型额度 smoke 是 release runner/RC 门禁,不再需要产品代码。
- 工作分支:
sandbox/pi-live(基于pi-agent-research)。 - 本机联调沙盒:独立 checkout
cindy-pi-latest-sandbox,独立 userDataCindy-dev-pi-latest,以--isolated=pi-latest命名。 - 启动(保留其它 dev 实例,被动模式,不抢定时任务):
只重启 pi 沙盒:先
XDT_USER_DATA_DIR='~/Library/Application Support/Cindy-dev-pi-latest' \ XDT_ISOLATED=1 XDT_ISOLATED_NAME=pi-latest \ pnpm restart:desktop:remote --preserve-runningpnpm desktop:whoami --all找到 pi-latest 的 PID,kill <pid>, 再跑上面的命令。绝不用会全局关实例的默认 restart(会误杀其它 checkout 的 dev)。 - 验证口径:maker-core
pnpm --filter @cindy/maker-core test;desktoppnpm --filter desktop run typecheck;mcpspnpm --filter @cindy/mcps run build && ... test; i18npnpm check:i18n+pnpm check:i18n-glossary。pi 真二进制集成测试在packages/maker-core/src/agents/pi/__tests__/pi-agent.integration.test.ts(二进制缺失自动 skip)。
- redacted thinking 修复;PI 会话图标(π);Auto-review 核心 + pi adapter + auto 档;
PI_OFFLINE / NO_PROXY;
--append-system-prompt(保留 pi 默认 prompt);斜杠命令转义 + 只读工具凭证路径收口;成本计量真值;权限档四语 i18n + 弹窗正文归一化 + 能力契约测试。 - HTML 导出(
export_html)、手动压缩(compact)、subagent 接 pi(Orca worker 可选 pi)。
维护不变量见 pi-harness.md §4(权限档从严到宽、凭证 regex 三处同步、斜杠转义、
auto fail-closed、成本派生链、弹窗正文 harness 无关)。
两个对抗式 review agent(BYOM 路由 / 记忆-斜杠-凭证)发现的真实问题已修复(均带测试):
- digest 按字节截断(index.ts
truncateToByteBudget):原按字符slice(0,7000),中文摘要 ~2731 字即超存储 8192 字节硬上限 →write抛shard-too-large被吞 → digest 静默丢失。 改为码点安全的字节预算截断;title 里的reason也收敛(去换行 + 截 40)防撑爆 maxTitleLen。 - 凭证门覆盖
/proc/<pid>/task/<tid>/environ(shared/auto-review.ts + cindy-bridge-source.ts 同步):原[^/\s]*跨不过/,task/tid 变体读同一份进程环境(含注入的 provider key)漏判。 正则放宽为[^\s]*。 - 凭证扫描递归 array / 嵌套 object(cindy-bridge-source.ts
touchesCredentialPath+ auto-review-policy.tsfindCredentialLeaf,深度上限 6):原只扫顶层 string 字段,{paths:[...]}/{opts:{path}}形态漏判。防御性收口(当前 pi 内置工具用顶层 path,暂无 已知利用面,但抗 pi 二进制升级后 tool schema 漂移)。 - BYOM env 变量名去重(pi-host.ts
buildPiNativeProvidersFromConfigs):my-vllm与my_vllm都归一成CINDY_PI_KEY_MY_VLLM→ 后写覆盖 → 一个 provider 的 key 被发往另一端点 (凭证串号)。撞名追加_2/_3保证独立 env 名。 - getAuthEnv 占位符只给订阅 OAuth provider(pi-host.ts
PI_OAUTH_SUBSCRIPTION_PROVIDERS): 原providerId && !== 'xd'把自定义 BYOM providerId 也塞占位符 → 毒化网关cindy块 → BYOM 会话中途切回网关模型 401。改为仅 anthropic/openai/xai 用占位符,BYOM/自定义拿真网关 key。 - 保留
cindyprovider id(custom-provider-store.ts RESERVED_IDS):撞 pi 网关 provider id 会让其模型既被排除出网关块又不写原生块 →--model校验失败。 - 两处源码契约测试同步 pi 改动:
sessionHeaderMenuParity(导出 HTML / 压缩暂隐; 任务分支只在 Cindy 分叉家族时显示,仍是头部专属)、automationGeneratedSessions( 自动组头图标改用agentKindToVendor,pi 会话显示 π)。
已评估暂不修(设计权衡 / 边缘,后续按需):
- #2 已解决 — 纯 BYOM 不依赖网关 key:
getState按 custom provider runtime 校验自身 apiKey/keyless 状态;即使旧会话没有 providerId,PiAgent 也会先解析 model→native provider 再过 auth。models.json 的cindyblock 在无网关 key 时只拿不可用占位值,不会发往 BYOM endpoint。 - #4 resolvePiNativeProviders 失败降级:抛错 → gateway-only。纯 BYOM 模型此时不在任何 block
→ pi
--model校验失败(fail-closed,会失败而非静默错路由),可接受;已 warn 日志。 - #5
managed鉴权自定义 provider:当前按 keyless 写 dummy key,若指远端会 401。取决于 是否真支持 managed 型 pi 自定义 provider,待该形态确认后再定。 - #7 catalog id 与原生 model id 一致性:双路由去重靠
id精确相等;host 两侧 id 生成须一致 (纯 BYOM 目录无该项,当前不触发)。可加断言。 - B3 多行斜杠命令:
escapeLeadingSlashCommand只护消息开头(pi 命令为 leading-only 解析); 若未来确认 pi 按行识别命令,再对换行后的/转义。
注:
makerSendToSessionOrdering.test.ts的 3 条 source-contract 失败为 upstream/main 自带(needleconst live = maker.getSession(targetSessionId);等在 upstream/base/HEAD 均 不存在),非 pi 工作引入;未改动其契约。
新增 digest 记忆类型:pi compaction_end.result.summary → deps.makerMemory.write 写 digest,
进 FTS 可 memory_search,但排除出 MEMORY.md / system prompt / LLM memory_write 工具。见
memory/types.ts、memory/storage.ts rebuildIndex、pi/index.ts writeCompactionDigest +
onEvent 钩子。下方原设计说明保留作背景。
目标:pi 压缩上下文时,把被丢弃内容的要点沉淀进 Cindy 记忆,新会话可召回。
关键约束(调研结论):
- CC 的 auto-memory / auto-dream 是 CC 二进制内部能力,pi 触达不到,不能复用。
cindy_memory是 per-workdir 文件式(MEMORY.md + 分片),只有 4 类user/feedback/project/reference,没有 session/auto 命名空间。写进去的任何东西都会被rebuildIndex列进 MEMORY.md,而 MEMORY.md 会内联进系统提示 —— 裸转存压缩摘要 = 直接 污染 curated 记忆 + 撑大系统提示。- pi translator 的
compact_boundary目前只带 token 数,不带摘要正文 (packages/maker-core/src/agents/pi/translator.ts的compaction_end)。摘要文本留在 pi 自己的 session JSONL 里,主进程看不到。 - pi bridge(
cindy-bridge-source.ts)能经已桥接的mcp__cindy_memory__memory_write调 cindy_memory(仅 Maker memory 模式开时),也能挂 pi 的session_before_compact钩子拿到preparation.messagesToSummarize(可serializeConversation成文本)。
推荐设计(不污染 curated memory)—— 二选一,动手前与 Chris 确认:
- 新增记忆 type(如
digest)并改storage.rebuildIndex把它排除出 MEMORY.md:只进 FTS(可被memory_search检索)、不进 curated 索引/系统提示。改packages/maker-core/src/types/memory.ts:15(MEMORY_TYPES)+memory/storage.ts的rebuildIndex(~312-338)。 - 灌进
session_search底层的messages_fts(原始对话历史库),而非 curated 分片 —— 语义上压缩摘要更接近「原始历史」而非「提炼后的偏好/决策」,天然不碰 MEMORY.md。见packages/lizi-mcps/src/memory/sessionSearch.ts。
落地路径:在 cindy-bridge-source.ts 挂 session_before_compact → 取
preparation.messagesToSummarize 序列化 → 经 MCP 通道写入上面选定的目标。注意:
(a) 仅 Maker memory 模式开时通;(b) 复用 flush-controller 脚手架
(packages/maker-core/src/memory/flush-controller.ts,当前只在 CC/Codex 接线,pi 未接);
(c) pi 的钩子是否真给摘要正文属 pi 二进制内部,需实测。
验收:pi 会话跑到触发压缩,确认要点进了选定存储、memory_search 能查到、MEMORY.md
没有被污染、系统提示未膨胀。
目标:用户配自定义/本地模型(Ollama / vLLM / 自建端点),pi 直连,不过 anthropic-compat 代理(设计原则:禁止「先转 Claude 再转 pi」双重转义)。
现状(可复用的基建 —— 比预想的多):Cindy 已有完整自定义 provider 体系 ——
apps/desktop/src/main/maker-host/custom-provider-store.ts(CRUD + DB schema)、
buildUserProvider、createDesktopProviderService.ts:384 setCustomProviders、
active-catalog.ts(base + custom 合并进目录,下游选择器/路由统一消费)。
关键发现:CustomProviderConfig.runtimes 是 Partial<Record<AgentKind, CustomProviderRuntimeConfig>>
(custom-provider-store.ts:51),数据模型天生支持 per-agent runtime,包括 pi;每个
runtime 带 baseUrl + models + auth。所以「自定义 provider 能不能挂 pi tab」在数据层已
成立;缺的是 pi 侧把它写成原生 provider 块、以及 UI 是否允许添加 pi runtime。
✅ 已交付:maker-core 核心(2026-07-30,commit 4c0b269b) —— 原生 provider 块 +
provider 感知路由 + 端到端真二进制测试(BYOM 模型直连原生端点、网关零请求)。契约:
AgentDeps.resolvePiNativeProviders + PiNativeProviderSpec(base-agent.ts);
writeModelsJson 写多 provider;buildModelProviderMap + provider 感知 setModel/--provider
(pi/index.ts)。host 侧只要实现 resolvePiNativeProviders 把 custom provider 喂进来即可。
2026-09-13 工作区更新:host、自定义供应商三 runtime、连接向导与标准模型详情已接通。
旧逐模型编辑器已移除,入口改为 ProviderConnectionDialog;协议不再锁定为 CC Messages。
Pi 的八种原生 API 保留,Codex / Claude Code 通过共享翻译与 Pi SDK 适配;账号、发现、
模型 API、Thinking、工作上下文与上游上限的验收和实际边界见
通用供应商目录。本段记录工作区实现,不代表已合并或发布。
目标:pi 原生是 append-only entry 树,支持分支、树导航、分支摘要。现在 Cindy 只用了最粗的 fork(散落成平级会话)。升级成「同一会话内可切换的分支树」,不引入新概念,按现有 fork/分支 迭代。
pi 侧能力:get_tree(rpc.md:724)返回树结构;fork/clone/get_fork_messages
已在 PiAgent.forkSdkSession 用;get_entries 可读分支条目;branchSummary 设置控制分支
摘要。
已落地:
- 统一成一个「会话分支」入口:外层继续展示 Cindy 原有
parentSessionId会话分叉,当前 Pi 会话节点内嵌 Pi append-only entry 树,没有再造第二套分支概念。 - maker-core 增加 harness-neutral
sessionTreecapability / snapshot / navigate 契约;Pi 通过get_tree读取,私有 bridge command 调ctx.navigateTree,可选离开分支时调用模型生成摘要。 - 切换 Pi 分支后,Cindy SQLite 在一笔事务内 soft-hide 旧可见投影并恢复活动路径;旧分支不删除。
选中 user entry 时原 prompt 回填编辑器;上下文占用从 Pi
get_session_stats.contextUsage权威估算恢复。 - IPC / preload / desktop remote transport / device-link allowlist / mobile transport contract 已全链路接通;
老被控端仍按
CHANNEL_NOT_ALLOWED能力降级。桌面 UI 用设计 token,Light/Dark 共用。 - 明确边界:Pi 分支切换只改变模型对话上下文,不回滚工作区文件;Cindy fork 仍是独立会话/ worktree 的粗粒度分叉。
见 pi-harness.md §6。要点:
- 平台二进制:v0.83.0 六平台 digest pin + Forge stage/签名路径已完成;异构 OS 启动 smoke 由各平台发布 runner 做,不能在 Apple Silicon Mac 上伪装成实机结果。
- 模型兼容矩阵:三种 wire protocol、真 Pi bridge/tool/usage/cache 自动测试已完成;逐个真实 商业模型只剩 RC 账号额度 smoke。
- 实机联调:compaction、scheduler 参数、v0.82.1→v0.83.0 resume、invalid fallback、 rewind/fork 后 resume 已自动覆盖。
- minimal effort 档(已完成):Pi capability 与所有 reasoning-capable Pi model descriptor
暴露
minimal;具体模型是否接受该档仍由 Pi v0.83 的 provider mapping 决定。 - settings.json 钉值:目前没写 pi settings.json,retry/超时全 pi 默认;若发现某默认值需防
二进制升级漂移,在
pi/index.ts加writeSettingsJson(与 models.json 同机制)。