feat: 跨 App 语气适应(前台 App 注入 LLM context)#117
Conversation
参考 Wispr Flow / Superwhisper Super Mode / Typeless 的"按应用调整语气"
设计:begin_session 捕获前台 app 标签 → polish/translate 调用前作为
"# 上下文\n当前前台应用:…" 拼到 system prompt 头部,让 LLM 自行根据
app 类型调风格,不维护硬编码 app→style 表。
实现
- coordinator.rs::capture_frontmost_app(macOS NSWorkspace 拿
localizedName + bundleIdentifier;Windows GetWindowTextW 拿前台
HWND 标题;Linux/其他平台 None)
- SessionState 加 front_app: Option<String>,begin_session 填充
- polish.rs::polish/translate_to 新增 front_app: Option<&str> 参数
- 原 working_languages_premise 提升为 context_premise,两个字段
一起拼成 # 上下文 块
- repolish 路径独立调用 capture_frontmost_app(不沿用原 session 的
上下文,因为用户重新润色时所处的 app 可能已经不同)
Out of scope(v1)
- 不读 AX 焦点元素的 selectedText(隐私 + Electron app 兼容性差)
- 不维护 app→style 映射表(让 LLM 自己理解)
- Linux 实现跳过
NSWorkspace 是 macOS 公开 API,无需额外权限;Windows 复用现有
foreground HWND 路径,也不增加权限要求。
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3ad57983d8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let title = String::from_utf16_lossy(&buf[..copied as usize]); | ||
| if title.is_empty() { | ||
| None | ||
| } else { | ||
| Some(title) |
There was a problem hiding this comment.
Replace window title with stable app identifier
On Windows, capture_frontmost_app stores the foreground window caption from GetWindowTextW, which commonly contains document/tab names and other user content rather than just the app name. That value is then propagated into LLM context and logs, so sensitive text can be exfiltrated to the provider and persisted in telemetry even though this feature is meant to pass app context. Capture a stable app identity (process/app name) or aggressively reduce the caption to a safe app label before storing it.
Useful? React with 👍 / 👎.
| if let Some(name) = app { | ||
| lines.push(format!( | ||
| "当前前台应用:{name}。请按这个应用的常见沟通风格调整语气——例如邮件类 app 偏正式、聊天类 app 偏口语、IDE / 文档类 app 偏技术或结构化。\u{4E0D}主动加入与用户原意无关的客套话。" | ||
| )); |
There was a problem hiding this comment.
Sanitize front-app text before prompt composition
context_premise interpolates front_app directly into the system prompt. In the Windows path this string is window-title text that can be influenced by untrusted content (for example, a browser page title) and can include newlines/instruction-like payloads, which allows prompt-injection-style overrides of polish/translation behavior. Normalize/escape control characters and quote or constrain this field before embedding it in the prompt.
Useful? React with 👍 / 👎.
Closes #116
改动
coordinator.rs::capture_frontmost_app:macOS NSWorkspace(公开 API,无权限要求)/ Windows GetWindowTextWSessionState.front_app: Option<String>—— begin_session 填充polish.rs::polish/translate_to新增front_app: Option<&str>参数context_premise(working_languages, front_app)替代原working_languages_premise,统一组装成 "# 上下文" 块repolish路径独立调用capture_frontmost_appTest plan
[coord] front_app captured: ...