Skip to content

feat(chat): 粘贴会话链接即引用另一段对话(#1324 应用内方案) - #1326

Merged
vastsa merged 3 commits into
vastsa:mainfrom
HanawaBanana:feat/session-reference
Oct 3, 2026
Merged

vastsa merged 3 commits into
vastsa:mainfrom
HanawaBanana:feat/session-reference

Conversation

@HanawaBanana

Copy link
Copy Markdown
Contributor

摘要

Issue #1324 的应用内方案(B):把 pi-desktop://session/<id> 链接本身当作引用——在会话溢出菜单复制链接,粘贴进任意输入框草稿并发送,本轮就带上那段对话的有界摘录,消息上以芯片呈现,模型按引用块读到它。

分支 feat/session-reference,基于 origin/main 1beb2e6a1(pnpm check:pr-base 通过),2 个提交:

  1. feat(chat): reference another conversation by pasting its link
  2. fix(chat): name a referenced conversation from its current title

refs #1324(A 方案:注册 pi-desktop:// 为系统协议处理器、open-url / second-instance 参数与安装器改动,仍在计划中,不在本 PR)

包含内容

共享层

  • session-link.ts:pi-desktop://session/<id> 的生成与解析(formatSessionLink / parseSessionLinks / isSessionLinkToken),单条消息最多 4 个引用;正则带负向前瞻((?![A-Za-z0-9_:/-])),所以 remote: 前缀 id 与更深路径不会被误判成引用。
  • MessageAttachment 新增 kind: "session" 与 text,并补 isRenderableAttachment 类型守卫,让渲染层的文件附件路径能排除会话引用。

Electron main

  • 新增 session-references.ts:派发前解析草稿里的每个链接,通过宿主 session.get 取有界摘录(最近 40 条消息 / 单字段 8 KB / 整段 16 KB,最新在后,并说明被边界截掉的更早条数),写进消息附件的 text。
  • 只读同一项目的对话;自引用在任何读取之前丢弃;不存在的 id、其他项目的对话、remote: 一律保持纯文本——粘贴不能读到读者本来打不开的转写。
  • agent-ipc.ts:附件既写进持久消息(后续轮次继续读),也作为本轮文本传给 sidecar。

Agent runtime

  • 引用块以 <session_reference name="…" session="…"> 排在用户自己的话之后引用给模型;属性做转义。
  • 修了一处必须修的接线:原先只有压缩/预算统计用到了带引用的文本,真正发给 provider 的仍是原始 modelInput.text——现在统一走 promptText(),并补了对应回归用例(先红后绿)。

渲染层

  • 用户消息上的会话引用渲染为聊天图标芯片,标签 会话引用 · 标题(9 语言目录),工具提示说明会打开哪段对话,点击打开被引用会话。
  • 芯片按被引用对话的当前标题命名:手动改名或首轮自动摘要改名都会跟随到每一处引用;该对话在当前视图列不出来时回退到引用产生时记录的名字。模型读到的引用块保留引用产生时的名字(它是那一刻的引文,身份始终是 session="<id>")。
  • promptAttachmentsFromMessage 与乐观消息路径都按 isRenderableAttachment 收窄,会话引用不会被当成可解析的文件引用。

i18n / 文档 / 测试

  • 新增 3 个键 ×9 语言:nav.copySessionLink、chat.sessionReference、chat.sessionReferenceOpen。
  • 中英双语文档:component-spec 新增 §20B(目的 / 解剖 / 状态 / 交互)、data-storage 附件块 kind:"session" + text、agent-runtime 步骤 6、E2E 计划场景 E2E-SESSION-reference-conversation-link。
  • 新增用例:shared 链接解析 5 条、runtime 引用块 3 条 + 本轮模型上下文 1 条、main 解析/边界 5 条、渲染层 8 条(芯片文案、zh-CN 渲染、消息分支、改名跟随、提示路径不把引用当附件、9 语言键)。

按 #1324 讨论确定的取舍

  • 不新增输入触发器:@ 文件、/ 命令保持不变,链接就是引用本身;草稿里它是纯文本,发送后才在消息上出现芯片。
  • 消息正文保留链接文本:它是可重发、可重新解析的那份来源,芯片只是附加的可读入口。
  • 只允许同项目引用;自引用丢弃。
  • pi-desktop:// 暂不注册系统协议:本 PR 只做应用内引用,A 方案单独提。

验证

  • pnpm check:pr-base 通过(origin/main 1beb2e6a1 是 HEAD 的祖先)。
  • pnpm build:js、pnpm lint(biome + 样式令牌)、pnpm -r --if-present typecheck、pnpm docs:check(548 页 / 83 组中英对照)。
  • pnpm --filter @pi-desktop/desktop test — 3464 通过
  • pnpm --filter @pi-desktop/shared test — 1168 通过;pnpm --filter @pi-desktop/i18n test — 29 通过;pnpm --filter @pi-desktop/agent-runtime test — 1167 通过
  • cargo test -p host-core --locked — 746 通过;cargo fmt --check

真机验证(zh-CN、独立配置目录、本地录制桩模型,走真实的会话溢出菜单与输入框):

CLIPBOARD     "pi-desktop://session/9db8f850-…"
MENU_ITEM     "复制会话链接"
CHIP          {"text":"会话引用 · 依赖清理顺序","tooltip":"打开会话 依赖清理顺序","session":"9db8f850-…"}
MODEL_REQUEST 目标会话的模型请求里出现:
              <session_reference name="依赖清理顺序" session="9db8f850-…">
              Referenced conversation "依赖清理顺序" (…). 2 messages shown in order, newest last.
              user: 把依赖清理的顺序记录一下:先删 lockfile,再重装依赖。
              </session_reference>
RENAMED_CHIP  {"text":"会话引用 · 依赖清理顺序(改名后)","tooltip":"打开会话 依赖清理顺序(改名后)"}
CHIP_OPENED   "9db8f850-…"        # 点击芯片确实切到被引用会话

已知问题

  1. 本机 cargo clippy 不可用(stable 工具链未安装 cargo-clippy),Rust 侧只跑了 cargo test 与 cargo fmt --check。
  2. 引用块里的名字不追溯更新:已经发给模型的块保持引用产生时的名字(见上)。如果你希望历史引用也按最新标题重写,我再单独改。
  3. 真机演示用的是本地夹具模型(不是真实供应商),用来证明「引用确实到了模型请求里」;换真实模型的观感请以你自己的配置为准。

AI 辅助说明

本 PR 的实现、文档与验证由 AI 编码代理协助完成:模型 deepseek-v4.1-flash,思考程度 max。上面所有命令与真机结论都在本地实际执行过;发现并修掉的接线缺陷(模型实际收到的请求漏掉引用块)与上面列出的已知项都如实写在正文里,没有隐藏。

A reader can now hand one conversation to another turn without retyping it:
the conversation overflow menu copies `pi-desktop://session/<id>` for every
reader, not only developer mode, and Electron main resolves every such link
in a draft into a bounded excerpt attachment before the turn starts. The
link is the whole interaction, so `@` files and `/` commands keep their
trigger symbols and the visible prompt is never rewritten.

The excerpt travels with the message: it is stored on the message's
attachment, quoted to the model as one `<session_reference>` block, and
re-resolved from the referenced conversation's current content when a
message that still contains the link is sent again. Only conversations of
the same project are read, the current conversation is dropped before any
read, and an unknown id, another project's conversation, or a `remote:`
identifier stays plaintext, so a paste cannot make the app read a
transcript its reader could not open.

The transcript shows a reference as a chat-icon chip that opens the
conversation it names. Registration as an operating-system protocol handler
is deliberately not part of this change.

refs vastsa#1324
A reference chip kept the name the referenced conversation carried when the
link was pasted, so renaming that conversation — by hand, or by the
first-turn summary — left every existing chip showing the old name. The chip
now reads the conversation's current title from the session list and falls
back to the name recorded on the message when this viewer no longer lists
that conversation. The quoted block keeps the name recorded when the
reference was made: it is a quotation, and the id is the identity.

refs vastsa#1324
@vastsa
vastsa merged commit 9cbcb78 into vastsa:main Oct 3, 2026
4 checks passed
@vastsa

vastsa commented Oct 3, 2026

Copy link
Copy Markdown
Owner

感谢实现,已完成审查并合入 main(merge commit 9cbcb78)。合入前先以 merge 方式把 PR 分支同步到最新 main,保留了原提交历史;更新后的 GitHub JS、Docs、base ancestry 和 Rust 检查全部通过。

审查重点包括链接解析与有界摘录、同项目和自引用边界、附件持久化/模型请求接线、引用芯片与多语言。候选树上另跑了会话引用测试、agent-runtime 全量测试(1162 项)、shared(5 项)、i18n(29 项)、桌面引用测试(13 项)、host-core(746 项)、fmt/clippy 和文档检查,均通过。

后续可补自动化跨模块 E2E:E2E-SESSION-reference-conversation-link 目前仍标为 Draft;本 PR 的来源级测试已覆盖解析、UI 和模型请求,PR 描述也记录了隔离配置目录下的本地录制模型手工验证。

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