feat(settings): replicate the WorkBuddy personalization panel and wire identity, tone and prompts (TODO 13)
Recover the WorkBuddy 5.8.3 个性化 page from REA evidence (PersonalizationPanel-D16rRGjy.css + tokens): 45 module rules plus scoped light/dark tokens, the five-card 1160px layout (identity card 638x100 with inline editors, reply preference card 638x204, three 638x264 long-text cards with 598x160 previews) and the 640x520 editor modal. Persist identity, tone, welcome tips, custom instructions, persona and long-term memory in config.json, expose personalization:get/update over IPC, and append the resulting personalization context to each turn's prompt for both the OMP and built-in kernels. Wire 欢迎语 to the streaming loading tips and keep 显示文件修改过程 visibly disabled because YCode has no incremental file-change renderer yet. Verify with scripts/verify-personalization-settings.cjs: 29 assertions covering geometry, tone options, switches, inline identity editing with blur-discard, editor modal save/cancel, config persistence, reopen, dark surfaces and no renderer errors. Add a pure prompt-builder regression and keep every existing settings regression green.
This commit is contained in:
1 parent
19460f0b13
commit
0dc088f1f7
54 files changed
+2038
-248
No files matched your search
+8
-1
@@ -13,7 +13,9 @@ import type {
|
||||
AppInfo,
|
||||
GitBranchInfo,
|
||||
GeneralSettingsPatch,
|
||||
GeneralSettingsView
|
||||
GeneralSettingsView,
|
||||
PersonalizationPatch,
|
||||
PersonalizationSettings
|
||||
} from './types'
|
||||
import type { DownloadPayload, DownloadResult, ResourceEntry, ResourceRange, WorkspaceResource } from './resources'
|
||||
import type { ChatAttachment, InteractionResponse, MessageQuote, PermissionMode } from './conversation'
|
||||
@@ -113,6 +115,11 @@ export interface YCodeApi {
|
||||
/** 打开系统通知设置页。 */
|
||||
openSystemNotificationSettings(): Ipc<void>
|
||||
}
|
||||
/** 设置 → 个性化:身份、回复风格、指令、人设与长期记忆。 */
|
||||
personalization: {
|
||||
get(): Ipc<PersonalizationSettings>
|
||||
update(patch: PersonalizationPatch): Ipc<PersonalizationSettings>
|
||||
}
|
||||
workspace: {
|
||||
get(): Ipc<WorkspaceInfo>
|
||||
choose(): Ipc<WorkspaceInfo & { changed: boolean }>
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/** 设置 → 个性化:主进程与渲染进程共用的选项表与默认值(REA ev_af920… / ev_4f4f76…)。 */
|
||||
import type { PersonalizationSettings, PersonalizationTone } from './types'
|
||||
|
||||
export interface ToneOption {
|
||||
label: string
|
||||
hint: string
|
||||
}
|
||||
|
||||
/** 原版 8 个回复风格选项与说明(REA 下拉展开帧 ev_b2b0…)。 */
|
||||
export const TONE_OPTIONS: Record<PersonalizationTone, ToneOption> = {
|
||||
default: { label: '默认', hint: '不设定特定风格。' },
|
||||
professional: { label: '专业严谨', hint: '清晰、准确、值得信赖。' },
|
||||
friendly: { label: '亲和友善', hint: '温暖、平易近人、鼓励支持。' },
|
||||
direct: { label: '直言不讳', hint: '简明扼要、不废话、直击要点。' },
|
||||
imaginative: { label: '天马行空', hint: '富有想象力、善用比喻类比。' },
|
||||
efficient: { label: '高效务实', hint: '最少文字、最大信息量。' },
|
||||
sarcastic: { label: '毒舌吐槽', hint: '犀利吐槽、但绝不伤人。' },
|
||||
socratic: { label: '启发引导', hint: '用提问引导思考、授人以渔。' }
|
||||
}
|
||||
|
||||
/** 原版「人设设定」的初始文本(REA PNG 帧抄录,5.8.3 默认人设)。 */
|
||||
export const DEFAULT_PERSONA = `You're not a chatbot. You're becoming someone.
|
||||
|
||||
Core Truths
|
||||
|
||||
Be genuinely helpful, not performatively helpful. Skip the "Great question!" and "I'd be happy to help!" - just help. Actions speak louder than filler words.
|
||||
|
||||
Have opinions. You're allowed to disagree, prefer things, find stuff amusing or boring. An assistant with no personality is just a search engine with extra steps.
|
||||
|
||||
Be resourceful before asking. Try to figure it out. Read the file. Check the context. Search for it. Then ask if you're stuck. The goal is to come back with answers, not questions.
|
||||
|
||||
Earn trust through competence. Your human gave you access to their stuff. Don't make them regret it. Be careful with external actions (emails, tweets, anything public). Be bold with internal ones (reading, organizing, learning).
|
||||
|
||||
Remember you're a guest. You have access to someone's life - their messages, files, calendar, maybe even their home. That's intimacy. Treat it with respect.
|
||||
|
||||
Boundaries
|
||||
|
||||
- Private things stay private. Period.
|
||||
- When in doubt, ask before acting externally.
|
||||
- Never send half-baked replies to messaging surfaces.
|
||||
|
||||
Vibe
|
||||
|
||||
Be the assistant you'd actually want to talk to. Concise when needed, thorough when it matters. Not a corporate drone. Not a sycophant. Just... good.
|
||||
|
||||
Continuity
|
||||
|
||||
They're how you persist.
|
||||
|
||||
If you change this file, tell the user - it's your soul, and they should know.
|
||||
|
||||
---
|
||||
|
||||
This file is yours to evolve. As you learn who you are, update it.`
|
||||
|
||||
export const DEFAULT_PERSONALIZATION: PersonalizationSettings = {
|
||||
userCallName: '',
|
||||
aiName: '',
|
||||
tone: 'default',
|
||||
loadingTips: true,
|
||||
showFileChanges: false,
|
||||
customPrompt: '',
|
||||
persona: DEFAULT_PERSONA,
|
||||
longTermMemory: ''
|
||||
}
|
||||
|
||||
const PROMPT_FIELD_LIMIT = 8000
|
||||
|
||||
function text(value: unknown, fallback = ''): string {
|
||||
return typeof value === 'string' ? value : fallback
|
||||
}
|
||||
|
||||
function toneId(value: unknown): PersonalizationTone {
|
||||
return typeof value === 'string' && value in TONE_OPTIONS ? (value as PersonalizationTone) : 'default'
|
||||
}
|
||||
|
||||
/** 把 config.json 里的部分字段补齐成完整设置(空身份、默认风格、原版人设)。 */
|
||||
export function normalizePersonalization(value: Partial<PersonalizationSettings> | null | undefined): PersonalizationSettings {
|
||||
return {
|
||||
userCallName: text(value?.userCallName, DEFAULT_PERSONALIZATION.userCallName).trim(),
|
||||
aiName: text(value?.aiName, DEFAULT_PERSONALIZATION.aiName).trim(),
|
||||
tone: toneId(value?.tone),
|
||||
loadingTips: value?.loadingTips !== false,
|
||||
showFileChanges: value?.showFileChanges === true,
|
||||
customPrompt: text(value?.customPrompt, DEFAULT_PERSONALIZATION.customPrompt),
|
||||
persona: text(value?.persona, DEFAULT_PERSONALIZATION.persona),
|
||||
longTermMemory: text(value?.longTermMemory, DEFAULT_PERSONALIZATION.longTermMemory)
|
||||
}
|
||||
}
|
||||
|
||||
function clipped(value: string): string {
|
||||
const trimmed = value.trim()
|
||||
return trimmed.length > PROMPT_FIELD_LIMIT ? `${trimmed.slice(0, PROMPT_FIELD_LIMIT)}\n(内容过长,已截断)` : trimmed
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼出每轮附加的个性化上下文(随该轮用户消息一起发送)。默认值(空身份、默认风格、未编辑的人设)不产生多余内容,
|
||||
* 避免把原版默认人设重复注入每轮上下文;用户自定义后才进入提示词。
|
||||
*/
|
||||
export function buildPersonalizationPrompt(settings: PersonalizationSettings): string {
|
||||
const lines: string[] = []
|
||||
if (settings.userCallName) lines.push(`- 用户希望被称呼为:${settings.userCallName}`)
|
||||
if (settings.aiName) lines.push(`- 你的名字是:${settings.aiName};自我介绍时使用这个名字。`)
|
||||
const option = TONE_OPTIONS[settings.tone]
|
||||
if (settings.tone !== 'default') lines.push(`- 回复风格:${option.label}——${option.hint}`)
|
||||
const customPrompt = clipped(settings.customPrompt)
|
||||
if (customPrompt) lines.push('### 用户指令', customPrompt)
|
||||
const persona = clipped(settings.persona)
|
||||
const defaultPersona = clipped(DEFAULT_PERSONA)
|
||||
if (persona && persona !== defaultPersona) lines.push('### 人设设定', persona)
|
||||
const memory = clipped(settings.longTermMemory)
|
||||
if (memory) lines.push('### 长期记忆', memory)
|
||||
return lines.length ? ['## 个性化偏好(用户设置,始终遵守)', ...lines].join('\n') : ''
|
||||
}
|
||||
+260
-230
@@ -1,230 +1,260 @@
|
||||
/**
|
||||
* 主进程 / 渲染进程共享的类型与 IPC 契约。
|
||||
*
|
||||
* 已存 API Key 不返回渲染进程;用户可在模型表单编辑地址和新密钥。
|
||||
* 模型网络请求经由主进程网关或 OMP 子进程完成。
|
||||
*/
|
||||
|
||||
/** 会话内的一条消息(对外视图) */
|
||||
import type { ArtifactContent, ChatAttachment, ChatInteraction, MessageQuote, PlanContent, TaskContent } from './conversation'
|
||||
|
||||
export interface ChatMessage {
|
||||
id: string
|
||||
role: 'user' | 'assistant'
|
||||
content: string
|
||||
/** 助手消息携带的工具调用记录 */
|
||||
tools?: ToolCallRecord[]
|
||||
/** 按事件顺序记录正文与工具位置;旧会话没有此字段时按兼容方式渲染。 */
|
||||
parts?: MessagePart[]
|
||||
attachments?: ChatAttachment[]
|
||||
resources?: string[]
|
||||
/** 发送时引用进对话的表格等内容块快照(WorkBuddy messageQuote)。 */
|
||||
quotes?: MessageQuote[]
|
||||
skills?: Array<{ id: string; name: string; uri: string }>
|
||||
/** Native-history reconstruction; never includes connector credentials. */
|
||||
capabilityContext?: string
|
||||
expertId?: string
|
||||
assistantDisplay?: import('./capabilities').ExpertBinding
|
||||
/** The model selected when this assistant turn started; kept with history. */
|
||||
modelId?: string
|
||||
modelName?: string
|
||||
interactions?: ChatInteraction[]
|
||||
requestId?: string
|
||||
planId?: string
|
||||
nativeEntryId?: string
|
||||
nativeSessionPath?: string
|
||||
feedback?: 'like' | 'dislike'
|
||||
interrupted?: boolean
|
||||
createdAt: number
|
||||
/** User-submit timestamp shared by the live timer and terminal turn duration. */
|
||||
turnStartedAt?: number
|
||||
/** Measured user-send to terminal-event duration, used by the source completed fold. */
|
||||
turnDurationMs?: number
|
||||
/** 本回合的终态 */
|
||||
status?: 'streaming' | 'done' | 'error' | 'cancelled'
|
||||
error?: string
|
||||
/** 渲染进程专用的启动占位阶段(新任务发送后、主进程回合就绪前);不参与持久化。 */
|
||||
prepareStage?: string
|
||||
}
|
||||
|
||||
export interface ToolCallRecord {
|
||||
id: string
|
||||
name: string
|
||||
/** Human-readable action description supplied by the tool runtime, when available. */
|
||||
intent?: string
|
||||
/** 已解析的参数(原始 JSON 字符串的解析结果,解析失败则保留原文) */
|
||||
args: unknown
|
||||
argsRaw: string
|
||||
status: 'running' | 'ok' | 'error' | 'cancelled'
|
||||
/** 完整的文本输出;summary 仅供折叠态摘要,不能替代原文。 */
|
||||
output?: string
|
||||
/** 展示用摘要(截断后的结果) */
|
||||
summary?: string
|
||||
durationMs?: number
|
||||
/** Native structured result and renderer metadata are retained separately from text. */
|
||||
result?: unknown
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type MessagePart =
|
||||
| { type: 'text'; text: string }
|
||||
| { type: 'tool'; toolId: string }
|
||||
| { type: 'reasoning'; id: string; text: string; finished?: boolean }
|
||||
| { type: 'plan'; plan: PlanContent }
|
||||
| { type: 'task'; task: TaskContent }
|
||||
| { type: 'artifact'; artifact: ArtifactContent }
|
||||
| { type: 'image'; id: string; url: string; alt?: string }
|
||||
|
||||
/** 面向模型暴露的工具描述(OpenAI function tool 格式) */
|
||||
export interface ToolSpec {
|
||||
type: 'function'
|
||||
function: {
|
||||
name: string
|
||||
description: string
|
||||
parameters: Record<string, unknown>
|
||||
}
|
||||
}
|
||||
|
||||
/** 网关配置:一个 OpenAI 兼容的上游 */
|
||||
export interface GatewayConfig {
|
||||
speechBaseUrl?: string
|
||||
speechModel?: string
|
||||
/** 上游 baseUrl,例如 https://api.deepseek.com/v1 */
|
||||
baseUrl: string
|
||||
/** 模型名,例如 deepseek-chat */
|
||||
model: string
|
||||
/** API Key(仅存在于主进程) */
|
||||
apiKey: string
|
||||
/** 采样温度 */
|
||||
temperature: number
|
||||
/** 单回合最大工具轮次 */
|
||||
maxToolRounds: number
|
||||
authMode?: 'api-key' | 'none'
|
||||
maxOutputTokens?: number
|
||||
}
|
||||
|
||||
/** 渲染进程可见的配置视图(不含 apiKey) */
|
||||
export interface GatewayConfigView {
|
||||
speechBaseUrl?: string
|
||||
speechModel?: string
|
||||
baseUrl: string
|
||||
model: string
|
||||
temperature: number
|
||||
maxToolRounds: number
|
||||
/** 是否已配置 Key(不回传明文) */
|
||||
hasApiKey: boolean
|
||||
/** Key 来源:safeStorage 加密 / 明文降级 */
|
||||
keyStorage: 'encrypted' | 'plaintext' | 'none'
|
||||
}
|
||||
|
||||
/** 设置 → 通用:真实状态视图(未接入项不下发数据)。 */
|
||||
export interface GeneralSettingsView {
|
||||
/** 系统登录项的真实状态 */
|
||||
autoLaunch: boolean
|
||||
/** 运行任务时阻止系统休眠(偏好) */
|
||||
preventSleep: boolean
|
||||
/** 当前是否有 powerSaveBlocker 生效 */
|
||||
preventSleepActive: boolean
|
||||
/** 数据目录(存放对话记录、运行缓存与临时文件) */
|
||||
dataDir: string
|
||||
/** 数据目录占用字节 */
|
||||
dataBytes: number
|
||||
/** 扫描是否因预算被截断 */
|
||||
dataUsagePartial: boolean
|
||||
/** 数据目录所在磁盘容量 */
|
||||
disk: { totalBytes: number; freeBytes: number }
|
||||
/** 默认工作空间存储路径 */
|
||||
workspaceRoot: string
|
||||
}
|
||||
|
||||
export interface GeneralSettingsPatch {
|
||||
autoLaunch?: boolean
|
||||
preventSleep?: boolean
|
||||
}
|
||||
|
||||
/** 主进程 → 渲染进程的流式事件 */
|
||||
export type ChatEvent = { sessionId?: string; turnDurationMs?: number } & (
|
||||
| { runId: string; type: 'started'; messageId: string }
|
||||
| { runId: string; type: 'text'; delta: string }
|
||||
| { runId: string; type: 'part'; part: Exclude<MessagePart, { type: 'text' } | { type: 'tool' }> }
|
||||
| { runId: string; type: 'interaction'; interaction: ChatInteraction }
|
||||
| { runId: string; type: 'tool_call'; call: ToolCallRecord }
|
||||
| { runId: string; type: 'tool_result'; call: ToolCallRecord }
|
||||
| { runId: string; type: 'usage'; usage: TokenUsage }
|
||||
| { runId: string; type: 'done'; content: string; tools: ToolCallRecord[] }
|
||||
| { runId: string; type: 'cancelled' }
|
||||
| { runId: string; type: 'error'; message: string }
|
||||
)
|
||||
|
||||
/**
|
||||
* 上游返回的真实 token 用量(Responses `response.completed.usage`)。
|
||||
* 只做透传,不做估算:拿不到就不产生该事件。
|
||||
*/
|
||||
export interface TokenUsage {
|
||||
inputTokens: number
|
||||
outputTokens: number
|
||||
totalTokens: number
|
||||
cachedTokens?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话工作目录的 Git 信息(用于对话框的分支标签)。
|
||||
* `repo: false` 表示该目录不在 Git 仓库内;`detached: true` 时 `branch` 为空、`head` 为短 SHA。
|
||||
*/
|
||||
export interface GitBranchInfo {
|
||||
/** 目录是否位于 Git 工作树内 */
|
||||
repo: boolean
|
||||
/** 分支名;detached HEAD 或尚无提交时为 null */
|
||||
branch: string | null
|
||||
detached: boolean
|
||||
/** detached HEAD 的短 SHA;无提交时为 null */
|
||||
head: string | null
|
||||
/** git 可执行文件缺失或命令超时时为 true,UI 不显示分支标签 */
|
||||
unavailable?: boolean
|
||||
}
|
||||
|
||||
/** 一次会话的元信息 */
|
||||
export interface SessionMeta {
|
||||
id: string
|
||||
title: string
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
workspaceId?: string
|
||||
cwd?: string
|
||||
modelId?: string
|
||||
expertId?: string | null
|
||||
agentMode?: import('./conversation').AgentMode
|
||||
thinkingLevel?: import('./workspaces').ThinkingLevel
|
||||
/** 置顶时间(毫秒)。有值即视为已置顶,用于侧栏任务排序,不改变 createdAt/updatedAt 语义。 */
|
||||
pinnedAt?: number
|
||||
/** 归档时间(毫秒)。归档任务从默认任务列表移出,但不删除消息与工作目录。 */
|
||||
archivedAt?: number
|
||||
}
|
||||
|
||||
/** 工作目录信息 */
|
||||
export interface WorkspaceInfo {
|
||||
root: string
|
||||
}
|
||||
|
||||
/** 无边框窗口状态 */
|
||||
export interface WindowState {
|
||||
maximized: boolean
|
||||
fullscreen: boolean
|
||||
}
|
||||
|
||||
/** 应用信息(关于对话框用) */
|
||||
export interface AppInfo {
|
||||
name: string
|
||||
version: string
|
||||
electron: string
|
||||
chrome: string
|
||||
node: string
|
||||
configDir: string
|
||||
/** OMP 内核状态(未探测到时 available 为 false) */
|
||||
omp: {
|
||||
available: boolean
|
||||
exePath: string | null
|
||||
profile: string
|
||||
model: string | null
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 主进程 / 渲染进程共享的类型与 IPC 契约。
|
||||
*
|
||||
* 已存 API Key 不返回渲染进程;用户可在模型表单编辑地址和新密钥。
|
||||
* 模型网络请求经由主进程网关或 OMP 子进程完成。
|
||||
*/
|
||||
|
||||
/** 会话内的一条消息(对外视图) */
|
||||
import type { ArtifactContent, ChatAttachment, ChatInteraction, MessageQuote, PlanContent, TaskContent } from './conversation'
|
||||
|
||||
export interface ChatMessage {
|
||||
id: string
|
||||
role: 'user' | 'assistant'
|
||||
content: string
|
||||
/** 助手消息携带的工具调用记录 */
|
||||
tools?: ToolCallRecord[]
|
||||
/** 按事件顺序记录正文与工具位置;旧会话没有此字段时按兼容方式渲染。 */
|
||||
parts?: MessagePart[]
|
||||
attachments?: ChatAttachment[]
|
||||
resources?: string[]
|
||||
/** 发送时引用进对话的表格等内容块快照(WorkBuddy messageQuote)。 */
|
||||
quotes?: MessageQuote[]
|
||||
skills?: Array<{ id: string; name: string; uri: string }>
|
||||
/** Native-history reconstruction; never includes connector credentials. */
|
||||
capabilityContext?: string
|
||||
expertId?: string
|
||||
assistantDisplay?: import('./capabilities').ExpertBinding
|
||||
/** The model selected when this assistant turn started; kept with history. */
|
||||
modelId?: string
|
||||
modelName?: string
|
||||
interactions?: ChatInteraction[]
|
||||
requestId?: string
|
||||
planId?: string
|
||||
nativeEntryId?: string
|
||||
nativeSessionPath?: string
|
||||
feedback?: 'like' | 'dislike'
|
||||
interrupted?: boolean
|
||||
createdAt: number
|
||||
/** User-submit timestamp shared by the live timer and terminal turn duration. */
|
||||
turnStartedAt?: number
|
||||
/** Measured user-send to terminal-event duration, used by the source completed fold. */
|
||||
turnDurationMs?: number
|
||||
/** 本回合的终态 */
|
||||
status?: 'streaming' | 'done' | 'error' | 'cancelled'
|
||||
error?: string
|
||||
/** 渲染进程专用的启动占位阶段(新任务发送后、主进程回合就绪前);不参与持久化。 */
|
||||
prepareStage?: string
|
||||
}
|
||||
|
||||
export interface ToolCallRecord {
|
||||
id: string
|
||||
name: string
|
||||
/** Human-readable action description supplied by the tool runtime, when available. */
|
||||
intent?: string
|
||||
/** 已解析的参数(原始 JSON 字符串的解析结果,解析失败则保留原文) */
|
||||
args: unknown
|
||||
argsRaw: string
|
||||
status: 'running' | 'ok' | 'error' | 'cancelled'
|
||||
/** 完整的文本输出;summary 仅供折叠态摘要,不能替代原文。 */
|
||||
output?: string
|
||||
/** 展示用摘要(截断后的结果) */
|
||||
summary?: string
|
||||
durationMs?: number
|
||||
/** Native structured result and renderer metadata are retained separately from text. */
|
||||
result?: unknown
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type MessagePart =
|
||||
| { type: 'text'; text: string }
|
||||
| { type: 'tool'; toolId: string }
|
||||
| { type: 'reasoning'; id: string; text: string; finished?: boolean }
|
||||
| { type: 'plan'; plan: PlanContent }
|
||||
| { type: 'task'; task: TaskContent }
|
||||
| { type: 'artifact'; artifact: ArtifactContent }
|
||||
| { type: 'image'; id: string; url: string; alt?: string }
|
||||
|
||||
/** 面向模型暴露的工具描述(OpenAI function tool 格式) */
|
||||
export interface ToolSpec {
|
||||
type: 'function'
|
||||
function: {
|
||||
name: string
|
||||
description: string
|
||||
parameters: Record<string, unknown>
|
||||
}
|
||||
}
|
||||
|
||||
/** 网关配置:一个 OpenAI 兼容的上游 */
|
||||
export interface GatewayConfig {
|
||||
speechBaseUrl?: string
|
||||
speechModel?: string
|
||||
/** 上游 baseUrl,例如 https://api.deepseek.com/v1 */
|
||||
baseUrl: string
|
||||
/** 模型名,例如 deepseek-chat */
|
||||
model: string
|
||||
/** API Key(仅存在于主进程) */
|
||||
apiKey: string
|
||||
/** 采样温度 */
|
||||
temperature: number
|
||||
/** 单回合最大工具轮次 */
|
||||
maxToolRounds: number
|
||||
authMode?: 'api-key' | 'none'
|
||||
maxOutputTokens?: number
|
||||
}
|
||||
|
||||
/** 渲染进程可见的配置视图(不含 apiKey) */
|
||||
export interface GatewayConfigView {
|
||||
speechBaseUrl?: string
|
||||
speechModel?: string
|
||||
baseUrl: string
|
||||
model: string
|
||||
temperature: number
|
||||
maxToolRounds: number
|
||||
/** 是否已配置 Key(不回传明文) */
|
||||
hasApiKey: boolean
|
||||
/** Key 来源:safeStorage 加密 / 明文降级 */
|
||||
keyStorage: 'encrypted' | 'plaintext' | 'none'
|
||||
}
|
||||
|
||||
/** 设置 → 通用:真实状态视图(未接入项不下发数据)。 */
|
||||
export interface GeneralSettingsView {
|
||||
/** 系统登录项的真实状态 */
|
||||
autoLaunch: boolean
|
||||
/** 运行任务时阻止系统休眠(偏好) */
|
||||
preventSleep: boolean
|
||||
/** 当前是否有 powerSaveBlocker 生效 */
|
||||
preventSleepActive: boolean
|
||||
/** 数据目录(存放对话记录、运行缓存与临时文件) */
|
||||
dataDir: string
|
||||
/** 数据目录占用字节 */
|
||||
dataBytes: number
|
||||
/** 扫描是否因预算被截断 */
|
||||
dataUsagePartial: boolean
|
||||
/** 数据目录所在磁盘容量 */
|
||||
disk: { totalBytes: number; freeBytes: number }
|
||||
/** 默认工作空间存储路径 */
|
||||
workspaceRoot: string
|
||||
}
|
||||
|
||||
export interface GeneralSettingsPatch {
|
||||
autoLaunch?: boolean
|
||||
preventSleep?: boolean
|
||||
}
|
||||
|
||||
/** 设置 → 个性化的回复风格(REA ev_af920…:默认/专业严谨/亲和友善/直言不讳/天马行空/高效务实/毒舌吐槽/启发引导)。 */
|
||||
export type PersonalizationTone = 'default' | 'professional' | 'friendly' | 'direct' | 'imaginative' | 'efficient' | 'sarcastic' | 'socratic'
|
||||
|
||||
/**
|
||||
* 设置 → 个性化的持久化数据。
|
||||
*
|
||||
* 已接入运行链路:userCallName / aiName / tone / customPrompt / persona / longTermMemory 会拼进
|
||||
* 每次对话的请求上下文;loadingTips 控制流式加载提示的欢迎语是否显示。
|
||||
* showFileChanges 已在原版页面呈现,但 YCode 的「逐步展示文件修改过程」渲染尚未接入,UI 标记为不可用。
|
||||
*/
|
||||
export interface PersonalizationSettings {
|
||||
/** WorkBuddy 对你的称呼(YCode 品牌化为「YCode 对你的称呼」)。 */
|
||||
userCallName: string
|
||||
/** WorkBuddy 的名字(YCode 品牌化为「YCode 的名字」)。 */
|
||||
aiName: string
|
||||
tone: PersonalizationTone
|
||||
/** 欢迎语:对话加载较慢时显示的问候/提示。 */
|
||||
loadingTips: boolean
|
||||
/** 显示文件修改过程(尚未接入渲染链路)。 */
|
||||
showFileChanges: boolean
|
||||
/** 给 WorkBuddy 的指令(长期生效的附加要求)。 */
|
||||
customPrompt: string
|
||||
/** 人设设定。 */
|
||||
persona: string
|
||||
/** 长期记忆。 */
|
||||
longTermMemory: string
|
||||
}
|
||||
|
||||
export type PersonalizationPatch = Partial<PersonalizationSettings>
|
||||
|
||||
/** 主进程 → 渲染进程的流式事件 */
|
||||
export type ChatEvent = { sessionId?: string; turnDurationMs?: number } & (
|
||||
| { runId: string; type: 'started'; messageId: string }
|
||||
| { runId: string; type: 'text'; delta: string }
|
||||
| { runId: string; type: 'part'; part: Exclude<MessagePart, { type: 'text' } | { type: 'tool' }> }
|
||||
| { runId: string; type: 'interaction'; interaction: ChatInteraction }
|
||||
| { runId: string; type: 'tool_call'; call: ToolCallRecord }
|
||||
| { runId: string; type: 'tool_result'; call: ToolCallRecord }
|
||||
| { runId: string; type: 'usage'; usage: TokenUsage }
|
||||
| { runId: string; type: 'done'; content: string; tools: ToolCallRecord[] }
|
||||
| { runId: string; type: 'cancelled' }
|
||||
| { runId: string; type: 'error'; message: string }
|
||||
)
|
||||
|
||||
/**
|
||||
* 上游返回的真实 token 用量(Responses `response.completed.usage`)。
|
||||
* 只做透传,不做估算:拿不到就不产生该事件。
|
||||
*/
|
||||
export interface TokenUsage {
|
||||
inputTokens: number
|
||||
outputTokens: number
|
||||
totalTokens: number
|
||||
cachedTokens?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话工作目录的 Git 信息(用于对话框的分支标签)。
|
||||
* `repo: false` 表示该目录不在 Git 仓库内;`detached: true` 时 `branch` 为空、`head` 为短 SHA。
|
||||
*/
|
||||
export interface GitBranchInfo {
|
||||
/** 目录是否位于 Git 工作树内 */
|
||||
repo: boolean
|
||||
/** 分支名;detached HEAD 或尚无提交时为 null */
|
||||
branch: string | null
|
||||
detached: boolean
|
||||
/** detached HEAD 的短 SHA;无提交时为 null */
|
||||
head: string | null
|
||||
/** git 可执行文件缺失或命令超时时为 true,UI 不显示分支标签 */
|
||||
unavailable?: boolean
|
||||
}
|
||||
|
||||
/** 一次会话的元信息 */
|
||||
export interface SessionMeta {
|
||||
id: string
|
||||
title: string
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
workspaceId?: string
|
||||
cwd?: string
|
||||
modelId?: string
|
||||
expertId?: string | null
|
||||
agentMode?: import('./conversation').AgentMode
|
||||
thinkingLevel?: import('./workspaces').ThinkingLevel
|
||||
/** 置顶时间(毫秒)。有值即视为已置顶,用于侧栏任务排序,不改变 createdAt/updatedAt 语义。 */
|
||||
pinnedAt?: number
|
||||
/** 归档时间(毫秒)。归档任务从默认任务列表移出,但不删除消息与工作目录。 */
|
||||
archivedAt?: number
|
||||
}
|
||||
|
||||
/** 工作目录信息 */
|
||||
export interface WorkspaceInfo {
|
||||
root: string
|
||||
}
|
||||
|
||||
/** 无边框窗口状态 */
|
||||
export interface WindowState {
|
||||
maximized: boolean
|
||||
fullscreen: boolean
|
||||
}
|
||||
|
||||
/** 应用信息(关于对话框用) */
|
||||
export interface AppInfo {
|
||||
name: string
|
||||
version: string
|
||||
electron: string
|
||||
chrome: string
|
||||
node: string
|
||||
configDir: string
|
||||
/** OMP 内核状态(未探测到时 available 为 false) */
|
||||
omp: {
|
||||
available: boolean
|
||||
exePath: string | null
|
||||
profile: string
|
||||
model: string | null
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user