Local-first AI agent workspace for coding, writing, design, research, and automation — one runtime for desktop GUI and TUI.

RAW Doc

AGENTS

Agent Runtime Notes

The Kun desktop app has one live agent runtime: the bundled Kun runtime.
The same runtime also serves the standalone Kun TUI and non-interactive
clients. GUI and TUI are independent clients that may be active at the same
time; neither client owns the runtime lifecycle or the canonical model
configuration.

Do not add a second live provider, provider switcher, runtime diagnostics panel,
or legacy CodeWhale/Reasonix process path. Code (including Design tasks), Work,
and Connect phone all enter the same Kun HTTP/SSE boundary. Connect phone still
uses the internal claw name, and Work retains the internal write name, for compatibility.

Client Surface Boundary

- Every turn records its initiating surface (gui, tui, cli, api, im,
or extension). Continuations and delegated child turns inherit it.
- Provider kind gui is reserved for capabilities that require the desktop
workbench, such as Design canvas mutation or Computer Use. Those providers
must not be advertised or executable on TUI/CLI/API/IM turns.
- Runtime-backed goals, todos, plans, Skills, MCP, attachments, approvals,
structured input, and subagents are shared Kun capabilities, not GUI tools.
- Keep the immutable Kun system prompt client-neutral. Put interface-specific
guidance in the dynamic per-turn context after the stable prefix.
- Never switch a process-global tool registry or prompt based on whichever
client connected most recently; GUI and TUI can run concurrently.

Allowed Extension Path

1. Add protocol fields in kun/src/contracts/.
2. Add agent behavior in kun/src/loop/, kun/src/services/, or a
new port/adapter under kun/src/ports/ and kun/src/adapters/.
3. Add HTTP endpoints under kun/src/server/routes/.
4. Map the endpoint/event in src/renderer/src/agent/kun-runtime.ts and
src/renderer/src/agent/kun-mapper.ts.
5. Add settings only under agents.kun.

Prompt-Managed Plan Worktrees

- agents.kun.lab.planWorktree.enabled gates this experiment and defaults to
false. It applies only to Direct plan builds; Graph keeps its normal current-
workspace flow and its own node isolation.
- On execution, Renderer first saves the plan, then reads the exact local
repository root, checked-out branch, and dirty-file count through the generic
Git branch API. A non-Git workspace, unavailable Git, or detached HEAD blocks
the send with a concrete error.
- Renderer injects a fixed Git lifecycle protocol and the authoritative plan
snapshot into the next user input on the current task. It does not fork or
select another task, change the task workspace, close the plan panel, create
a host run record, or monitor integration.
- The Agent creates a uniquely named temporary branch/worktree, performs all
implementation and validation there, rebases when the target moved, uses
merge --ff-only, and cleans up only after ancestry or unchanged-work proof.
- Uncommitted source-checkout changes remain exactly as-is and are excluded
from the worktree baseline. The Agent must never stash, reset, clean, switch,
commit, or otherwise manipulate them. If they block integration, preserve the
temporary worktree/branch and report the recovery details.
- Repository paths, branch names, prefixes, titles, and plan Markdown are
structurally encoded inside the user input. None of this dynamic context may
enter the immutable system prefix, including when switching Code and Design.
- Legacy planBuildRunId and admission fields may still be parsed from stored
history, but they are inert: they do not freeze input, recover a run, rebind a
workspace, or receive special task presentation.

Forbidden Paths

- No AgentSwitcher.
- No ConnectionStatusBar.
- No RuntimeDiagnosticsDialog or runtime self-check UI.
- No CodeWhale/Reasonix adapter, process manager, RPC bridge, updater, or
importer.
- No legacy drawing/painting starter card outside the current Design mode.
- No /usage or /runtime slash command that opens a runtime control panel.
The standalone TUI may expose /usage as a read-only report backed by
GET /v1/usage; it must not add runtime diagnostics or control actions.

Legacy Data Rule

Old persisted keys may be read only inside settings migration:

- agentProvider: codewhale | reasonix | deepseek-runtime maps to kun.
- agents.codewhale, agents.reasonix, and legacy deepseek values seed
agents.kun once.
- Saved settings must contain only agents.kun.
- Old Connect phone (internal Claw) agentThreadIds.codewhale/reasonix fold into
agentThreadIds.kun.

Verification

Run:

bash
npm run typecheck
npm test
npm run build

Manual smoke:

- Code can create a Kun thread, stream a reply, approve/deny tools, and
interrupt a turn.
- CodeWhale parity endpoints still work through Kun: thread search/archive
filters, fork, session resume, request_user_input submit/cancel, and usage.
- Cache telemetry uses DeepSeek native prompt_cache_hit_tokens /
prompt_cache_miss_tokens; hot Kun turns should stay above 90% cache
hit after the stable prefix is warm.
- Immutable prefix drift and malformed tool-call/tool-result history must be
caught before a request reaches DeepSeek.
- A Code-workbench conversation can choose Code or Design for every next turn;
accepted turns freeze their own surface while the Code-owned thread and
timeline remain stable. The first accepted Design turn locks only its
document/output/style profile, and later Code turns remain valid.
- With the Lab experiment enabled, a Direct plan build sends the prompt-managed
worktree protocol on the same task, leaves dirty source files untouched, and
preserves unresolved worktree/branch state for manual recovery. Graph does
not receive that protocol.
- Work can open the workspace, request inline completion, and use selected-text
assistant actions.
- Connect phone can save settings and run a manual task through a Kun thread.
- Settings -> Agents shows only Kun.

The full plan is in
docs/kun-architecture.md.

---

AGENTS.Zh CN

代理运行时说明

Kun 桌面应用当前只有一个可运行的本地 Agent 运行时:仓库自带的同名 Kun 运行时。

不要新增第二套运行时、运行时切换器、运行时诊断面板,或旧的 CodeWhale / Reasonix 进程路径。Code(含 Design 任务)、Work、连接手机都统一走同一个 Kun HTTP/SSE 边界。连接手机在代码内部仍沿用 claw 命名,Work 内部仍沿用 write 命名,作为兼容标识。

允许的扩展路径

1. 在 kun/src/contracts/ 中新增协议字段。
2. 在 kun/src/loop/kun/src/services/kun/src/ports/ / kun/src/adapters/ 下新增端口与适配器来实现新行为。
3. 在 kun/src/server/routes/ 下新增 HTTP 接口。
4. 在 src/renderer/src/agent/kun-runtime.tssrc/renderer/src/agent/kun-mapper.ts 中完成端点与事件映射。
5. 仅在 agents.kun 下新增设置项。

提示词管理的计划 Worktree 边界

- agents.kun.lab.planWorktree.enabled 是默认关闭的实验开关,仅适用于 Direct 计划构建;
Graph 保持当前工作区流程和自身节点隔离。
- Renderer 点击执行时先保存计划,再用通用 Git 分支 API 读取精确仓库根、本地当前分支和
脏文件数。非 Git、Git 不可用或 detached HEAD 会阻止发送,脏工作区不会被阻止。
- 应用只把固定 Git 生命周期协议和权威计划快照注入当前任务的下一条 user input,不创建
或切换任务、不改变 workspace、不关闭计划面板,也不持久化或监听宿主运行记录。
- Agent 从目标分支的已提交 HEAD 创建唯一临时分支和 worktree,在其中实现、测试、提交、
必要时 rebase,并只用 merge --ff-only 合入。源 checkout 的未提交修改不进入基线,
且不得被 stash、reset、clean、切换或提交。
- 只有证明临时提交已包含在目标分支后才能非强制清理。测试失败、冲突无法可靠解决或合入
受阻时必须保留 worktree 和分支并报告恢复信息。
- 动态分支、路径、标题、脏文件数和 Markdown 经过结构化编码后只进入 user input;
Code / Design 切换也不得改变 immutable system prefix。
- 旧 planBuildRunId 等字段仅作历史解析,不再触发恢复、输入冻结、workspace 重绑或特殊展示。

禁止路径

- 不要新增 AgentSwitcher
- 不要新增 ConnectionStatusBar
- 不要新增 RuntimeDiagnosticsDialog 或运行时自检 UI。
- 不要恢复 CodeWhale/Reasonix 的适配器、进程管理、RPC 桥、更新器或导入器。
- 不要恢复独立于当前 Design 模式之外的旧绘图/绘画启动卡片。
- 不要新增打开运行时控制面板的 /usage/runtime 斜杠命令。

旧数据兼容规则

旧的持久化 key 仅在 settings 迁移时按只读路径使用:

- agentProvider: codewhale | reasonix | deepseek-runtime 映射为 kun
- agents.codewhaleagents.reasonix 和旧 deepseek 的值会一次性写入 agents.kun
- 保存后的 settings 仅保留 agents.kun
- 旧连接手机(内部 Claw)的 agentThreadIds.codewhale/reasonix 会并入 agentThreadIds.kun

验证清单

执行:

bash
npm run typecheck
npm test
npm run build

手工冒烟检查:

- Code 可以创建 Kun 会话、流式回传回复、进行工具审批/拒绝、以及中断回合。
- CodeWhale 的等价能力应保持在 Kun 下可用:会话搜索/归档筛选、fork、会话恢复、request_user_input 提交与取消、usage 查询。
- 缓存指标使用 DeepSeek 原生 prompt_cache_hit_tokens / prompt_cache_miss_tokens;在稳定前缀热身后,热门对话的 hit rate 应长期保持在 90% 以上。
- 不可变前缀漂移与异常的 tool-call/tool-result 历史必须在请求下发 DeepSeek 前被拦截。
- Code 可在同一会话中切换下一回合的 Code / Design 意图;Design 在共享时间线中创建、
迭代、预览与导出设计稿。
- 开启实验后,Direct 计划构建在当前任务发送提示词 Worktree 协议,保持源目录脏文件原样,
并在失败时保留现场;Graph 不注入该协议。
- Work 可以打开工作区、发起 inline 补全、使用选中文本助手动作。
- 连接手机可以保存设置,并通过 Kun 会话执行手工任务。
- 设置 -> Agent 仅显示 Kun。

完整方案见 docs/kun-architecture.md

---

CONTRIBUTING.En

Contributing

Simplified Chinese

Thank you for contributing to Kun.

This document explains how contributors should collaborate on the project, what standards to follow, and how changes should be proposed.

Project Taste

Code is easy. Good taste is rare.

For Kun, taste means clear workflows, restrained interfaces, humane copy, and behavior that feels obvious after one use. Strong contributions show judgment, not just implementation.

Contribution Scope

Contributions are welcome for:

- bug fixes
- UI and UX improvements
- runtime integration improvements
- documentation
- localization
- build and release workflow improvements

Branch Strategy

The expected branch flow is:

- develop: active collaboration and daily integration branch
- master: stable release branch, updated by maintainers from develop
- feature branches: optional short-lived branches created from develop

Rules:

- Do not develop directly on master
- Prefer starting work from the latest develop
- If you create a feature branch, branch off from develop
- Open pull requests into develop unless maintainers explicitly request another base branch

Before You Start

1. Make sure your local repository is up to date.
2. Switch to develop.
3. Install dependencies with npm install.
4. Confirm the project starts or builds successfully before making changes.

Shape of a Typical PR

A well-structured PR for Kun is focused and self-contained. It typically:

- Touches 1-3 new files and modifies 2-5 existing files for wiring
- Scopes to a single feature, fix, or documentation update
- Includes a video or GIF if the UI changed
- Includes unit tests if project logic changed
- Passes npm run typecheck, npm run build, and npm run test

If you discover related work that needs doing, open a separate issue rather than expanding the PR scope.

Local Development Checklist

Before opening a PR, contributors should verify:

- the app still runs in development with npm run dev
- type checking passes with npm run typecheck
- production build passes with npm run build
- unit tests pass with npm run test
- UI changes include a video or GIF that shows the changed flow
- logic changes include unit tests for the changed behavior
- documentation is updated if behavior, setup, or workflow changed
- localization is updated if user-facing text changed

CI Verification Commands

bash

Type checking


npm run typecheck

Production build


npm run build

Unit tests


npm run test

Full development smoke test


npm run dev

Coding Expectations

- Keep changes focused and scoped
- Avoid unrelated refactors in the same PR
- Follow existing project structure and naming conventions
- Prefer readable code over clever code
- Preserve cross-platform behavior where possible
- Do not commit secrets, API keys, tokens, or machine-specific private paths

Documentation Expectations

When your change affects project usage or collaboration, update the relevant docs:

- README.md and README.en.md for project-level usage
- docs/DEVELOPMENT.md and docs/DEVELOPMENT.zh-CN.md for workflow/process updates
- this contributing guide when standards change

Pull Request Standards

Each PR should:

- have a clear and specific title
- explain what changed and why
- describe user-facing impact
- mention any setup, migration, or compatibility notes
- stay reasonably small when possible

Recommended PR structure:

text

Summary

What this PR does in 1-2 sentences.

Why

The problem or gap it addresses.

Validation

How you verified the change (commands run, manual tests performed).

Media

Attach a video or GIF if UI changed. Screenshots are welcome as extra context.

Tests

List unit tests added or updated if project logic changed.

For most contributions, opening the PR from a short-lived feature branch is preferred over pushing directly to develop or master.

Review Standards

Reviewers should evaluate:

- correctness
- regressions
- product taste and interaction quality
- clarity and maintainability
- consistency with current architecture
- documentation completeness
- whether validation steps were actually performed

Commit Guidance

Good commits are:

- small enough to review
- logically grouped
- written with clear commit messages

Follow conventional commits:

- feat: New feature
- fix: Bug fix
- docs: Documentation changes
- refactor: Code refactoring
- style: Formatting, UI polish
- chore: Maintenance tasks

Examples:

- docs: rewrite README and contribution guides
- feat: improve runtime connection recovery
- fix: handle missing Kun binary path

Reporting Issues

When reporting issues, please include:

- Operating system and version
- Kun version (from Settings or the About dialog)
- Bundled kun version (kun --version in the same directory, if available)
- Steps to reproduce the issue
- Expected vs actual behavior
- Relevant error messages, logs, or screenshots

Contributor Behavior

Please collaborate in a way that is:

- respectful
- clear
- constructive
- open to feedback

If a change is large or risky, align with maintainers before investing heavily in implementation.

Need Help?

If requirements are unclear, ask for clarification before making broad architectural or workflow changes. Feel free to open an issue for any questions about contributing.

License

External contributions are accepted under the Contributor License Agreement.
By submitting a contribution, you agree to grant the project owner the rights
described in the CLA, including the right to sublicense and relicense your
contribution as part of Kun under commercial, proprietary, noncommercial, or
other license terms.

The project itself remains available under the PolyForm Noncommercial License 1.0.0
unless the project owner grants a separate written commercial license.

---

CONTRIBUTING

Contributing

简体中文

Thank you for contributing to Kun.

This document explains how contributors should collaborate on the project, what standards to follow, and how changes should be proposed.

Project Taste

Code is easy. Good taste is rare.

For Kun, taste means clear workflows, restrained interfaces, humane copy, and behavior that feels obvious after one use. Strong contributions show judgment, not just implementation.

Contribution Scope

Contributions are welcome for:

- bug fixes
- UI and UX improvements
- runtime integration improvements
- documentation
- localization
- build and release workflow improvements

Branch Strategy

The expected branch flow is:

- develop: active collaboration and daily integration branch
- master: stable release branch, updated by maintainers from develop
- feature branches: optional short-lived branches created from develop

Rules:

- Do not develop directly on master
- Prefer starting work from the latest develop
- If you create a feature branch, branch off from develop
- Open pull requests into develop unless maintainers explicitly request another base branch

Before You Start

1. Make sure your local repository is up to date.
2. Switch to develop.
3. Install dependencies with npm install.
4. Confirm the project starts or builds successfully before making changes.

Shape of a Typical PR

A well-structured PR for Kun is focused and self-contained. It typically:

- Touches 1-3 new files and modifies 2-5 existing files for wiring
- Scopes to a single feature, fix, or documentation update
- Includes a video or GIF if the UI changed
- Includes unit tests if project logic changed
- Passes npm run typecheck, npm run build, and npm run test

If you discover related work that needs doing, open a separate issue rather than expanding the PR scope.

Local Development Checklist

Before opening a PR, contributors should verify:

- the app still runs in development with npm run dev
- type checking passes with npm run typecheck
- production build passes with npm run build
- unit tests pass with npm run test
- UI changes include a video or GIF that shows the changed flow
- logic changes include unit tests for the changed behavior
- documentation is updated if behavior, setup, or workflow changed
- localization is updated if user-facing text changed

CI Verification Commands

bash

Type checking


npm run typecheck

Production build


npm run build

Unit tests


npm run test

Full development smoke test


npm run dev

Coding Expectations

- Keep changes focused and scoped
- Avoid unrelated refactors in the same PR
- Follow existing project structure and naming conventions
- Prefer readable code over clever code
- Preserve cross-platform behavior where possible
- Do not commit secrets, API keys, tokens, or machine-specific private paths

Documentation Expectations

When your change affects project usage or collaboration, update the relevant docs:

- README.md and README.en.md for project-level usage
- docs/DEVELOPMENT.md and docs/DEVELOPMENT.zh-CN.md for workflow/process updates
- this contributing guide when standards change

Pull Request Standards

Each PR should:

- have a clear and specific title
- explain what changed and why
- describe user-facing impact
- mention any setup, migration, or compatibility notes
- stay reasonably small when possible

Recommended PR structure:

text

Summary

What this PR does in 1-2 sentences.

Why

The problem or gap it addresses.

Validation

How you verified the change (commands run, manual tests performed).

Media

Attach a video or GIF if UI changed. Screenshots are welcome as extra context.

Tests

List unit tests added or updated if project logic changed.

For most contributions, opening the PR from a short-lived feature branch is preferred over pushing directly to develop or master.

Review Standards

Reviewers should evaluate:

- correctness
- regressions
- product taste and interaction quality
- clarity and maintainability
- consistency with current architecture
- documentation completeness
- whether validation steps were actually performed

Commit Guidance

Good commits are:

- small enough to review
- logically grouped
- written with clear commit messages

Follow conventional commits:

- feat: New feature
- fix: Bug fix
- docs: Documentation changes
- refactor: Code refactoring
- style: Formatting, UI polish
- chore: Maintenance tasks

Examples:

- docs: rewrite README and contribution guides
- feat: improve runtime connection recovery
- fix: handle missing Kun binary path

Reporting Issues

When reporting issues, please include:

- Operating system and version
- Kun version (from Settings or the About dialog)
- Bundled kun version (kun --version in the same directory, if available)
- Steps to reproduce the issue
- Expected vs actual behavior
- Relevant error messages, logs, or screenshots

Contributor Behavior

Please collaborate in a way that is:

- respectful
- clear
- constructive
- open to feedback

If a change is large or risky, align with maintainers before investing heavily in implementation.

Need Help?

If requirements are unclear, ask for clarification before making broad architectural or workflow changes. Feel free to open an issue for any questions about contributing.

License

External contributions are accepted under the Contributor License Agreement.
By submitting a contribution, you agree to grant the project owner the rights
described in the CLA, including the right to sublicense and relicense your
contribution as part of Kun under commercial, proprietary, noncommercial, or
other license terms.

The project itself remains available under the PolyForm Noncommercial License 1.0.0
unless the project owner grants a separate written commercial license.

飞书 / Lark 流式 smoke 测试(发版前必跑)

本节对应 feature/feishu-streaming-with-live-fix 引入的飞书 / Lark SDK markdown 流式回复功能。发版前必须手工跑一遍下列 case。

自动化已覆盖

| 维度 | 覆盖方式 |
|---|---|
| 单条流式正常路径 | src/main/feishu-streamer.test.ts happy-path case |
| reasoning delta 过滤 | 同上,reasoning case |
| 跨 turn 过滤 | 同上,cross-turn case |
| append 失败 → setContent(partial) | 同上,append-failure case |
| SSE 订阅失败 → 一次性 send fallback | src/main/claw-runtime.test.ts streaming fallback case |
| feishuStream = false → 走原轮询 | 同上,feishuStream=false case |
| 集成 chat 视图实时性 | src/renderer/src/components/chat/MessageTimeline.tool-summary.test.ts live bubble case |
| onClawChannelActivity 自动切 thread | src/renderer/src/store/chat-store-navigation-actions.test.ts 路由 case |

手工 smoke checklist

- [ ] 单条对话:发"你好" → streaming 卡出现 → 1-2 秒内开始刷字
- [ ] 长回答:写一段代码 → 验证 30k 字符切卡能跨第二张卡
- [ ] 故意限流:把 outbound.retry.maxAttempts = 1 → 触发限流 → 观察 fallback 到一次性 send
- [ ] 故意 turn_failed:用会抛错的 MCP 工具 → 观察 partial 补发
- [ ] 群聊 @bot:replyInThread: true 仍生效,streaming 卡出现在 thread 里
- [ ] DM:replyInThread: false 默认
- [ ] Connect phone 视图实时性(关键 —— 本期修复):bot 收到消息后 chat 视图立即出现 streaming 文本,不卡
- [ ] 主动点击 thread:从 streaming 状态切到该 thread → blocks 与 liveAssistant 内容一致
- [ ] 跨 turn 隔离:在 turn A streaming 中再来一条消息触发 turn B → turn A 收尾,turn B 独立开卡

验证命令

bash
npm run typecheck
npm run lint
npm run test
npm run build
npm run build:kun

Electron 手动启动 + 真飞书账号(本机 + 测试机器人 appId/secret)


npm run dev

---

CONTRIBUTING.Zh CN

贡献说明

English

感谢你为 Kun 做贡献。

这份文档说明了贡献者应该如何协作、遵循什么标准,以及改动应如何提交。

项目品味

代码不难,难得的是好品味。

在 Kun 里,品味意味着清晰的流程、克制的界面、自然的文案,以及用一次就能理解的行为。好的贡献不只是把功能做出来,也要体现判断力。

贡献范围

欢迎以下方向的贡献:

- 缺陷修复
- UI / UX 优化
- 运行时集成改进
- 文档补充与修订
- 本地化内容完善
- 构建和发布流程优化

分支策略

建议采用以下分支流转方式:

- develop:协作与日常集成分支
- master:稳定发布分支,由维护者从 develop 合入
- 功能分支:可选,从 develop 拉出的短期分支

规则如下:

- 不要直接在 master 上开发
- 日常开发优先从最新 develop 开始
- 如果要建立功能分支,应从 develop 拉出
- 除非维护者明确指定,否则 PR 默认提到 develop

开始之前

1. 先确保本地仓库已同步到最新状态。
2. 切换到 develop 分支。
3. 运行 npm install 安装依赖。
4. 在修改前先确认项目可以正常启动或构建。

典型 PR 的结构

一个结构良好的 Kun PR 应该聚焦且自包含。通常:

- 涉及 1-3 个新文件,修改 2-5 个现有文件进行接入
- 范围限定在单个功能、修复或文档更新
- 如果界面有变化,附带视频或 GIF
- 如果项目逻辑有变化,附带单元测试
- 通过 npm run typechecknpm run buildnpm run test

如果在开发过程中发现其他需要处理的问题,请单独开 issue,不要扩大当前 PR 的范围。

本地开发检查清单

在发起 PR 之前,贡献者应至少确认:

- 应用可通过 npm run dev 正常开发运行
- npm run typecheck 通过
- npm run build 通过
- npm run test 通过
- UI 改动已附带展示变更流程的视频或 GIF
- 逻辑改动已为变更行为补充单元测试
- 如果改动影响使用方式、安装方式或流程,已同步更新文档
- 如果改动影响用户可见文案,已同步更新本地化内容

CI 验证命令

bash

类型检查


npm run typecheck

生产构建


npm run build

单元测试


npm run test

完整开发冒烟测试


npm run dev

代码要求

- 改动尽量聚焦、范围清晰
- 不要在同一个 PR 里夹带无关重构
- 尽量遵循现有项目结构和命名方式
- 优先写易读、易维护的代码
- 尽量保持跨平台行为一致
- 不要提交密钥、Token、API Key 或带有隐私的机器本地路径

文档要求

当你的改动会影响项目使用方式或协作方式时,请同步更新相关文档:

- README.mdREADME.en.md:项目级说明
- docs/DEVELOPMENT.mddocs/DEVELOPMENT.zh-CN.md:开发流程与协作规范
- 当前这份贡献说明:当贡献标准发生变化时更新

Pull Request 标准

每个 PR 应尽量满足:

- 标题清晰、具体
- 说明改了什么以及为什么改
- 写清楚用户侧会受到什么影响
- 如有安装、迁移、兼容性注意事项,应明确说明
- 规模尽量可控,方便评审

推荐 PR 描述结构:

text

Summary

用一两句话说明这个 PR 做了什么。

Why

要解决的问题或填补的空白。

Validation

如何验证改动(执行的命令、手动测试步骤)。

Media

如果界面有变化,附上视频或 GIF。截图可以作为补充材料。

Tests

如果项目逻辑有变化,列出新增或更新的单元测试。

对于大多数贡献,建议从短期功能分支发起 PR,而不是直接向 developmaster 推送提交。

评审标准

评审时建议重点关注:

- 正确性
- 是否引入回归
- 产品品味与交互质量
- 可读性与可维护性
- 是否符合当前架构方向
- 文档是否同步完整
- 校验步骤是否真实执行

Commit 建议

好的 Commit 应该:

- 颗粒度适中,便于审阅
- 按逻辑分组
- 提交信息清晰明确

遵循 conventional commits 规范:

- feat: 新功能
- fix: 缺陷修复
- docs: 文档变更
- refactor: 代码重构
- style: 格式化、界面调整
- chore: 维护任务

示例:

- docs: rewrite README and contribution guides
- feat: improve runtime connection recovery
- fix: handle missing Kun binary path

提交 Issue

提交 Issue 时,请尽可能包含以下信息:

- 操作系统及版本
- Kun 版本(可在设置页或关于对话框查看)
- 内置的 kun 版本(如可用,在同目录下执行 kun --version
- 复现步骤
- 预期行为与实际行为
- 相关错误信息、日志或截图

贡献者协作方式

请以以下方式协作:

- 尊重他人
- 表达清晰
- 反馈建设性
- 愿意讨论与调整

如果改动范围较大或风险较高,建议先和维护者对齐方向,再投入较多实现成本。

需要帮助?

如果需求或边界不明确,先沟通确认,再进行较大范围的架构或流程调整。如有任何关于贡献的问题,欢迎提 Issue。

许可证

外部贡献基于英文 Contributor License Agreement 接收。提交贡献即表示你同意 CLA 中的授权条款,包括项目所有者可将你的贡献作为 Kun 的一部分进行再授权、商业授权或其他形式授权。

项目本身默认仍基于 PolyForm Noncommercial License 1.0.0 发布;除非项目所有者另行提供书面商业授权。

---

DESIGN MODE

Design mode (设计模式)

Design mode is a Design task type inside the Code (chat) workbench, alongside
the top-level Work workspace. It is an AI design workstation: you describe a UI, an agent produces a
single-file interactive artifact, a live canvas renders it, and you iterate — with
a first-class, two-way bridge to the coding agent that ships it.

Implementation history lives in DESIGN_MODE_PLAN.md

(the original plan) and the feat(design): … commits on feature/design-mode.

---

1. What it is

The loop:

1. Describe a design in the right pane (with a design-context form: brand color,
tone, design-system preset, structured tokens).
2. The design agent writes one self-contained HTML document to a reserved path
under .kun-design/.
3. The center canvas live-renders it (a <webview>, refreshed as the agent
writes).
4. Iterate in place — each turn snapshots a new version.
5. Hand the approved design to the coding agent ("Implement in code"), which
publishes a shared design system and opens a fresh code thread.

Product positioning:

| Mode | Primary job | Output |
|---|---|---|
| Code | Work against a real repository, edit files, run commands, review changes, and ship implementation. | Code diffs, plans, todos, reviews, runnable app changes. |
| Design | Turn requirements, references, or existing UI into visual direction before implementation. | UI drafts, interactive HTML prototypes, graph artifacts, exported prototypes, shared DESIGN_SYSTEM.md. |
| Work | Draft, edit, polish, and export office documents. | Document workspaces, inline completions/edits, HTML / PDF / DOC / DOCX exports. |

Design mode is therefore not a legacy painting shortcut. It is the design leg
of Kun's requirement -> design -> plan -> code -> verify loop, sharing the same
runtime, provider configuration, approvals, and thread mechanics as Code and
Work.

Positioning: this is the only one of the surveyed tools where design lives inside
the coding-agent IDE with an organic design↔code loop (see §11).

---

2. Architecture

Design mode renders inside the Code Workbench, alongside Work — AppShell only
forks settings vs Workbench, so no shell change was needed.

- Route: AppRoute gains 'design'; openDesign / ensureDesignThreadForWorkspace
/ createDesignThread historically mirrored the internal write navigation actions
(chat-store-navigation-actions.ts). The design thread is tracked in a thin
registry (design/design-thread-registry.ts).
- Tabs: WorkspaceModeTabs renders Code / Work; the shared composer selects Code / Design task type.
- Three panes (components/design/):
- DesignSidebar — mode tabs + artifact list (per-kind icons, version count,
implement / delete / rename, provenance + drift badges) + "New design" / "New
canvas".
- DesignCanvas — the live canvas: preview / code (shiki-highlighted) / live
(the real running app from code mode) views, viewport switch, device frame,
light/dark background, export, reload. Early-returns DesignGraphView for
graph artifacts.
- DesignAgentPanel — the composer + a collapsible design-context form
(brand-color picker, tone chips, design-system preset) + iterate/new/busy hint.
- Store: design/design-workspace-store.ts (thin zustand store) holds artifacts,
active id, canvas/viewport state, the design context, settings-driven knobs, the
error banner, and the design-system hash. It is the single owner of artifact
mutations + on-disk persistence.

File map

text
src/renderer/src/design/
design-types.ts DesignArtifact, DesignArtifactKind ('html'|'graph'),
DesignCanvasView, viewports, createDesignArtifactId
design-context.ts DesignContext, presets, craft baseline,
formatDesignContextLines / formatDesignSystemMarkdown,
hashDesignSystem
design-turn-prompt.ts buildDesignTurnPrompt / buildDesignFromCodePrompt /
buildDesignImageNodePrompt
design-implement-prompt.ts buildImplementDesignPrompt (design → code)
design-graph.ts node-canvas doc model + topo sort
design-graph-run.ts runDesignNode (node → agent turn → await output)
design-artifact-persistence.ts meta.json sidecars + reconstruct-from-disk
design-workspace-store.ts the store
src/renderer/src/components/design/
DesignWorkspaceView / DesignSidebar / DesignCanvas / DesignAgentPanel / DesignGraphView

---

3. Artifact model & durability

A DesignArtifact = { id, kind, title, relativePath, createdAt, updatedAt,
versions[], implementedAt?, implementedThreadId?, implementedDesignSystemHash? }
.

- On disk: each artifact is a directory .kun-design/<id>/:
- HTML artifacts: v1.html, v2.html, … (the latest is the current document).
- Graph artifacts: graph.json (+ <nodeId>.html / <nodeId>.png node outputs).
- meta.json — a sidecar mirroring the artifact's metadata.
- HTML meta.json may include prototypeLinks[], the outgoing planned flow
transitions to other screen artifacts.
- Durability: the artifact list used to be in-memory only and was lost on reload.
Now every mutation (upsert / addVersion / markImplemented / rename) writes
meta.json, and on load rehydrateArtifacts() rebuilds the list from
window.kunGui.listWorkspaceDirectory('.kun-design') — reading each meta.json,
falling back to reconstructing from the on-disk files when a sidecar is missing.
removeArtifact deletes the whole dir (deleteWorkspaceEntry) and a
session-scoped removedArtifactIds guard stops a not-yet-flushed delete from
resurfacing on the next mount.

---

4. The design loop

- buildDesignTurnPrompt produces the turn: write ONE standalone HTML document to
the exact reserved path, build it incrementally (small skeleton then edit calls,
every payload < ~4000 chars), finish with </html>. The design context + the craft
baseline (§10) are appended.
- Live canvas: DesignCanvas watches the artifact file (startWriteWorkspaceFileWatch,
with a retry while the file does not yet exist) and reloads the webview (not remount)
once a complete document exists. The webview is used (not srcdoc/blob) because the
parent CSP kills inline-script iframes; authorizeWritePrototype allow-lists the
file:// URL.
- Iterate-in-place: when an artifact is active, the next turn passes its current
version as basePath ("read it first, apply ONLY the changes") instead of starting
fresh; a new version is appended.

---

5. Design ↔ code integration (the moat)

This is what makes design mode "organic", not isolated:

1. Implement in code (implementDesignInCode) — publishes the shared design system
to .kun-design/DESIGN_SYSTEM.md, builds an implement prompt, opens a fresh code
thread, dispatches the turn, and records provenance (markImplemented).
2. Shared design systemDESIGN_SYSTEM.md is the single source of truth both the
design agent and the code agent read; the design context is injected into both.
3. Reverse-design (sendDesignFromCode) — turn an existing UI file into an
iterable HTML mockup (the inverse of implement), closing the round trip. Exposed
from the file-preview panel ("Redesign").
4. Requirement → design (exploreSddRequirementInDesign) — a bridge from the SDD
requirement flow into the design canvas.
5. Unified preview — the canvas live view shows the real running app served by
code mode's dev server, so the design canvas and the real product share one surface.
6. Bidirectional drift — provenance is two-way:
- Design drift (updatedAt > implementedAt) → ⟳ badge ("re-implement").
- Code driftimplementDesignInCode snapshots a hash of the published
DESIGN_SYSTEM.md onto the artifact; on load the store re-reads DESIGN_SYSTEM.md
and compares, so an artifact implemented against an older shared design system
shows a ⚠ badge. ✓ = in sync.

---

6. Node canvas (graph artifacts)

A graph artifact is a small design pipeline on a React Flow canvas
(DesignGraphView), persisted as graph.json.

- Node kinds:
- prompt — carries text / context.
- design — generates an HTML artifact at .kun-design/<graphId>/<nodeId>.html.
- image — generates an image at .kun-design/<graphId>/<nodeId>.png (multimodal).
- Execution engine (runDesignNode + runGraph): Run topologically orders the
nodes (Kahn; cycles are rejected), then runs each design/image node in order
collecting upstream nodes' text along incoming edges, dispatching one agent turn, and
awaiting that node's output before the next. HTML nodes poll the file until it ends in
</html>; image nodes poll the directory until the .png appears. Per-node status is
live (running / done / error); design outputs preview inline (a docked webview panel),
image outputs render inline (readWorkspaceImage<img>).
- Minimap + grid background; hover-delete on nodes; empty-graph hint.

---

7. Settings

Design settings are a full slice (AppSettingsV1.design, DesignSettingsV1, ~20
fields: workspace root, brand/tone/preset, tokens (radius/density/font), model /
provider / reasoning effort, generation prompt, implement stack hint, inject-into-code,
publish-design-system, canvas defaults, live refresh, device frame, …). Rendered as a
multi-card settings tab.

Landmine — adding a design settings field touches 9 places or settings-sync

infinite-loops: DesignSettingsV1 + the patch type, defaultDesignSettings /

normalizeDesignSettings / mergeDesignSettings, the .strict()

designSettingsPatchSchema (in app-ipc-schemas.ts), index.ts applySettingsPatch,

settings-utils.ts mergeSettings/coerceRendererSettings, the store

loadDesignSettings, and the UI. The .strict() belongs on the design sub-schema,

not the top-level envelope.

---

8. Export

design:export-prototype (main IPC, mirrors write:export) exports the current
prototype to a standalone HTML file or a PDF (rendered via a hidden
BrowserWindow + printToPDF, reusing the internal Work export pipeline), through a native save
dialog that defaults to the artifact title. Buttons live on the canvas toolbar.

---

9. Built-in "design system & craft" skill

src/main/skill-bundled.ts seeds a built-in skill into ~/.kun/skills/design-system/
on first launch (idempotent marker, mirrors ensureBundledUiPlugins). Its SKILL.md
carries design-system-first thinking and the anti-AI-slop craft baseline, so the agent
auto-gets design guidance (triggers on design prompts, or via load_skill). Honors
deletion; appears after the next runtime restart.

---

10. Design context, tokens & craft

- Design context = brand color, tone, design-system preset, structured tokens
(radius / density / font), free-form guidelines. It is injected into the design turn,
the implement turn, and the reverse-design turn.
- 14 presets (shadcn / radix / material / iOS / fluent / ant / chakra / carbon /
polaris / bootstrap / geist / brutalism / editorial / none).
- Craft baseline (DESIGN_CRAFT_LINES) — an anti-AI-slop rubric appended to every
generation prompt: avoid cream/sand backgrounds, purple→blue gradients, bounce easing,
nested cards, low-contrast gray-on-tint; verify contrast; provide a
prefers-reduced-motion fallback; use a real type scale and one spacing scale.

---

11. Positioning vs reference projects

| Capability | Design mode | AI-CanvasPro | open-design | penpot |
|---|---|---|---|---|
| Paradigm | design mode inside a coding-agent IDE | multimodal generative node canvas | agent-native design app | vector design platform |
| Artifact | single-file HTML | text/image/video/audio/3D | web/mobile/decks/video | vector SVG/components |
| Design ↔ code | strong, in-IDE loop | none | hand-off to code agents | MCP + design-as-code |
| Design system / tokens | presets + structured tokens + DESIGN_SYSTEM.md | none | 150 DESIGN.md systems | first-class design tokens |
| Export | HTML / PDF | local save | HTML/PDF/PPTX/MP4 | SVG/CSS/HTML/JSON |
| Node canvas | prompt/design/image + run engine | mature (7 node types) | automation | none |
| Collaboration / MCP | — / deferred (§14) | — | parallel sessions | realtime / MCP |

The deliberate moat is design↔code; the deliberate non-goals are penpot's
vector editor and realtime collaboration (heavy, off the agent-native thesis).

---

12. Stitch alignment plan

Reference: Stitch - Design with AI and Google
Labs' March 18, 2026 announcement describe Stitch as an AI-native infinite
software-design canvas for natural-language UI creation, project-wide design
agent reasoning, images/text/code as canvas context, Agent Manager parallel
directions, DESIGN.md import/export, instant prototype playback, voice critiques,
and MCP/developer-tool export.

Kun should align by strengthening the same workflow spine while keeping the
in-IDE design-to-code advantage:

1. Canvas maturity first: selection, lock/visibility, nudge, snap, grouping,
resize, rotate, layer order, device frames, and keyboard handling must feel
predictable before adding larger agent flows. Locked or hidden layers are
non-editable across hit-test, marquee, inspector, delete/duplicate, drag, and
keyboard nudge. Keyboard editing now includes group/ungroup, stable block
layer ordering, and ancestor-root normalization for duplicate/delete. The
inspector exposes multi-selection align and distribute controls backed by the
same shape ops the design agent can call. Resize handles now use the same
object/grid snap guides as move gestures, while only moving the dragged edge.
Move snapping now ignores hidden layers and descendants of hidden parents, so
invisible board content cannot create ghost alignment pulls.
The layers panel now flattens the board into a predictable top-to-bottom tree,
supports collapsing frame/group subtrees, and exposes explicit lock/visibility
controls for screen readers and tooltips.
Rectangle, ellipse, frame, and screen-frame creation now use the same snap
guide system while preserving strict square/circle drawing when Shift is held.
Arrow and line endpoints snap to grid/object guide positions during creation,
while Shift keeps its independent angle-lock behavior.
The zoom menu now exposes explicit grid and object-snap toggles, so users can
inspect or draw freely without changing hidden state.
The canvas now includes a minimap navigator that shows visible top-level
board content, highlights selected layers, and recenters the viewport by
click/drag for multi-screen and multi-direction boards.
Text creation supports both click-to-edit and drag-to-size snapped note boxes
so board text can act as first-class design context.
Arrow/line point editing now supports vertex drag, midpoint insertion, and
Option/Alt-click or double-click vertex deletion for path cleanup.
Drag-created shapes, text boxes, screens, lines, and freehand strokes now
record one undo entry using their final bounds/points, so redo restores the
object the user actually drew instead of the pointer-down preview.
Cmd/Ctrl+D now duplicates the editable selection roots as one selected block
with a single undo/redo entry, matching the copy/paste and Alt/Option-drag
interaction model; cloned frame subtrees now rewrite internal frame
ownership so descendants point at the new frame, not the source.
Shape copy/cut/paste now works for editable selection roots, preserves full
subtrees, offsets repeat pastes, and keeps cut and paste undo/redo as single
canvas changes; image paste remains the fallback when no shape clipboard
exists.
Freehand strokes are simplified on commit to preserve the drawn contour while
keeping render/persistence/AI-snapshot payloads bounded.
Rotation now has visible corner handles, undo grouping, and 15°/45° modifier
snapping instead of relying on the inspector field alone. Spacebar pan is a
true temporary hand tool and restores the previous drawing/editing tool on
release; middle-mouse drag also temporarily routes to the hand tool for
infinite-board navigation without changing the active drawing/editing tool.
Alt/Option-drag duplicates the current editable selection only once the
pointer really moves, drags the copy, and records duplicate+move as a single
undoable canvas change. Shift-dragging an existing selection now locks
movement to the dominant horizontal or vertical axis while preserving
same-axis snapping.
Shift/Cmd/Ctrl marquee selection adds hits to the existing selection instead
of replacing it; Alt/Option marquee subtracts hits from the current
selection. Marquee results are normalized to editable root layers so parent
and child overlaps do not create mixed selections. Fit-all now frames
visible board content, while fit-selection uses the same editable-root
selection semantics as marquee/duplicate/delete.
2. Infinite board as project memory: the board is the source of visual
context for screens, references, image slots, text notes, tokens, components,
and generated variants. Agent snapshots should stay compact but preserve
selected layers, nearby screens, and design-system bindings. Canvas snapshots
now mark selected, inView, and nearSelection shapes and, when capped,
prioritize those local-context layers before older/offscreen board content.
Line/freehand vertices are sampled per shape with pointsOmitted reported so
long annotations do not dominate the prompt.
3. Parallel directions: build on variant-matrix, add-screens, and
multi-page mode to support named exploration branches ("directions") that can
be generated, compared, accepted, or archived on the board. Multi-page runs
now stamp their generated screens with a shared direction id/name and the
design sidebar exposes those direction groups with persisted accept/archive
controls. Archived directions move out of the main direction list into a
restore-able archived section. The sidebar now exposes a comparison summary
for active directions: screen coverage, prototype links, implemented count,
shared screens, and per-direction unique screens. It can open a visual
side-by-side compare overlay with one live HTML preview column per direction
and synchronized switching for shared or partially covered screen names.
Remaining work: pixel/style diff overlays plus richer archive filtering.
4. Google-compatible DESIGN.md: root DESIGN.md is the canonical public
project theme. The Design canvas discovers and validates it, projects it as
a deterministic specimen board, and maps compatible tokens into native
canvas state. Kun-generated project handoff lives at
.kun-design/HANDOFF.md; legacy .kun-design/DESIGN.md and
.kun-design/design-system.json remain compatibility/migration inputs and
never compete as theme sources. Per-artifact DESIGN.md files remain local
implementation notes rather than project themes.
5. Prototype playback: HTML screen frames now persist planned
prototypeLinks[] from the multi-page planner's linksTo metadata, render
those links as a non-editing flow overlay on the board, and expose a Play
overlay for stepping through generated screens from the selected frame.
Generated HTML links whose href matches the planned flow are captured in
Play mode and route to the target screen. Unknown local/relative prototype
links are now captured as missing targets instead of letting the webview drift
away; the player can seed the design rail with a request to create and wire
the missing next screen. Remaining work: deeper stateful interaction capture
for non-navigation controls.
6. Voice and critique loop: route voice input into the design rail and let
the agent run a critique/repair pass against the selected screen, frame, or
whole board. The canvas toolbar now exposes a local critique entry point:
it runs the design-system lint pass against the current editable selection
subtree (or the whole board when nothing is selected), stashes findings into
the next canvas prompt, opens the design rail, and seeds a focused repair
request. Voice input remains the next layer on top of that repair loop.
7. MCP/developer-tool bridge: ship the deferred design-artifacts MCP server
once packaged-startup verification is available, then expose read-only
artifact/design-system resources to coding tools.

---

13. Extension seams

The discriminated unions are designed so later phases add a case, never rewrite:
DesignArtifactKind ('html' | 'graph' — penpot is a future member),
DesignCanvasView ('preview' | 'code' | 'live'), DesignGraphNodeKind
('prompt' | 'design' | 'image'). The canvas renderer and the turn builder branch on
these.

---

14. Deferred: MCP exposure (the plan)

Exposing design artifacts to agents over MCP is fully mapped but intentionally not
shipped — it touches main/index.ts's startup child-process gating in ~6 sites (the
most startup-critical file) and cannot be verified without a packaged run. The plan:

1. Add read-only /design/internal/list + /design/internal/read to
ScheduleRuntime.handleInternalRequest (schedule-runtime.ts); resolve the
workspace server-side via settings.write.activeWorkspaceRoot; reuse the existing
bearer-token auth.
2. Add src/main/design-artifacts-mcp-server.ts (mirror claw-schedule-mcp-server.ts)
— a stdio MCP server proxying to those endpoints, registering design_list_artifacts
/ design_read_artifact.
3. Add design-artifacts-mcp-node-entry.ts; handle the launch flag in main/index.ts
(generalize runningClawScheduleMcpServer's 6 gate sites).
4. Inject the server into the Kun config in kun-process.ts (mirror
buildGuiScheduleKunMcpServer), and package the entry via package.json.

---

15. Runtime-only behaviors to verify

Typecheck/lint/unit tests cover the code shape, not these (need a real npm run dev):

- Artifact rehydration (list survives reload), PDF export (hidden-window printToPDF).
- Node-canvas execution (sequential agent turns, live status), image nodes (the agent
must land the generated image at the node path — generate_image writes to
.deepseekgui-images/ by default, so the node prompt asks it to copy to the reserved
path; if it doesn't, the node shows an error rather than breaking).
- The built-in design skill activating (appears after a runtime restart).
- The code-drift ⚠ badge appearing after the shared design system changes.

---

DEVELOPMENT.En

Development Workflow

Simplified Chinese

This document defines how developers should work in this repository, especially around the default branch, pull requests, and contribution quality standards.

Development Baseline

- develop is the active collaboration and daily integration branch
- master is the stable release branch, updated by maintainers from develop
- Routine feature and fix work should start from the latest develop
- Short-lived feature branches are encouraged for non-trivial changes

1. Update your local repository.
2. Switch to develop.
3. Pull the latest changes from develop.
4. Create an optional feature branch from develop for your work.
5. Implement and validate your changes locally.
6. Open a PR back into develop.
7. Merge after review and passing checks.

Example Commands

Sync develop

bash
git checkout develop
git pull origin develop

Create a feature branch from develop

bash
git checkout develop
git pull origin develop
git checkout -b feat/short-description

Push your branch

bash
git push origin feat/short-description

Pull Request Flow

Default target branch:

- develop

Typical PR path:

1. Develop on a short-lived feature branch created from develop
2. Push the branch to the remote
3. Open a PR into develop
4. Address review feedback
5. Merge after approval and passing checks

Required Validation Before PR

At minimum, run:

bash
npm run typecheck
npm run build
npm run test

If your change affects runtime behavior or UI, also run:

bash
npm run dev

Manually verify the affected workflow before opening the PR.

PR Quality Standard

Code is easy. Good taste is rare. Review should protect the product experience, not only the implementation.

A PR should be:

- focused on one main purpose
- easy to review
- supported by validation results
- documented when behavior changes

Your PR description should include:

- what changed
- why it changed
- how you verified it
- a video or GIF if UI behavior changed
- unit tests added or updated if project logic changed

Change Scope Standard

Prefer:

- one topic per PR
- minimal unrelated formatting churn
- no opportunistic refactors unless they are necessary for the change

Avoid:

- mixing docs, refactors, and feature work without explanation
- large undocumented behavior changes
- bypassing normal review for risky changes

Localization Standard

If you change user-facing text:

- update English and Chinese strings together when possible
- keep wording consistent across docs and UI

Documentation Standard

Update documentation when changes affect:

- setup
- commands
- runtime requirements
- branch strategy
- release behavior
- contributor workflow

Merge Guidance

Merge contribution changes into develop only after:

- review feedback is addressed
- checks pass
- the change is considered stable enough for the daily integration branch

master is reserved for stable releases. After maintainers decide the current develop state is ready to publish, they merge develop into master.

Suggested Branch Naming

Examples:

- feat/runtime-settings
- fix/connection-probe
- docs/bilingual-readme
- refactor/chat-store

Maintainer Notes

If maintainers later adjust protected branches, required reviewers, or stricter automated gates, this document should be updated to match the repository rules.

---

DEVELOPMENT

Development Workflow

简体中文

This document defines how developers should work in this repository, especially around the default branch, pull requests, and contribution quality standards.

Development Baseline

- develop is the active collaboration and daily integration branch
- master is the stable release branch, updated by maintainers from develop
- Routine feature and fix work should start from the latest develop
- Short-lived feature branches are encouraged for non-trivial changes

1. Update your local repository.
2. Switch to develop.
3. Pull the latest changes from develop.
4. Create an optional feature branch from develop for your work.
5. Implement and validate your changes locally.
6. Open a PR back into develop.
7. Merge after review and passing checks.

Example Commands

Sync develop

bash
git checkout develop
git pull origin develop

Create a feature branch from develop

bash
git checkout develop
git pull origin develop
git checkout -b feat/short-description

Push your branch

bash
git push origin feat/short-description

Pull Request Flow

Default target branch:

- develop

Typical PR path:

1. Develop on a short-lived feature branch created from develop
2. Push the branch to the remote
3. Open a PR into develop
4. Address review feedback
5. Merge after approval and passing checks

Required Validation Before PR

At minimum, run:

bash
npm run typecheck
npm run build
npm run test

If your change affects runtime behavior or UI, also run:

bash
npm run dev

Manually verify the affected workflow before opening the PR.

PR Quality Standard

Code is easy. Good taste is rare. Review should protect the product experience, not only the implementation.

A PR should be:

- focused on one main purpose
- easy to review
- supported by validation results
- documented when behavior changes

Your PR description should include:

- what changed
- why it changed
- how you verified it
- a video or GIF if UI behavior changed
- unit tests added or updated if project logic changed

Change Scope Standard

Prefer:

- one topic per PR
- minimal unrelated formatting churn
- no opportunistic refactors unless they are necessary for the change

Avoid:

- mixing docs, refactors, and feature work without explanation
- large undocumented behavior changes
- bypassing normal review for risky changes

Localization Standard

If you change user-facing text:

- update English and Chinese strings together when possible
- keep wording consistent across docs and UI

Documentation Standard

Update documentation when changes affect:

- setup
- commands
- runtime requirements
- branch strategy
- release behavior
- contributor workflow

Merge Guidance

Merge contribution changes into develop only after:

- review feedback is addressed
- checks pass
- the change is considered stable enough for the daily integration branch

master is reserved for stable releases. After maintainers decide the current develop state is ready to publish, they merge develop into master.

Release Automation

Stable releases are published by GitHub Actions when a same-repository PR from develop into master is merged.

The release workflow:

- computes the next vX.Y.Z patch tag from the latest three-part semver tag
- reuses a tag that already points at the merge commit when a workflow is rerun
- builds signed and notarized macOS arm64/x64 packages, a Windows x64 installer, and Linux x64 AppImage plus deb packages
- uploads release assets and update metadata to GitHub Releases and the R2 stable channel
- promotes R2 stable/latest only after all platform uploads succeed

Repository maintainers must configure these GitHub Actions secrets before the first automated release:

- R2: R2_BUCKET, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_PUBLIC_BASE_URL, and either R2_ACCOUNT_ID or R2_ENDPOINT
- Optional R2 override: R2_RELEASE_PREFIX
- macOS signing: MAC_CODESIGN_P12_BASE64, CSC_KEY_PASSWORD, APPLE_API_KEY_BASE64, APPLE_API_KEY_ID, APPLE_API_ISSUER

The repository Actions settings must allow GITHUB_TOKEN to write repository contents so the workflow can create tags and publish releases.

The local npm run release:mac and npm run release:win commands remain available as manual fallback tools.

Suggested Branch Naming

Examples:

- feat/runtime-settings
- fix/connection-probe
- docs/bilingual-readme
- refactor/chat-store

Maintainer Notes

If maintainers later adjust protected branches, required reviewers, or stricter automated gates, this document should be updated to match the repository rules.

---

DEVELOPMENT.Zh CN

开发流程

English

这份文档定义了本仓库的开发协作方式,重点说明默认分支、PR 流程,以及贡献质量标准。

开发基线

- develop 是开发协作与日常集成分支
- master 是稳定发布分支,由维护者从 develop 合入
- 日常功能开发和缺陷修复都应从最新 develop 开始
- 对于稍大一些的改动,建议使用短期功能分支

推荐流程

1. 先同步本地仓库。
2. 切换到 develop
3. 拉取 develop 最新代码。
4. 如有需要,从 develop 拉出功能分支开展开发。
5. 在本地完成实现并做好校验。
6. 提交 PR 回到 develop
7. 在通过评审和检查后合并。

示例命令

同步 develop

bash
git checkout develop
git pull origin develop

develop 拉功能分支

bash
git checkout develop
git pull origin develop
git checkout -b feat/short-description

推送分支

bash
git push origin feat/short-description

Pull Request 流程

默认目标分支:

- develop

典型流程如下:

1. 在从 develop 拉出的短期功能分支上开发
2. 将分支推送到远端
3. 发起指向 develop 的 PR
4. 根据 Review 意见继续修改
5. 在通过校验并获得认可后合并

PR 前必须做的校验

至少执行:

bash
npm run typecheck
npm run build
npm run test

如果改动影响运行时行为或 UI,额外建议执行:

bash
npm run dev

并手动验证受影响流程后再发起 PR。

PR 质量标准

代码不难,难得的是好品味。评审要守住产品体验,而不只是实现是否能跑。

一个合格的 PR 应当:

- 目标明确,只围绕一个主要主题
- 易于审阅
- 有明确的校验结果支撑
- 行为变更时同步更新文档

PR 描述建议至少包含:

- 改了什么
- 为什么要改
- 如何验证
- 如果涉及 UI,附上视频或 GIF
- 如果涉及项目逻辑,列出新增或更新的单元测试

改动范围标准

推荐:

- 一个 PR 聚焦一个主题
- 尽量减少无关格式化改动
- 非必要不要顺手做大范围重构

避免:

- 没有解释就把文档、重构、功能改动混在一起
- 大范围行为变化却没有文档说明
- 对高风险改动绕过正常评审流程

本地化标准

如果修改了用户可见文案:

- 尽量同步更新中英文内容
- 保持文档和 UI 用词一致

文档标准

当改动影响以下内容时,应同步更新文档:

- 安装或初始化方式
- 命令使用方式
- 运行时要求
- 分支策略
- 发布流程
- 贡献者协作方式

合并建议

贡献改动只有在满足以下条件后,才应该合入 develop

- Review 意见已处理
- 检查项通过
- 改动已经达到适合进入日常集成分支的稳定程度

master 仅用于稳定发布。维护者确认 develop 中的改动适合发布后,再将 develop 合入 master

自动发布

当同仓库内从 develop 指向 master 的 PR 被合并后,GitHub Actions 会自动发布稳定版本。

每次合并发布 PR 前,发布负责人必须完成稳定版发版交验清单与故障排查,重点核验 Graph 时序、Linux AppImage、Windows NSIS/跨范围迁移、打包后的 Electron/Extension smoke、跨平台 evidence 和 R2 stable/latest

发布 workflow 会:

- 基于最新三段式 semver tag 自动生成下一个 vX.Y.Z patch tag
- 如果 rerun 时当前 merge commit 已经有 tag,则复用该 tag
- 构建已签名并公证的 macOS arm64/x64 包、Windows x64 安装器、Linux x64 AppImage 与 deb
- 将发布产物和更新元数据上传到 GitHub Releases 与 R2 stable 渠道
- 只有在全部平台上传成功后,才会 promote R2 stable/latest

首次自动发布前,维护者需要配置这些 GitHub Actions secrets:

- R2:R2_BUCKETR2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEYR2_PUBLIC_BASE_URL,以及 R2_ACCOUNT_IDR2_ENDPOINT
- 可选 R2 覆盖项:R2_RELEASE_PREFIX
- macOS 签名:MAC_CODESIGN_P12_BASE64CSC_KEY_PASSWORDAPPLE_API_KEY_BASE64APPLE_API_KEY_IDAPPLE_API_ISSUER

仓库的 Actions 设置还需要允许 GITHUB_TOKEN 写入 repository contents,这样 workflow 才能创建 tag 并发布 Release。

本地 npm run release:macnpm run release:win 命令保留为手动兜底工具。

分支命名建议

示例:

- feat/runtime-settings
- fix/connection-probe
- docs/bilingual-readme
- refactor/chat-store

维护者说明

如果后续仓库调整受保护分支、强制 Reviewer、自动化测试门禁等规则,应同步更新本文件,保持与真实仓库规则一致。

---

KUN CONFIG

Kun Agent 与模型配置说明

本文说明 Kun(桌面应用与运行时)的本地配置文件在哪里、哪些字段由 UI 管理、哪些字段适合手工扩展,以及模型上下文压缩阈值应该如何配置。

配置文件分层

Kun 有两层配置。

1. GUI settings

这是桌面应用自己的设置文件,保存设置页里的 Agent 运行时选项。

- macOS: ~/Library/Application Support/Kun/kun-settings.json
- Windows: %APPDATA%/Kun/kun-settings.json
- Linux: ~/.config/Kun/kun-settings.json

Agent 运行时设置在 agents.kun 下,例如端口、data dir、默认模型、审批策略、sandbox、token economy 等。多数用户通过设置页修改这些字段。

2. Kun runtime config

这是 Kun 本地运行时读取的高级配置文件。默认路径是:

text
~/.kun/data/config.json

如果 agents.kun.dataDir 改成了别的目录,实际路径就是:

text
<dataDir>/config.json

kun serve --config <path> 可以显式指定配置文件;如果没有指定,Kun 会尝试读取 {dataDir}/config.json

启动时的读取顺序

GUI 启动 Kun 时会按下面的顺序合并配置。

1. GUI 读取 kun-settings.json(旧版 deepseek-gui-settings.json 会自动迁移),得到 agents.kun 和通用 provider 配置。
2. GUI 在启动 Kun 前同步 <dataDir>/config.json,写入 UI 管理的 token economy、默认压缩摘要参数、默认模型 profiles、runtime tuning、MCP search 和附件能力。
3. Kun serve 读取 <dataDir>/config.json--config 指定的文件。
4. CLI 参数和环境变量会覆盖 serve 里的基础启动字段,例如 --model--portKUN_MODELKUN_PORT
5. AgentLoop、review loop 和子 Agent 都从同一份模型配置加载模型能力与上下文压缩阈值。

从旧目录升级

旧版本可能把 GUI 管理的 Runtime 数据放在 ~/.deepseekgui/kun。新版 GUI
取得单实例锁后、加载设置和启动 Runtime 前,会执行可恢复迁移:

- 如果设置仍选择旧目录,整份旧 Runtime 存储会以同卷原子改名迁到
~/.kun/data,包括线程、事件、附件、凭据、索引和 config.json
- 如果新目录已经有数据,它会先被改名为相邻的
data.pre-deepseekgui-migration-<timestamp>.bak。其中不冲突的线程和身份
数据会补入迁移后的存储;发生冲突的版本继续完整保留在备份中。
- 旧路径随后成为指向新目录的符号链接(macOS/Linux)或目录联接
(Windows),仅用于旧版本兼容。当前 GUI 只使用新路径。
- 设置文件会先生成
kun-settings.json.pre-runtime-data-migration-<timestamp>.bak,再把
agents.kun.dataDir 改为 ~/.kun/data
- 迁移器与 GUI 使用完全相同的 settings 查找顺序;如果当前设置仍位于
旧 Electron userData 目录,或 settings 文件是符号链接,会备份并改写
实际生效的那一份,不会让旁边的过期设置抢占配置权威。
- 迁移前会检查是否仍有 Kun Runtime 进程使用旧目录。发现活跃写入者时
会在任何目录改名前停止迁移;如果写入者恰好在切换窗口重新创建旧
目录,该目录会完整保留为冲突备份,并回滚两个标准目录的名称。

迁移日志 kun-runtime-data-migration-v2.json 和报告
kun-runtime-data-migration-v2-report.json 位于上方列出的 GUI settings
目录。迁移可在下次启动续跑;如果遇到文件占用、权限、跨卷或链接创建
失败,GUI 会保留所有原目录和备份,并阻止 Runtime、配置同步、凭据迁移
及清理任务继续写旧目录。

迁移完成后,~/.kun/data/config.json 是唯一自动生效的 Runtime 配置。
当前 GUI 不会再发现、复制或回退读取独立的
~/.deepseekgui/kun/config.json。兼容链接下看到的同名文件只是新配置的
同一个文件。若真实旧目录在后续升级中再次出现,它会被隔离为
kun.post-migration-<timestamp>.bak,其中的配置不会覆盖新配置。用户
显式选择的自定义 dataDir 不参与这次标准目录迁移。
这也适用于迁移完成后的后续版本:用户后来改成自定义目录时,完成态
迁移日志不会再把它强制改回 ~/.kun/data。若旧设置仍在但新旧目录都
不存在,则执行空存储切换并在新目录开始,不会永久阻止 Runtime 启动。

推荐的 config.json 结构

json
{
"serve": {
"host": "127.0.0.1",
"port": 18899,
"dataDir": "~/.kun/data",
"runtimeToken": "<local-access-token>",
"apiKey": "",
"baseUrl": "https://api.deepseek.com/beta",
"model": "deepseek-v4-pro",
"approvalPolicy": "auto",
"sandboxMode": "workspace-write"
},
"models": {
"profiles": {
"deepseek-v4-pro": {
"contextWindowTokens": 1000000,
"contextCompaction": {
"softThreshold": 980000,
"hardThreshold": 990000
},
"inputModalities": ["text"],
"outputModalities": ["text"],
"supportsToolCalling": true,
"messageParts": ["text"]
}
}
},
"contextCompaction": {
"defaultSoftThreshold": 96000,
"defaultHardThreshold": 108800,
"summaryMode": "model",
"summaryTimeoutMs": 15000,
"summaryMaxTokens": 2048,
"summaryInputMaxBytes": 98304
},
"runtime": {
"streamIdleTimeoutMs": 450000,
"toolStorm": { "enabled": true },
"toolArgumentRepair": { "maxStringBytes": 524288 }
}
}

GUI 管理的运行时会在 runtimeToken 为空时自动生成并保存本地访问令牌。

模型配置写在哪里

模型相关配置写在顶层 models.profiles

每个 key 是模型 ID。模型 ID 会按小写匹配,也支持 provider 前缀,例如请求模型是 vendor/deepseek-v4-pro 时,也可以匹配 deepseek-v4-pro

json
{
"models": {
"profiles": {
"my-128k-model": {
"aliases": ["vendor/my-128k-model"],
"contextWindowTokens": 128000,
"contextCompaction": {
"softRatio": 0.85,
"hardRatio": 0.93
},
"inputModalities": ["text"],
"outputModalities": ["text"],
"supportsToolCalling": true,
"messageParts": ["text"]
}
}
}
}

可用字段:

- aliases: 这个 profile 还要匹配的模型别名。
- contextWindowTokens: 模型上下文窗口大小。
- contextCompaction.softThreshold: 达到多少 input tokens 后开始压缩。
- contextCompaction.hardThreshold: 达到多少 input tokens 后强制更激进压缩。
- contextCompaction.softRatio: 按 contextWindowTokens 比例计算 soft threshold。
- contextCompaction.hardRatio: 按 contextWindowTokens 比例计算 hard threshold。
- inputModalities: 输入模态,目前常用 ["text"]["text", "image"]
- outputModalities: 输出模态,通常是 ["text"]
- supportsToolCalling: 模型是否支持 tool calling。
- messageParts: 模型消息 part 能力,例如 ["text"]["text", "image_url"]

如果同时写了 softThresholdsoftRatio,显式 token 阈值优先。hardThreshold 必须大于或等于 softThreshold

默认模型 profile

Kun 内置 DeepSeek V4 默认模型画像:

json
{
"models": {
"profiles": {
"deepseek-v4-pro": {
"contextWindowTokens": 1000000,
"contextCompaction": {
"softThreshold": 980000,
"hardThreshold": 990000
},
"inputModalities": ["text"],
"outputModalities": ["text"],
"supportsToolCalling": true,
"messageParts": ["text"]
},
"deepseek-v4-flash": {
"aliases": ["deepseek-chat", "deepseek-reasoner"],
"contextWindowTokens": 1000000,
"contextCompaction": {
"softThreshold": 980000,
"hardThreshold": 990000
},
"inputModalities": ["text"],
"outputModalities": ["text"],
"supportsToolCalling": true,
"messageParts": ["text"]
}
}
}
}

也就是说,V4 是 1M 上下文,正常情况下接近 980k input tokens 才触发上下文压缩;接近 990k 时进入更强的压缩策略。

全局压缩配置写在哪里

全局压缩配置写在顶层 contextCompaction。它只负责“不知道具体模型 profile 时的兜底阈值”和“摘要行为”,不要再把模型窗口大小写在这里。

json
{
"contextCompaction": {
"defaultSoftThreshold": 96000,
"defaultHardThreshold": 108800,
"summaryMode": "model",
"summaryTimeoutMs": 15000,
"summaryMaxTokens": 2048,
"summaryInputMaxBytes": 98304
}
}

字段说明:

- defaultSoftThreshold: 未匹配到模型 profile 时,达到多少 input tokens 开始压缩。
- defaultHardThreshold: 未匹配到模型 profile 时,达到多少 input tokens 强制压缩。
- summaryMode: GUI 管理的配置默认并归一为 model。手工维护 config.json
时仍可显式写 heuristic 使用本地摘要骨架;model 模式下模型摘要失败、
超时或返回空文本时会自动降级为本地摘要骨架。
- summaryTimeoutMs: 模型摘要调用超时时间。
- summaryMaxTokens: 模型摘要输出 token 上限。
- summaryInputMaxBytes: 摘要输入文本最大字节数。

Agent 配置写在哪里

普通 Agent 运行时配置由 GUI settings 的 agents.kun 管理。主要字段:

json
{
"agents": {
"kun": {
"binaryPath": "",
"port": 18899,
"autoStart": true,
"dataDir": "~/.kun/data",
"model": "deepseek-v4-pro",
"approvalPolicy": "auto",
"sandboxMode": "workspace-write",
"tokenEconomyMode": false,
"insecure": false
}
}
}

设置页会保存这些字段。GUI 模式下默认模型以 agents.kun.model 为准;config.json 里的 serve.model 更适合 standalone kun serve 使用,因为 GUI 启动时会把设置页里的模型作为启动参数传给 Kun。

Hooks 配置写在哪里

Hooks 写在 config.json 顶层的 hooks 数组里,GUI 启动 Kun 时通过
--data-dir 自动加载,无需额外开关:

json
{
"hooks": [
{
"phase": "PreToolUse",
"matcher": "bash|write_file|mcp__*",
"command": "node ~/.kun-hooks/guard.js",
"timeoutMs": 10000
},
{ "phase": "UserPromptSubmit", "command": "~/.kun-hooks/prompt-context.sh" }
]
}

支持的 phasePreToolUsePostToolUse(工具调用前后,可改写参数 /
输出、拒绝或自动放行)、UserPromptSubmit(回合开始前,可拒绝或注入
上下文)、TurnStartTurnEndPreCompact(只读通知)。命令通过
stdin 收到 JSON invocation,退出码 0 + stdout JSON 返回结构化结果,
退出码 2 阻断动作,其余非零只产生 hook_warning 事件。完整参考
(各阶段载荷、失败语义、示例脚本)见 kun-hooks.md

用户如何自定义

常见做法:

1. 在设置页修改端口、data dir、默认模型、审批策略、sandbox 和 token economy。
2. 打开 <dataDir>/config.json,在 models.profiles 里增加或覆盖模型 profile。
3. 如果要把自定义模型作为 GUI 默认模型,把 agents.kun.model 改成该模型 ID。
4. 重启 Kun runtime,让新配置生效。

自定义 1M 模型并在 950k 左右开始压缩:

json
{
"models": {
"profiles": {
"vendor/my-1m-model": {
"aliases": ["my-1m-model"],
"contextWindowTokens": 1000000,
"contextCompaction": {
"softThreshold": 950000,
"hardThreshold": 980000
},
"inputModalities": ["text"],
"outputModalities": ["text"],
"supportsToolCalling": true,
"messageParts": ["text"]
}
}
}
}

自定义图片输入模型:

json
{
"models": {
"profiles": {
"vision-model": {
"contextWindowTokens": 128000,
"contextCompaction": {
"softRatio": 0.75,
"hardRatio": 0.9
},
"inputModalities": ["text", "image"],
"outputModalities": ["text"],
"supportsToolCalling": true,
"messageParts": ["text", "image_url"]
}
}
}
}

兼容旧配置

旧版本曾支持把模型 profile 写在:

json
{
"contextCompaction": {
"modelProfiles": {}
}
}

这个位置仍然会被读取,以免已有用户配置失效。但新配置请使用:

json
{
"models": {
"profiles": {}
}
}

当两个位置都写了同一个模型时,models.profiles 的配置优先。

相关源码

- 默认 GUI Agent 设置:src/shared/app-settings-kun.ts
- GUI 同步 <dataDir>/config.jsonsrc/main/kun-process.ts
- Kun config schema:kun/src/config/kun-config.ts
- 模型 profile 解析:kun/src/loop/model-context-profile.ts
- 上下文压缩器:kun/src/loop/context-compactor.ts
- serve 解析入口:kun/src/cli/serve.ts
- 示例配置:kun/config.example.json

---

UI PLUGINS

UI 插件开发指南(形象工坊)

Kun 的「形象工坊」允许任何人制作并安装自己的视觉形象包:既可以替换工作台里的
泳动小鸟、欢迎/睡觉/坐着等状态形象,也可以给应用主体、侧边栏、主舞台和写作/设计工作面换上主题背景,
再配合主题 token 与进行中文案完成一套皮肤。v1.5 起可以把一张完整人物立绘放进
Kun 会话舞台;v1.6 的 scene 进一步提供左右导轨、卡片、背景式构图、五类专属美术槽位、
人物焦点/蒙版以及宿主内置动效,让每个人物主题拥有不同的 UI 语言而不开放任意 CSS。

iKun 模式就是一个随应用分发的示例:它会在首次启动时自动安装
(id 为 ikun,见 src/main/ui-plugin-bundled.ts),在形象工坊里与第三方插件同级。
它额外带有应用针对 ikun id 制作的专属动画;第三方插件使用通用的形象与背景框架。

一个 UI 插件就是一个文件夹:manifest.json + 被 manifest 引用的图片。
插件是纯声明式的,没有任何可执行代码;应用不会执行插件中的 JS、HTML、CSS 或 SVG。
启用主题时,Kun 主进程会重新读取并校验已安装的 manifest 和图片,根据固定槽位生成宿主 CSS,
再通过 Electron 内部的 CDP 接口注入工作台。这个实现不开放远程调试端口,也没有插件脚本入口。

text
my-plugin/
├── manifest.json
├── img/
│ ├── swim.png
│ ├── portrait.png
│ └── stage.webp
├── scene/
│ ├── backdrop.webp
│ ├── frame.png
│ └── emblem.png
└── artwork/
└── stage-source.svg # 可选的创作源文件,不会安装或执行

安装方式:设置 → 形象工坊 → 安装插件文件夹…,选中插件目录即可。
应用校验 manifest 和图片后,只把 manifest 与被 figures / backgrounds / scene 引用的图片
复制进应用数据目录(~/.kun/ui-plugins/<id>/);未引用的创作源文件不会复制。

官方示例见 examples/ui-plugins/starlight/。它同时演示了
旧版兼容的形象槽位、背景路径简写和完整背景图层对象。

manifest.json 参考

text
/ Detailed source-code truncated for AI context efficiency. /

顶层字段

| 字段 | 必填 | 规则 |
|---|---|---|
| id | ✓ | 2–40 位小写字母/数字/连字符;保留字 default / kun / on / off / none 不可用(ikun 被预装示例占用,重装会覆盖它) |
| name | ✓ | ≤60 字符 |
| version | ✓ | 语义化版本,如 1.0.0 |
| author / description | | ≤80 / ≤240 字符 |
| figures | 至少一类 | 形象槽位对象;活动小形象支持 png/webp/jpg/jpeg/gif;portrait 仅支持静态 png/webp/jpg/jpeg |
| backgrounds | 至少一类 | light / dark 主题下可放 app / sidebar / stage / write / design;图片仅支持静态 png/webp/jpg/jpeg(不支持 APNG、animated WebP) |
| presentation | | 人物舞台的严格声明式配置;一旦提供就必须同时提供 figures.portrait |
| scene | | v1.6 专属场景;必须同时提供完整 presentation 回退、figures.portrait 与至少一个 artwork 槽位 |
| labels | | 仅 zh / en;键限 working / workingSprint / workingDive / workingSurf;每条 ≤24 字符 |
| tokens | | 仅 light / dark;键限 --ds-*;值禁止 url()、分号、花括号等;总数 ≤60 |
| features.cameos | | true 时启用主会话两侧的不定时出没彩蛋 |

figuresbackgrounds 可以分别省略,但二者合计至少要包含一个有效图片槽位;空对象等同于未提供。
所有图片路径都必须是插件目录内的相对路径,禁止绝对路径、.. 与反斜杠。

背景图层(backgrounds)

backgrounds 按主题和区域组织。五个内部区域彼此独立;write / design 是稳定兼容键,可省略,此时继续使用 stage:

| 槽位 | 作用区域 | 默认透明度 |
|---|---|---|
| app | 整个工作台内容区的底层背景 | 0.22 |
| sidebar | 左侧栏背景 | 0.18 |
| stage | 主内容/会话舞台背景 | 0.32 |
| write | Work 办公工作面(稳定内部键) | 0.50 |
| design | Code 内 Design 白板(稳定内部键) | 0.50 |

顶栏(topbar)不属于上述五个背景槽位,仍由主题 token --ds-topbar-bg 控制。

一个图层可以直接写成图片路径,也可以写成对象:

json
{
"backgrounds": {
"light": {
"app": "img/paper-texture.jpg",
"sidebar": {
"path": "img/sidebar.webp",
"fit": "contain",
"position": "bottom-right",
"opacity": 0.14
}
}
}
}

- 字符串是 { "path": "…" } 的简写。
- fit 可为 covercontain,默认 cover
- position 默认 center,可为 top-left / top / top-right / left / center /
right / bottom-left / bottom / bottom-right
- opacity 范围为 01;省略时使用上表对应区域的默认值。
- lightdark 不会互相回退。例如只声明 light.stage 时,深色主题不会偷偷沿用它;
如需两种主题显示同一张图,请在两边都显式声明。

背景图片本身不携带布局或样式权限。应用只读取图像像素,再在固定的安全图层中应用上述
fitpositionopacity 参数;插件不能提供选择器、CSS 值或脚本。

人物舞台(presentation,v1.5)

figures.portrait 是会话舞台使用的主人物图片。建议使用透明背景的原始人物立绘,不要把
Kun 的侧栏、顶栏、输入框或其它应用界面烘焙进图片。Kun 会保留人物原画,只在图片外侧
绘制宿主框景和氛围层,不会重新设计人物。portrait 必须是静态 PNG/JPEG/WebP;GIF、APNG
和 animated WebP 会在安装及每次加载时被拒绝,避免绕过“减少动态效果”或持续占用解码资源。
这个限制不影响 swimgreet 等既有活动小形象继续使用 GIF。

presentation 必须完整包含下面三个对象。所有键和取值都是严格白名单;未知键、缺失键、
任意 CSS、选择器、URL 或布局字符串都会让安装失败。

character

| 字段 | 可选值/范围 | 作用 |
|---|---|---|
| anchor | top-right / right / bottom-right | 人物在会话舞台右侧的锚点 |
| size | medium / large / hero | 宿主预设的立绘尺寸 |
| offsetX / offsetY | -1212 的整数 | 在锚点基础上的百分比微调 |
| opacity | 01 的有限数字 | 整个人物层透明度 |
| frame | soft-card / paper / crystal / hologram / backstage / portal / polaroid / ticket / seal | 九种宿主绘制的框景语言 |
| motion | none / breathe / float | 固定的轻动效;系统开启“减少动态效果”时自动关闭 |
| contentReserve | none / narrow / wide | 给消息和输入框预留人物空间,避免遮挡 |

readability

| 字段 | 可选值 | 作用 |
|---|---|---|
| scrim | none / opposite-character / full | 不加遮罩、只保护人物对侧文字区、或保护整个舞台 |
| strength | soft / medium / strong | 固定遮罩强度 |

surfaces

sidebartopbarcomposercards 四个键都必须提供。每个键只能选择
solid / translucent / glass / strong-glass。这些名称映射到 Kun 内置材质,
插件不能覆盖模糊半径、阴影、边框或 CSS 属性。

人物层、装饰层和遮罩层均为 pointer-events: nonearia-hidden;不会拦截聊天、输入或
辅助技术。会话舞台窄于 980px 或开启专注模式时,人物与装饰自动隐藏并归还内容宽度。

专属人物场景(scene,v1.6)

scenepresentation 之上的渐进增强。它不允许插件传入 DOM、CSS 或动画代码,而是让
插件从 Kun 固定的场景积木中组合专属 UI。声明 scene 时仍必须保留完整 presentation,
供只认识 v1.5 的宿主安全回退。scene.apiVersion 当前只能是 "1.6"

场景布局和人物

- layout: rail-right / rail-left / card-right / card-left /
backdrop-right / backdrop-center。导轨和卡片布局会由宿主给消息列与输入框预留左右空间;
背景布局始终放在内容层下方。
- character.scale: compact / standard / hero;fit: contain / cover;
focalPoint 使用背景图层相同的九宫格位置。
- character.mask: none / soft-card / circle / arch / diamond /
hologram / portal / polaroid / ticket
- offsetX / offsetY-1212 的整数,opacity01,flipX 为布尔值。
- 人物 motion.presetnone / breathe / float / sway;所有 motion 还必须指定
speed(slow / normal / fast)与 phase(a / b / c)。

专属美术槽位

artwork 至少声明一个槽位:backdrop / ambient 位于整个舞台的安全底层;
frame / foreground / emblem 位于裁切后的人物视觉区。每层必须完整提供:

| 字段 | 可选值/范围 |
|---|---|
| path / darkPath | 插件内静态 PNG/JPEG/WebP 相对路径;darkPath 可选,深色模式覆盖默认图 |
| anchor | 九宫格位置 |
| size | small / medium / large / full |
| fit | contain / cover |
| offsetX / offsetY | -1212 的整数 |
| opacity | 01 |
| blend | normal;仅 backdrop / ambient 可用 screen / soft-light |
| motion.preset | none / float / drift-x / drift-y / pulse / orbit / twinkle / scan |

人物和美术动效都由宿主实现,系统开启“减少动态效果”时自动停止。不要把 Kun 的输入框、
消息、侧栏等界面烘焙进这些图;宿主会负责真实 UI 的避让和层级。

宿主外观配方

chrome 必须包含 sidebartopbarcomposercards 四项。每项可选
inherit / soft / editorial / paper / crystal / hologram / backstage /
portal / polaroid / ticket / seal,也可以使用 11 套人物专属配方:
botanical / fortune-ledger / dream-gate / washi / scrapbook / aurora /
synth / midnight-pass / nautical / grand-line / dimension-lab / starlight

人物专属配方由 Kun 宿主实现,会以同一套视觉语言同时处理侧边栏、顶栏、输入框、用户/助手
消息、普通卡片与表格表面。例如 botanical 使用柔和植物感层次,fortune-ledger 使用账簿式
线条,synthdimension-lab 使用不同的未来界面语言。插件只选择白名单名称;不能提供
选择器、属性值、DOM 或控件,所有装饰保持在真实交互层下方,不会拦截输入。

四项可以独立选择,但人物主题通常应让四项使用同一个专属配方,以保证会话卡片、输入区和
导航区与人物形象保持一致。这些名称是 Kun 固定的表面配方,不是 CSS 权限。

场景舞台、人物视觉区、装饰图和可读性遮罩均为不可交互安全层。窗口变窄、专注模式开启或
终端面板展开时,宿主会隐藏整套场景并取消消息列/输入框位移,避免任何图片或装饰遮挡输入。

宿主受控的 CDP 主题注入

CDP 是 Kun 应用主题的宿主实现细节,不是 manifest 能申请的代码执行能力。激活一次插件时:

1. 渲染层只把插件 id 发给主进程。
2. 主进程从 ~/.kun/ui-plugins/<id>/ 重新读取并规范化 manifest,重新校验全部引用图片。
3. Kun 自己的样式生成器把白名单 token、固定背景槽位以及已归一化的人物/场景数值变量组合成 CSS。
4. 主进程短暂附加 mainWindow.webContents.debugger,调用固定的 Runtime.evaluate 程序,
只用 style.textContent 创建或更新一个宿主管理的 <style> 节点,随后立即分离 debugger。
5. 渲染层用固定 React 组件显示主进程验证后的 figures.portraitscene.artwork data URL,
枚举值只设置为宿主认识的受控 data-* 状态;不会执行插件代码或插入插件标记。
6. 工作台重新加载后,主进程用同一份宿主生成 CSS 重新注入;停用插件时删除该节点并清理状态。

Kun 不使用 --remote-debugging-port,不会连接外部 WebSocket,也不接受插件提供的 CSS、JS、
选择器或 CDP payload。若 DevTools 或其它调试器已经占用该 webContents 的 debugger,
本次启用会安全失败且不会分离对方的调试会话;关闭占用方后可重新启用主题。

形象槽位(figures)

动画小形象建议 主体朝左、透明背景、最长边 512px 左右。portrait 建议保留人物原始
纵向构图和透明背景,在 2 MiB 槽位预算内可使用更高分辨率。缺失动画槽位会回退到默认
Kun 美术,或按下表回退链借用插件内的其它槽位;portrait 不参与动画回退链。

| 槽位 | 出现在哪里 | 缺失时回退 |
|---|---|---|
| portrait | presentation / scene 人物舞台中的完整人物立绘 | 不显示人物舞台;声明 presentationscene 时此槽位必填 |
| swim | 回合进行中的泳动动画主体(推进/冲刺/潜入)、各处最终兜底 | 默认 Kun 鸟 |
| surf | 泳动动画的冲浪姿态、庆祝「胜利巡游」 | swim |
| greet | 欢迎卡片、侧边栏轮播、出没「探头」、庆祝「跃起欢呼」 | swim |
| sleep | 运行时唤醒页、侧边栏轮播、出没「打盹」 | sitswim |
| sit | 选择工作区空状态、侧边栏轮播、出没「歇脚」、庆祝「举杯」 | greetswim |
| run | 出没「横穿/对穿」、庆祝「胜利巡游」 | surfswim |
| toggleIcon | 形象工坊里的预览小图 | swimgreet … |

尺寸与体积限制

形象预算沿用既有的按槽位计数规则;背景预算、复制文件与全部资源总额按相对路径去重:

- manifest.json ≤64 KiB。
- 每个形象槽位引用的图片 ≤2 MiB;全部形象槽位合计 ≤24 MiB。同一路径被多个形象槽位
引用时,仍会按槽位分别计入该项预算。
- 任一形象图片宽、高均 ≤4096 px,且单张解码尺寸 ≤12 MP;全部形象槽位合计 ≤48 MP。
与体积预算相同,同一路径被多个形象槽位引用时会按槽位计入总像素预算。
- 单张背景图片 ≤8 MiB;去重后的全部背景图片合计 ≤32 MiB。
- 按相对路径去重后的形象、背景与场景文件合计 ≤48 MiB。
- 任一背景图片宽、高均 ≤8192 px,且单张解码尺寸 ≤24 MP(宽 × 高)。
- 去重后的全部背景图片解码尺寸合计 ≤64 MP。
- 单张 scene.artwork 图片 ≤4 MiB;按相对路径去重后的场景图片合计 ≤16 MiB。
- 任一场景图片宽、高均 ≤4096 px,且单张解码尺寸 ≤12 MP;去重后的场景图片合计 ≤40 MP。

形象工坊列表优先使用 toggleIconswimgreet 等小形象作为预览,不会把全尺寸
portrait 的 base64 放进列表 IPC。只有 portrait 可用时,宿主会生成最长边 ≤256px、编码后
≤96 KiB 的单帧静态 WebP 缩略图;无法满足上限时显示占位图。

这些限制同时约束压缩文件大小和解码后的像素规模。安装、预装和重新加载时还会调用应用
已有的图片解码器验证像素数据,而不只信任文件头。形象工坊列表若只能用背景作为卡片预览,
仅会返回 ≤512 KiB 且 ≤2.1 MP 的背景;更大的背景仍可正常安装和启用,列表中显示占位图。

兼容旧版 Kun

旧版 Kun 不认识 backgrounds 时会忽略该字段,背景不会生效。portraitpresentation
需要支持 v1.5 的 Kun;严格校验旧版可能把 portrait 视为未知槽位而拒绝安装。较早的校验器还要求
figures 存在,因此需要兼容旧版时,建议至少保留一个形象槽位(通常是 swim
toggleIcon)。scene 需要支持 v1.6 的 Kun;兼容 v1.5 时请保留 presentation,旧宿主会忽略
场景而使用人物舞台回退。新版允许制作只有背景、没有自定义形象的插件。

安全模型(为什么这样设计)

1. 无代码执行:manifest 只接受声明式字段;JS、HTML、CSS、SVG 不能作为运行资源。
2. 白名单安装:只复制 manifest 与 figures / backgrounds / scene 引用的安全图片;路径禁止
越界,未引用文件不会安装。
3. 主进程校验:安装时校验扩展名、文件签名、完整像素解码、文件大小、图像尺寸与累计预算;
不合规的图片会让安装失败。
4. 隔离渲染:页面不会直接访问插件目录或任意文件路径;图片经主进程校验并转换为
data: URL,背景只进入宿主生成的主题 CSS,人物只进入固定的无交互 React 图片层。
5. 固定背景参数:背景只能选择固定槽位、两种缩放方式、九宫格位置和 01 透明度。
6. 主题 token 白名单:键名必须是 --ds-*,值经过字符集校验;应用生成的样式锚定在
html[data-ui-plugin='<id>'] 下,由主进程通过短生命周期 CDP 会话注入,停用即移除。
7. 人物舞台白名单:只接受固定锚点、尺寸、框景、动效、遮罩和材质枚举;数值范围由
主进程验证,所有选择器、标记、事件和动画实现均由 Kun 固定提供。
8. 专属场景白名单:只接受固定布局、五类美术槽位、蒙版、焦点、动效和外观配方;原始路径
不进入 DOM 或 CSS,渲染层只接收主进程验证后的静态图片 data URL。

调试技巧

- 安装失败时,设置页会列出 manifest 或图片的具体校验错误。
- 修改插件后重新执行一次「安装插件文件夹…」即可覆盖更新(同 id 覆盖安装)。正在使用
该插件时,先切到默认形象再切回来,即可确保重新载入最新资源。
- 如果提示 DevTools 或其它 debugger 已占用 CDP,关闭对应调试器后重新启用主题。Kun 不会
抢占或主动分离不属于主题控制器的调试会话。
- 如果背景妨碍文字可读性,先降低 opacity;不要把重要文字烘焙进背景图。
- 可用的 --ds-* token 清单见 src/renderer/src/styles/base-shell.css 顶部的
:root[data-theme='dark'] 变量块。常用 token 包括 --ds-accent
--ds-accent-soft--ds-selection--ds-topbar-bg

---

WRITE INLINE COMPLETION MODES.En

Work short-completion and inspired long-completion technical description

This document describes Work's dual-mode completion scheme for text-writing tasks. It splits the original single-path ghost text into two writing intentions: short completion in a flow state, and long completion with inspiration when you pause and think.

Why should we split it into two sets?

Writing completion has two conflicting goals:

- When typing in flow, users need low latency, short, accurate, and non-interruptive input.
- When pausing to think, users need a more complete next sentence or paragraph to help catch their inspiration.

If only one set of strategies is used, two types of problems will arise:

- Trigger too aggressively: long completion interrupts the rhythm being typed.
- The trigger is too conservative: only one or two words are given when the user stops, which lacks heuristic value.

Therefore, the current automatically triggered ghost text path is split into short and long, which share the editor context and provider service, but use different trigger conditions, prompts, token budgets and quality filtering. The same IPC/service also has a manual edit mode for selection-based inline editing; that path is documented in WRITE_INLINE_EDIT_RAG.en.md.

Overall architecture

mermaid
flowchart TD
A["CodeMirror update"] --> B["Build InlineCompletionRequestContext"]
B --> C{"Can request short completion?"}
B --> D{"Can request long completion?"}
C -->|yes| E["short debounce timer"]
D -->|yes| F["long debounce timer"]
E --> G["request FIM with mode=short"]
F --> H["request FIM with mode=long"]
G --> I["short candidate quality filter"]
H --> J["long candidate quality filter"]
I --> K["ghost text"]
J --> K

Core implementation:

- src/renderer/src/write/inline-completion/codemirror.ts
- src/renderer/src/write/inline-completion/policy.ts
- src/renderer/src/write/inline-completion/prompt.ts
- src/renderer/src/write/inline-completion/feedback.ts
- src/main/services/write-inline-completion-service.ts

Pattern definition

WriteInlineCompletionMode is defined in src/shared/write-inline-completion.ts:

ts
export type WriteInlineCompletionMode = 'short' | 'long' | 'edit'

The completion request will carry:

ts
{
mode?: 'short' | 'long' | 'edit'
}

When mode is not passed, it is regarded as short by default to ensure compatibility with old calling paths. This document focuses on the two automatic ghost text modes; edit uses the same request type for explicit inline replacement.

Short completion

Short complement to the heart flow input.

Trigger conditions

Short completion uses the basic strategy shouldRequestInlineCompletion:

- The completion master switch is turned on.
- The current cursor is not a selection.
- The character after the cursor is not a word character.
- The current document has sufficient context.
- Not the URL tail.
- Blank lines need to have structured context or paragraph opportunities.

Default parameters

| Parameters | Default value | Meaning |
| --- | ---: | --- |
| debounce | 650 ms | Request after how long to stop typing |
| max tokens | 96 | FIM maximum generation length |
| min accept score | 0.52 | local candidate display threshold |
| max visible chars | 220 | ghost text maximum number of characters |
| max visible lines | 6 | ghost text maximum number of lines |
| RAG snippets | 3 | Inject up to 3 retrieved snippets |

Prompt strategy

Short completion of prompt emphasizes:

- Only the inserted text is returned.
- Prefer to return null when blurred.
- No duplication of suffix.
- Do not diverge new topics.
- Maintain Markdown structure, indentation, and current tone.

Quality filtering

Short completion is penalized more severely:

- Too long candidate.
- Too many row candidates.
- Duplicate candidate with suffix.
- Candidates for joining words after a complete sentence.
- Overly general beginning.

This makes short completion more like "next string of typing" than AI-active writing.

Inspirational long completion

The inspiration long complement is oriented towards "give me something to continue writing" after the user pauses.

Trigger conditions

Long completion is based on the basic conditions of short completion, with additional restrictions:

- Long completion switch is on.
- The cursor must be at the end of the line.
- There is no remaining text after the current line.
- Not in table context.
- Not in title context.
- The current document or local context reaches a higher semaphore.
- When the current line ends with a word character, a longer partial signal is needed to avoid triggering at half a word time.

These restrictions ensure that long completions only appear where the user actually stopped.

Default parameters

| Parameters | Default value | Meaning |
| --- | ---: | --- |
| debounce | 2800 ms | triggered after a longer pause |
| max tokens | 256 | Allow about one paragraph of continuation inspiration |
| min accept score | 0.36 | looser than short completion |
| max visible chars | 900 | ghost text maximum number of characters |
| max visible lines | 14 | ghost text maximum number of lines |
| RAG snippets | 5 | Inject up to 5 retrieved snippets |

Prompt strategy

Long completion adds a hidden comment before prompt:

markdown

It explicitly tells the model:

- Users are pausing for inspiration.
- Can give a more complete next sentence or paragraph.
- Still has to fit the current draft.
- Don't summarize the document.
- Don't generate the entire article.

Quality filtering

Long completion reuses the duplication detection, sentence boundary detection and generalization penalty of short completion, but relaxes the length limit and lowers the initial threshold.

This way it displays a more complete paragraph while still avoiding the following problems:

- Repeat existing suffix.
- Suddenly opened a new topic.
- The output is too long and the entire content is too long.
- Insert large blocks of text at unsuitable locations.

Dual timer scheduling

The editor plug-in maintains two timers internally:

- shortTimer
- longTimer

Every time the document, selection, or focus changes:

1. sequence += 1
2. Clear the old timer
3. Recalculate the context
4. If short completion is possible, set a short timer
5. If long completion is possible, set a long timer

When the request returns, it checks:

- Whether the current request id is still the latest sequence.
- Whether the editor state is still the state when the request was made.
- Whether the current cursor position still matches the anchor.

If the user continues to type, the old request will naturally expire, and expired completions will not be inserted into the interface.

Relationship with RAG

Dual-mode completion and cross-text retrieval are two levels of capabilities:

- Dual mode determines "when to make up, how long to make up, and what strategy to use to make up".
- RAG determines "which cross-text fragments are referenced before completion".

Short completion:

- Pay attention to local smoothness.
- Fewer RAG fragments.
- Candidates are shorter.

Long completion:

- Pay attention to the continuity of inspiration.
- More RAG clips.
- prompt clearly prompts to pause and continue writing.

Setting items

The settings page provides:

- Enable ghost text completion.
- Cross-text search enhancements.
- FIM API address.
- Complete the model.
- Short completion trigger delay.
- Short completion shows strictness.
- Maximum length of short completion.
- Inspiration long completion switch.
- Inspiration long completion trigger delay.
- Inspired long completion to maximum length.

Default values are defined in src/shared/app-settings.ts:

- DEFAULT_WRITE_INLINE_COMPLETION_DEBOUNCE_MS
- DEFAULT_WRITE_INLINE_COMPLETION_MAX_TOKENS
- DEFAULT_WRITE_INLINE_COMPLETION_MIN_ACCEPT_SCORE
- DEFAULT_WRITE_INLINE_LONG_COMPLETION_DEBOUNCE_MS
- DEFAULT_WRITE_INLINE_LONG_COMPLETION_MAX_TOKENS
- DEFAULT_WRITE_INLINE_LONG_COMPLETION_MIN_ACCEPT_SCORE

User experience principles

The core of this design is "not grabbing the pen":

- Only short completion appears when the user is typing quickly.
- Long completion only appears when the user pauses for a long time.
- Long completion only appears at line ends/paragraph boundaries.
- Tab to accept, Esc to hide.
- Local filtering does not expire and is not displayed.
- Silently disappears when API or retrieval fails.

Failed to downgrade

Failure in any link will not affect editor input:

- Set Off: Not Requested.
- API Key is missing: the return fails and is not displayed.
- Retrieval failed: degraded to normal FIM.
- FIM failed: not displayed.
- Candidates with low scores: not displayed.
- The user continues to enter: the old request is invalid.

Test coverage

Related tests:

- src/main/services/write-inline-completion-service.test.ts
- src/main/ipc/app-ipc-schemas.test.ts
- src/main/settings-store.test.ts

Key coverage:

- Automatic short/long ghost text requests use FIM /completions; explicit mode: "edit" or action-capable requests use chat completions.
- Short completion default mode.
- Long completion uses independent prompt and token budget.
- Set default values ​​for migration.
- IPC schema accepts mode: "long" and mode: "edit"; the latter is covered by the inline edit tests and docs.

Follow-up optimization direction

- Add a separate display style to long completion to distinguish between "next string of typing" and "inspirational continuation".
- Added a quieter mode for "only trigger long completion on empty lines".- Automatically adjust long completion debounce with acceptance rate feedback.
- Save independent completion preferences for different writing spaces.
- Increase long completion threshold to reduce divergence when RAG hits are weak.

---

WRITE INLINE COMPLETION MODES.Zh CN

Work 短补全与灵感长补全技术说明

这份文档说明 Work 办公模式中文本写作场景的双模式补全方案。它把原本单一路径的 ghost text 拆成两种写作意图:心流状态下的短补全,以及停顿思考时的灵感长补全。

为什么要拆成两套

写作补全有两个互相冲突的目标:

- 心流输入时,用户需要低延迟、短、准、不打扰。
- 停顿思考时,用户需要更完整的下一句或下一段,帮助接住灵感。

如果只用一套策略,会出现两类问题:

- 触发太积极:长补全打断正在输入的节奏。
- 触发太保守:用户停住时只给一两个词,缺少启发价值。

因此当前自动触发的 ghost text 路径拆成 shortlong 两个 mode,共享编辑器上下文和服务端请求链路,但使用不同触发条件、prompt、token 预算和质量过滤。同一套 IPC / service 还包含面向选区编辑的手动 edit mode,详见 WRITE_INLINE_EDIT_RAG.zh-CN.md

总体架构

mermaid
flowchart TD
A["CodeMirror 更新"] --> B["构造 InlineCompletionRequestContext"]
B --> C{"是否可短补全"}
B --> D{"是否可长补全"}
C -->|是| E["短 debounce 计时器"]
D -->|是| F["长 debounce 计时器"]
E --> G["mode=short 请求 FIM"]
F --> H["mode=long 请求 FIM"]
G --> I["短候选质量过滤"]
H --> J["长候选质量过滤"]
I --> K["ghost text"]
J --> K

核心实现:

- src/renderer/src/write/inline-completion/codemirror.ts
- src/renderer/src/write/inline-completion/policy.ts
- src/renderer/src/write/inline-completion/prompt.ts
- src/renderer/src/write/inline-completion/feedback.ts
- src/main/services/write-inline-completion-service.ts

模式定义

WriteInlineCompletionMode 定义在 src/shared/write-inline-completion.ts

ts
export type WriteInlineCompletionMode = 'short' | 'long' | 'edit'

补全请求会携带:

ts
{
mode?: 'short' | 'long' | 'edit'
}

未传 mode 时默认视为 short,保证旧调用路径兼容。本文重点说明两个自动 ghost text mode;edit 复用同一请求类型承载显式 inline replacement。

短补全

短补全面向心流输入。

触发条件

短补全使用基础策略 shouldRequestInlineCompletion

- 补全总开关开启。
- 当前光标不是选区。
- 光标后一个字符不是单词字符。
- 当前文档有足够上下文。
- 不是 URL 尾部。
- 空白行需要有结构化上下文或段落机会。

默认参数

| 参数 | 默认值 | 含义 |
| --- | ---: | --- |
| debounce | 650 ms | 停止输入多久后请求 |
| max tokens | 96 | FIM 最大生成长度 |
| min accept score | 0.52 | 本地候选显示阈值 |
| max visible chars | 220 | ghost text 最大字符数 |
| max visible lines | 6 | ghost text 最大行数 |
| RAG snippets | 3 | 最多注入 3 个检索片段 |

Prompt 策略

短补全的 prompt 强调:

- 只返回插入文本。
- 模糊时宁可返回空。
- 不重复 suffix。
- 不发散新主题。
- 保持 Markdown 结构、缩进、当前语气。

质量过滤

短补全会更严格地惩罚:

- 过长候选。
- 过多行候选。
- 与 suffix 重复的候选。
- 在完整句子后硬接词的候选。
- 过于泛化的开头。

这让短补全更像“下一串键入”,而不是 AI 主动写作。

灵感长补全

灵感长补全面向用户停顿后的“给我一点可继续写的东西”。

触发条件

长补全基于短补全基础条件,再增加限制:

- 长补全开关开启。
- 光标必须在行尾。
- 当前行后面没有剩余文本。
- 不在表格上下文中。
- 不在标题上下文中。
- 当前文档或局部上下文达到更高信号量。
- 当前行以单词字符结束时,需要更长局部信号,避免半个词时触发。

这些限制确保长补全只在“用户真的停住了”的地方出现。

默认参数

| 参数 | 默认值 | 含义 |
| --- | ---: | --- |
| debounce | 2800 ms | 更长停顿后触发 |
| max tokens | 256 | 允许约一段的续写灵感 |
| min accept score | 0.36 | 比短补全更宽松 |
| max visible chars | 900 | ghost text 最大字符数 |
| max visible lines | 14 | ghost text 最大行数 |
| RAG snippets | 5 | 最多注入 5 个检索片段 |

Prompt 策略

长补全会在 prompt 前加入隐藏注释:

markdown

它明确告诉模型:

- 用户是停顿寻求灵感。
- 可以给更完整的下一句或下一段。
- 仍然必须贴合当前草稿。
- 不要总结文档。
- 不要生成整篇文章。

质量过滤

长补全复用短补全的重复检测、句边界检测和泛化惩罚,但放宽长度限制,并降低初始阈值。

这样它可以显示更完整的段落,同时仍避免以下问题:

- 重复现有 suffix。
- 突然开新主题。
- 输出过长整篇内容。
- 在不适合的位置插入大段文本。

双计时器调度

编辑器插件内部维护两个 timer:

- shortTimer
- longTimer

每次文档、选区或焦点变化时:

1. sequence += 1
2. 清掉旧 timer
3. 重新计算上下文
4. 如果可短补全,设置短 timer
5. 如果可长补全,设置长 timer

请求返回时会检查:

- 当前 request id 是否仍是最新 sequence。
- 编辑器 state 是否仍是发起请求时的 state。
- 当前光标位置是否仍匹配 anchor。

如果用户继续输入,旧请求会自然失效,不会把过期补全插入界面。

与 RAG 的关系

双模式补全和跨文本检索是两层能力:

- 双模式决定“什么时候补、补多长、用什么策略补”。
- RAG 决定“补全前参考哪些跨文本片段”。

短补全:

- 注重局部流畅。
- RAG 片段更少。
- 候选更短。

长补全:

- 注重灵感连续。
- RAG 片段更多。
- prompt 明确提示停顿续写。

设置项

设置页提供:

- 启用幽灵文本补全。
- 跨文本检索增强。
- FIM API 地址。
- 补全模型。
- 短补全触发延迟。
- 短补全显示严格度。
- 短补全最大长度。
- 灵感长补全开关。
- 灵感长补全触发延迟。
- 灵感长补全最大长度。

默认值定义在 src/shared/app-settings.ts

- DEFAULT_WRITE_INLINE_COMPLETION_DEBOUNCE_MS
- DEFAULT_WRITE_INLINE_COMPLETION_MAX_TOKENS
- DEFAULT_WRITE_INLINE_COMPLETION_MIN_ACCEPT_SCORE
- DEFAULT_WRITE_INLINE_LONG_COMPLETION_DEBOUNCE_MS
- DEFAULT_WRITE_INLINE_LONG_COMPLETION_MAX_TOKENS
- DEFAULT_WRITE_INLINE_LONG_COMPLETION_MIN_ACCEPT_SCORE

用户体验原则

这套设计的核心是“不抢笔”:

- 用户正在快速输入时,只出现短补全。
- 用户停顿较久时,才出现长补全。
- 长补全只在行尾/段落边界出现。
- Tab 接受,Esc 隐藏。
- 本地过滤不过时不展示。
- API 或检索失败时静默消失。

失败降级

任意环节失败都不会影响编辑器输入:

- 设置关闭:不请求。
- API Key 缺失:返回失败,不显示。
- 检索失败:退化为普通 FIM。
- FIM 失败:不显示。
- 候选低分:不显示。
- 用户继续输入:旧请求失效。

测试覆盖

相关测试:

- src/main/services/write-inline-completion-service.test.ts
- src/main/ipc/app-ipc-schemas.test.ts
- src/main/settings-store.test.ts

重点覆盖:

- 自动 short/long ghost text 请求走 FIM /completions;显式 mode: "edit" 或可能返回 action 的请求走 chat completions。
- 短补全默认 mode。
- 长补全使用独立 prompt 和 token budget。
- 设置默认值迁移。
- IPC schema 接受 mode: "long"mode: "edit";后者由 inline edit 测试和文档覆盖。

后续优化方向

- 给长补全增加单独的显示样式,区别“下一串键入”和“灵感续写”。
- 增加“只在空行触发长补全”的更安静模式。
- 用接受率反馈自动调整长补全 debounce。
- 为不同工作空间保存独立补全偏好。
- 在 RAG 命中较弱时,提高长补全阈值,减少发散。

---

WRITE INLINE EDIT RAG.En

Use BM25 + keyword RAG for Work text editing: an exploration and implementation

Text completion in Work has proven one thing: writing scenarios don’t necessarily require heavy vector libraries. As long as it can retrieve terms, facts, and style fragments from the same Work workspace with low latency, the FIM model can more reliably catch the current paragraph.

The question explored this time is more specific: Can text editing also use BM25 + keyword RAG? For example, the user selects a noun and asks AI to replace other nouns with the same name in this paragraph. It is not a traditional ghost text completion, but an in-place replacement of an existing text.

The conclusion is: Yes, but it should be designed as "boundary-marked middle replacement" rather than "cursor continuation".

Why is it not a normal completion?

The goal of completion is to predict the next short paragraph of text after the cursor:

text
prefix [cursor] suffix

The goal of editing is to replace an existing text:

text
prefix [original edit scope] suffix

If you still regard editing as cursor completion, the model can only insert content at the cursor, and it is difficult to naturally complete "replacing other positions within the paragraph as well". A more appropriate approach is to hollow out the paragraph to be edited:

text
prompt = edit instruction + retrieved snippets + prefix
suffix = suffix
model returns = replacement for original edit scope

In other words, the "middle" is no longer an empty cursor, but a paragraph or selection that needs to be regenerated.

What problems does RAG solve in editing?

BM25 + keyword RAG is not responsible for "deciding how to change" for the model, it is responsible for providing local facts and writing constraints to the model:

- How to write product names, character names, and project terms.
- Similar paragraph tone and sentence structure in the same writing space.
- A standard representation of a concept in other documents.
- When a user selects a short word, which cross-document fragments explain the context of the word.

This works well for editing tasks, as editors are often more afraid of "flying" than "not being divergent enough". Keyword search is simpler than embedding, but it is very effective in term replacement, style continuation, and recall of paragraphs on the same topic, and the desktop cost is low.

Design trade-offs

This time, the existing Work completion retrieval service is reused:

- Continue scanning Markdown/text files in the current Work workspace.
- Continue to use Chinese and English tokens + Chinese 2 to 4 character n-grams.
- Continue weighting title, path, phrase hits with BM25 score.
- The file currently being edited is still excluded from the search results to avoid repeatedly feeding the original text back to the model.

Newly added is the editing layer:

- When the user selects a short word or sentence, the editing scope is expanded to the current natural paragraph by default.
- When the user selects long text or text that spans blank lines, only the original selection will be edited.
- When users manually perform one-time phrase replacement, they will first use deterministic rules to replace other identical phrases in the same paragraph, such as deepseek gui -> DeepSeek GUI.
- The rendering side sends prefix, suffix, original, instruction and selection metadata.
- The rendering end will bring the user/AI editing records of the current file in the last 2 minutes to help the model understand "continue to change like this".
- The main process constructs an edit prompt and injects retrieved fragments as reference-only context.
- The model only returns replacement, and the rendering end replaces the replacement in place back to the document.

Prompt form

The key to editing prompt is to have clear boundaries:

markdown
raw prefix...

Explicit edit requests currently use the same write:inline-completion IPC, but the main process sends them through chat completions so the model can choose a marked EDIT action:

json
{
"model": "deepseek-v4-flash",
"messages": [
{
"role": "system",
"content": "You are DeepSeek GUI inline writing..."
},
{
"role": "user",
"content": "<<<PREFIX ... >>>\n<<<EDIT_SCOPE ... >>>\n<<<SUFFIX ... >>>"
}
],
"max_tokens": 512
}

This allows the model to see both the front and rear boundaries of the edit and treat the retrieved fragment as a reference-only context.

Recent edits Intent Signal

BM25 + keyword RAG solves cross-file references, recent edits solves the editing intention that just happened in the current file. The implementation logs user input and deleted/inserted text produced by AI in-place editing, before and after neighbors, edit source, and AI editing instructions.

Additionally, term casing and simple renaming are not entirely model dependent. The editor will propagate one-time phrase replacements in the same paragraph: when you change deepseek gui to DeepSeek GUI or DXGUI, other deepseek gui in the same natural paragraph will be replaced simultaneously. This deterministic layer is responsible for the consistency of "must happen", and recent edits and RAG are responsible for understanding this intent during subsequent AI edits.

When the user enters weak instructions such as "Continue to change like this", "Replace the same", "Retouch as before", the prompt will remind the model to infer the current editing mode from recent edits; if recent edits conflict with the current instruction, the current instruction will take precedence.

See docs/WRITE_INLINE_EDIT_RECENT_EDITS.en.md for detailed technical description.

markdown
Recent local edits in this file. Treat these as intent signals...

[1] 2s ago; source=user; range=20-32
Deleted: DeepSeek GUI
Inserted: Work mode
Around: Earlier term: [[edit]] should be consistent.

Why do we need to expand the paragraph in a short selection area?

"Replacing a noun and simultaneously replacing other parts of the paragraph" essentially does not replace a few selected characters, but allows the model to rewrite the paragraph.

So there is a heuristic in this implementation:

- The selected text must not exceed 120 characters.
- The selection does not span empty lines.
- then the editing scope extends to the natural segment between the nearest blank line, heading, code fence, or separator line.

In this way, the user only needs to select a word and enter "Change Alpha to Work mode". The model will get the original of the entire paragraph and return the replacement of the entire paragraph.

Failure protection

Editing in place is riskier than ghost text, so several layers of protection are added to the implementation:

- It will fail directly when there is no API key, the completion capability is turned off, and there is no command.
- Multiple selections are not supported at the moment to avoid errors when merging multiple non-consecutive ranges.
- When the model returns empty text, only delete class directives are allowed to be applied.
- After the request returns, it will be checked whether the original editing range is still the same as when the request was made; if the user has already changed this section, the application will be rejected.
- Work's original autosave mechanism is still used after application, and the editor state is not bypassed.

Implementation files

Major new additions and changes:

- src/shared/write-inline-edit.ts: Edit request/result type.
- src/main/services/write-inline-completion-service.ts: handles mode: "edit" requests, FIM/chat edit prompts, RAG injection, action parsing, and debug logging.
- src/renderer/src/write/inline-edit.ts: Expand the selection section, construct the payload, and apply replacement.
- src/renderer/src/write/recent-edits.ts: Logging, filtering and prompt payload conversion of recent edit contexts.
- src/renderer/src/write/term-propagation.ts: Term case/rename propagation in the same paragraph.
- src/renderer/src/components/write/WriteWorkspaceView.tsx: The selected-text floating layer supports two paths: "AI Editing" and "Send to Work Assistant"; the component name retains the internal Write compatibility identifier.
- src/main/ipc/app-ipc-schemas.ts, src/preload/index.ts, src/shared/kun-gui-api.ts: the existing write:inline-completion IPC accepts mode: "edit" plus editCandidate and recent edits.

Test coverage:

- Edit payload schema.
- Edit action requests and replacement extraction.
- RAG fragment injection into editing prompt.
- Recent edits inject editing prompt.
- Propagation of terminology in the same paragraph.
- Short selections are expanded into paragraphs.
- replacement only replaces the resolved range.

You can continue to explore later

- Added diff preview to allow users to confirm before applying.
- Display RAG hit segment number, recent edits hit number and source as lightweight hints.
- Select a narrower or wider editing range for different instruction categories. For example, "correct typos" do not need to be expanded, and "unify terminology" should be expanded.
- Added optional editing modes such as "minimal changes" or "readability rewrite" to prompt.

This version first clears the most critical closed loop: selected words -> paragraph-level inline edit -> BM25/keyword RAG assistance -> in-situ replacement. It shares basic capabilities with completion, but the interaction goals are clearly different: completion is to add text after the cursor, and editing is to rewrite in a controlled range.

---

WRITE INLINE EDIT RAG.Zh CN

用 BM25 + 关键词 RAG 做 Work 文本编辑:一次探索和落地

Work 里的文本补全已经证明了一件事:写作场景不一定需要重型向量库。只要能在低延迟内从同一个工作空间里找回术语、事实和风格片段,FIM 模型就能更稳地接住当前段落。

这次探索的问题更具体:文本编辑能不能也走 BM25 + 关键词 RAG? 例如用户选中一个名词,让 AI 把这个段落里的其他同名词也替换掉。它不是传统 ghost text 补全,而是对一段已有文本做原地替换。

结论是:可以,但它应该被设计成“带边界标记的中间替换”,而不是“光标续写”。

为什么不是普通补全

补全的目标是预测光标后的下一小段文本:

text
prefix [cursor] suffix

编辑的目标则是替换一段已有文本:

text
prefix [original edit scope] suffix

如果仍然把编辑当成 cursor completion,模型只能在光标处插入内容,很难自然完成“段落内其他位置也一起替换”。更合适的办法是把待编辑段落挖空:

text
prompt = 编辑指令 + 检索片段 + prefix
suffix = suffix
model returns = replacement for original edit scope

也就是说,“middle”不再是空光标,而是一个需要被重新生成的段落或选区。

RAG 在编辑里解决什么问题

BM25 + 关键词 RAG 不负责替模型“决定怎么改”,它负责给模型提供局部事实和写作约束:

- 产品名、人物名、项目术语应该怎么写。
- 同一工作空间里类似段落的语气和句式。
- 某个概念在其他文档里的标准表述。
- 用户选中短词时,哪些跨文档片段能解释这个词的上下文。

这很适合编辑任务,因为编辑通常更怕“改飞”而不是“不够发散”。关键词检索比 embedding 更朴素,但在术语替换、风格延续、同主题段落召回上很有效,且桌面端成本低。

设计取舍

这次实现复用现有 Work 补全的检索服务:

- 继续扫描当前工作空间内的 Markdown / 文本文件。
- 继续用中英文 token + 中文 2 到 4 字 n-gram。
- 继续用 BM25 分数加标题、路径、短语命中加权。
- 当前正在编辑的文件仍从检索结果中排除,避免把原文重复喂回模型。

新增的是编辑层:

- 用户选中短词或短句时,默认把编辑范围扩展到当前自然段。
- 用户选中较长文本或跨空行文本时,只编辑原选区。
- 用户手动做一次性短语替换时,会先用确定性规则把同段其他同短语一起替换,例如 deepseek gui -> DeepSeek GUI
- 渲染端发送 prefixsuffixoriginalinstruction 和选区元数据。
- 渲染端会带上最近 2 分钟内当前文件的用户/AI 编辑记录,帮助模型理解“继续这样改”。
- 主进程构造编辑 prompt,并把检索片段作为 reference-only 上下文注入。
- 模型只返回 replacement,渲染端把 replacement 原地替换回文档。

Prompt 形态

编辑 prompt 的关键是边界清晰:

markdown
原始 prefix...

显式编辑请求目前复用 write:inline-completion IPC,但主进程会通过 chat completions 发送,让模型返回带标记的 EDIT action:

json
{
"model": "deepseek-v4-flash",
"messages": [
{
"role": "system",
"content": "You are DeepSeek GUI inline writing..."
},
{
"role": "user",
"content": "<<<PREFIX ... >>>\n<<<EDIT_SCOPE ... >>>\n<<<SUFFIX ... >>>"
}
],
"max_tokens": 512
}

这样可以让模型同时看到编辑前后边界,并把检索片段当成 reference-only 上下文。

Recent edits 意图信号

BM25 + 关键词 RAG 解决的是跨文件参考,recent edits 解决的是当前文件里刚刚发生的编辑意图。实现会记录用户输入和 AI 原地编辑产生的删除/插入文本、前后邻域、编辑来源和 AI 编辑指令。

此外,术语大小写和简单重命名不完全依赖模型。编辑器会对一次性短语替换做同段传播:当你把 deepseek gui 改成 DeepSeek GUIDXGUI,同一个自然段里其他 deepseek gui 会同步替换。这个确定性层负责“必须发生”的一致性,recent edits 和 RAG 再负责后续 AI 编辑时理解这种意图。

当用户输入“继续这样改”“同样替换”“照刚才那样润色”这类弱指令时,prompt 会提醒模型从 recent edits 中推断当前编辑模式;如果 recent edits 和当前指令冲突,则优先当前指令。

详细技术说明见 docs/WRITE_INLINE_EDIT_RECENT_EDITS.zh-CN.md

markdown
Recent local edits in this file. Treat these as intent signals...

[1] 2s ago; source=user; range=20-32
Deleted: DeepSeek GUI
Inserted: Work mode
Around: Earlier term: [[edit]] should be consistent.

为什么短选区要扩段落

“替换某个名词,并同步替换段落其他地方”本质上不是替换选中的几个字符,而是让模型重写这个段落。

因此这次实现里有一个启发式:

- 选中文本不超过 120 个字符。
- 选区没有跨空行。
- 则编辑范围扩展到最近空行、标题、代码围栏或分隔线之间的自然段。

这样用户只需要选中一个词,输入“把 Alpha 改成 Work mode”,模型拿到的是整个段落的 original,返回的也是整个段落 replacement。

失败保护

原地编辑比 ghost text 风险更高,所以实现里加了几层保护:

- 没有 API key、补全能力关闭、无指令时直接失败。
- 多选区暂不支持,避免把多个非连续范围合并出错。
- 模型返回空文本时,只有删除类指令才允许应用。
- 请求返回后会检查原编辑范围是否仍和发起请求时一致;如果用户已经改过这段,就拒绝应用。
- 应用后仍走 Work 原有 autosave 机制,不绕过编辑器状态。

落地文件

主要新增和改动:

- src/shared/write-inline-edit.ts:编辑请求/结果类型。
- src/main/services/write-inline-completion-service.ts:处理 mode: "edit" 请求、FIM / chat 编辑 prompt、RAG 注入、action 解析和调试日志。
- src/renderer/src/write/inline-edit.ts:选区扩段落、构造 payload、应用 replacement。
- src/renderer/src/write/recent-edits.ts:最近编辑上下文的记录、筛选和 prompt payload 转换。
- src/renderer/src/write/term-propagation.ts:同段术语大小写/重命名传播。
- src/renderer/src/components/write/WriteWorkspaceView.tsx:选中文本浮层支持“AI 编辑”和“发送到办公助手”两条路径(组件名保留内部 Write 兼容标识)。
- src/main/ipc/app-ipc-schemas.tssrc/preload/index.tssrc/shared/kun-gui-api.ts:复用 write:inline-completion IPC,通过 mode: "edit"editCandidate 和 recent edits 承载 inline edit。

测试覆盖:

- 编辑 payload schema。
- 编辑 action 请求和 replacement 提取。
- RAG 片段注入编辑 prompt。
- Recent edits 注入编辑 prompt。
- 同段术语传播。
- 短选区扩展为段落。
- replacement 只替换解析出的范围。

后续可以继续探索

- 增加 diff preview,让用户确认后再应用。
- 把 RAG 命中片段数量、recent edits 命中数量和来源展示为轻量提示。
- 对不同指令分类选择更窄或更宽的编辑范围,例如“改错别字”不必扩段,“统一术语”应扩段。
- 在 prompt 中加入“最小改动”或“可读性重写”等可选编辑模式。

这一版先把最关键的闭环打通:选中词语 -> 段落级 inline edit -> BM25/关键词 RAG 辅助 -> 原地替换。它和补全共享基础能力,但交互目标明确不同:补全是在光标后加字,编辑是在一个受控范围里重写。

---

WRITE INLINE EDIT RECENT EDITS.En

Work text editor's Recent Edits intent context

This document explains the "recent edit context" capability added this round. The problem it solves is: after the user has just made a series of edits, and then selects a piece of text for AI to edit, the model should know "what happened in the last second", so as to better understand weak instructions such as "continue to change like this", "replace similarly" and "keep this style".

Background

The previous version of inline edit already had three types of context:

- prefix/suffix: provider context around the editable range.
- original: the current selection or paragraph to be replaced.
- BM25 + Keyword RAG: Recall terms, facts and style fragments from other documents in the writing space.

These contexts can explain "what the current paragraph is", but they cannot explain "how the user just changed it". For example:

1. The user manually changes the first DeepSeek GUI to Work mode.
2. The user selects another word in the same paragraph and enters "Continue to change like this".

Without recent edits, the model can only guess what "such" means. New ability to inject recent edits into the prompt as an intent signal.

Data collection

The collection point is updateListener of CodeMirror:

- Only log real document changes docChanged.
- Full replacement caused by external synchronization will be excluded through externalValueSyncAnnotation.
- Each change records deleted text, inserted text, left neighborhood of the old document, and right neighborhood of the new document.
- After the AI ​​in-place editing application is successful, an edit from the inline-edit source will be manually recorded, because such changes are synchronized back to the editor through the React value and will not be recorded as user input by CodeMirror.

Deterministic term propagation

In addition to leaving recent edits to the model for understanding, this round also adds a rigid capability that does not depend on the model: same paragraph term propagation.

When a user replaces one phrase with another all at once, for example:

text
deepseek gui -> DeepSeek GUI
deepseek gui -> DXGUI

The editor will search for other case-insensitive identical phrases in the current natural paragraph and replace them synchronously. This solves the basic text editing experience of "I just changed this to uppercase, and everything else should also be uppercase."

In order to avoid accidental injury, there are these restrictions on dissemination:

- Propagate only within the same natural paragraph, not across blank lines, headers, code fences and separators.
- Only handles one-time phrase replacement, not normal verbatim input.
- The phrase must be morphologically similar to a term, such as being long enough, containing spaces, uppercase and lowercase letters, numbers, underscores, or hyphens.
- Will check word boundaries to avoid accidentally replacing local strings in mydeepseek gui.

Record structure:

ts
type WriteRecentEdit = {
source: 'user' | 'inline-edit'
timestamp: number
filePath: string
from: number
to: number
deletedText: string
insertedText: string
beforeContext: string
afterContext: string
instruction?: string
scopeKind?: 'selection' | 'paragraph'
}

Noise control

Recent edits are short-term, lightweight, in-memory context and are not persisted.

Current strategy:

- Keep up to 48 entries.
- TTL is 2 minutes.
- Continuous typing will be merged into a single record within a 3 second window to avoid splitting a term into multiple single character signals.
- Single deletion/insertion of text will be length-cropped.
- When constructing the inline edit payload, only the current file is taken.
- The sorting will combine "the newer, the more important" and "the closer to the current editing range, the more important".
- Inject up to 8 bars.

This can overwrite "what was changed in the last second" and avoid mistaking editing habits long ago for current intentions.

Prompt injection

The main process adds a Recent local edits block to the provider prompt. For automatic short/long completions this is part of the hidden Markdown comment; for explicit edit requests it is included in the chat action prompt.

markdown

At the same time, the system constraints are clearly stated:

- recent edits is an intent signal, not a forced copy.
- Intent can be inferred from recent edits when the user says "continue", "the same", "like this".
- If recent edits conflict with the current command, the current command takes precedence.

Troubleshooting dashboard

In order to locate "why the model is not edited/completed as expected", an AI writing call log pop-up window has been added to the Work area of the settings page. Inline edit and inline completion both call write:inline-completion; edit requests are recorded as mode: "edit" in the main process memory. The user can refresh and view recent records, or clear them with one click.

Text editing record display:

- The actual provider prompt or chat messages.
- The tracked suffix / trailing context.
- Original editing scope original.
- Model raw return rawResponse.
- Parsed replacement.
- Model used, time taken, number of RAG fragments, number of recent edits and error message.

Text completion record display:

- The actual provider prompt.
- The tracked suffix.
- Model raw return rawResponse.
- Parsed completion.
- Completion mode, model, time taken, number of RAG fragments and error messages.

This can distinguish three types of problems:

- Prompt did not make the "uniform terminology/case" clear.
- The model returned does not obey the replacement-only constraint.
- Application layer replacement scope or deterministic propagation logic does not take effect.

Relationship with RAG

BM25 + keywords RAG and recent edits to solve different problems:

- RAG is responsible for cross-document fact, terminology, and style recall.
- Recent edits is responsible for the editing intentions that just occurred in the current file.

In implementation, recent edits will also participate in retrieval query construction. That is, after a user has just replaced an old term with a new term, it will be easier for subsequent editors to recall reference fragments containing the new term.

mermaid
flowchart LR
A["CodeMirror docChanged"] --> B["record recent edit"]
B --> C["Zustand write state"]
D["user selection + edit instruction"] --> E["resolve edit scope"]
C --> F["filter recent edits for current file"]
E --> F
F --> G["write:inline-completion payload (mode=edit)"]
G --> H["main process builds edit prompt"]
H --> I["DeepSeek EDIT action"]
I --> J["replace in place"]
J --> K["record inline-edit recent edit"]

Implementation files

- src/renderer/src/write/recent-edits.ts: recent edit creation, cropping, TTL, filtering and sorting.
- src/renderer/src/write/term-propagation.ts: Term case/rename propagation in the same paragraph.
- src/renderer/src/components/write/WriteMarkdownEditor.tsx: Collect user edits from CodeMirror transaction.
- src/renderer/src/write/write-workspace-store.ts: Save recent edits.
- src/renderer/src/write/inline-edit.ts: Put recent edits into inline edit payload.
- src/main/services/write-inline-completion-service.ts: Inject recent edits into edit/completion prompts, participate in RAG query, parse the returned action, and record the shared debug log.
- src/renderer/src/components/SettingsView.tsx: Display text editing/completion call log pop-up window.
- src/shared/write-inline-edit.ts: Shared payload type.

Test coverage

- Creation of recent edits, TTL filtering, current file filtering.
- Same paragraph term propagation and word boundary protection.
- write:inline-completion payloads with mode: "edit" can carry recent edits.
- IPC schema accepts structured recent edits.
- Provider prompts contain the recent edits intent signal.

Follow-up direction

- Merge continuous typing into coarser-grained editing segments to reduce prompt noise.
- Automatically adjust the weight of recent edits according to instructions, for example, "Same as above" relies more on history, "Rewrite this paragraph" relies more on the current selection.
- Added diff preview to allow users to confirm before applying replacement.

---

WRITE INLINE EDIT RECENT EDITS.Zh CN

Work 文本编辑的 Recent Edits 意图上下文

这份文档说明本轮新增的“最近编辑上下文”能力。它解决的问题是:用户刚刚做过一串编辑后,再选中一段文字让 AI 编辑时,模型应该知道“上一秒发生了什么”,从而更好地理解“继续这样改”“同样替换”“保持这个风格”这类弱指令。

背景

上一版 inline edit 已经具备三类上下文:

- prefix / suffix:可编辑范围前后的 provider 上下文。
- original:当前要替换的选区或段落。
- BM25 + 关键词 RAG:从工作空间其他文件召回术语、事实和风格片段。

这些上下文能解释“当前段落是什么”,但不能解释“用户刚刚在怎么改”。例如:

1. 用户手动把第一处 DeepSeek GUI 改成 Work mode
2. 用户又选中同一段里的另一个词,输入“继续这样改”。

没有 recent edits 时,模型只能猜“这样”指什么。新增能力会把最近编辑作为意图信号注入 prompt。

数据采集

采集点在 CodeMirror 的 updateListener

- 只记录真实文档变化 docChanged
- 外部同步产生的全量替换会通过 externalValueSyncAnnotation 排除。
- 每次 change 记录删除文本、插入文本、旧文档左侧邻域、新文档右侧邻域。
- AI 原地编辑应用成功后,会手动记录一条 inline-edit 来源的 edit,因为这类变更通过 React value 同步回编辑器,不会被 CodeMirror 当作用户输入记录。

确定性术语传播

除了把 recent edits 交给模型理解,本轮还补了一个不依赖模型的刚性能力:同段术语传播

当用户一次性把一个短语替换成另一个短语时,例如:

text
deepseek gui -> DeepSeek GUI
deepseek gui -> DXGUI

编辑器会在当前自然段内查找其他大小写不敏感的同短语,并同步替换。这解决的是“我刚把这里改成大写,其他地方也应该变成大写”的基础文本编辑体验。

为了避免误伤,传播有这些限制:

- 只在同一自然段内传播,不跨空行、标题、代码围栏和分隔线。
- 只处理一次性短语替换,不处理普通逐字输入。
- 要求短语形态足够像术语,例如长度足够、包含空格、大小写、数字、下划线或连字符。
- 会检查词边界,避免把 mydeepseek gui 里的局部字符串误替换。

记录结构:

ts
type WriteRecentEdit = {
source: 'user' | 'inline-edit'
timestamp: number
filePath: string
from: number
to: number
deletedText: string
insertedText: string
beforeContext: string
afterContext: string
instruction?: string
scopeKind?: 'selection' | 'paragraph'
}

噪声控制

Recent edits 是短期、轻量、内存态上下文,不做持久化。

当前策略:

- 最多保留 48 条。
- TTL 为 2 分钟。
- 连续打字会在 3 秒窗口内合并成一条记录,避免把一个术语拆成多个单字符信号。
- 单条删除/插入文本会做长度裁剪。
- 构造 inline edit payload 时,只取当前文件。
- 排序会综合“越新越重要”和“离当前编辑范围越近越重要”。
- 最多注入 8 条。

这能覆盖“上一秒改了什么”,同时避免把长时间以前的编辑习惯误当成当前意图。

Prompt 注入

主进程会把 Recent local edits 区块加入 provider prompt。自动 short/long 补全会放进隐藏 Markdown comment;显式 edit 请求会放进 chat action prompt。

markdown

同时系统约束明确说明:

- recent edits 是 intent signal,不是强制复制。
- 当用户说“继续”“同样”“照这样”时,可以从 recent edits 推断意图。
- 如果 recent edits 和当前指令冲突,优先当前指令。

排障看板

为了定位“模型为什么没按预期编辑/补全”,设置页的 Work 区域新增了 AI 写作调用日志弹窗。inline edit 和 inline completion 都复用 write:inline-completion 调用;编辑请求会以 mode: "edit" 记录在主进程内存中,用户可以刷新查看最近记录,也可以一键清空。

文本编辑记录展示:

- 实际发送给 provider 的 prompt 或 chat messages。
- 记录的 suffix / 后置上下文。
- 原始编辑范围 original
- 模型原始返回 rawResponse
- 解析后的 replacement
- 使用的模型、耗时、RAG 片段数、recent edits 数量和错误信息。

文本补全记录展示:

- 实际发送给 provider 的 prompt
- 记录的 suffix
- 模型原始返回 rawResponse
- 解析后的 completion
- 补全模式、模型、耗时、RAG 片段数和错误信息。

这样可以区分三类问题:

- Prompt 没把“统一术语/大小写”说清楚。
- 模型返回没有遵守 replacement-only 约束。
- 应用层替换范围或确定性传播逻辑没有生效。

与 RAG 的关系

BM25 + 关键词 RAG 和 recent edits 解决不同问题:

- RAG 负责跨文件事实、术语、风格召回。
- Recent edits 负责当前文件里刚发生的编辑意图。

实现上,recent edits 也会参与检索查询构造。也就是说,用户刚把一个旧术语替换成新术语后,后续编辑会更容易召回包含新术语的参考片段。

应用链路

mermaid
flowchart LR
A["CodeMirror docChanged"] --> B["记录 recent edit"]
B --> C["Zustand 写作状态"]
D["用户选区 + 编辑指令"] --> E["解析编辑 scope"]
C --> F["筛选当前文件 recent edits"]
E --> F
F --> G["write:inline-completion payload (mode=edit)"]
G --> H["主进程构造编辑 prompt"]
H --> I["DeepSeek EDIT action"]
I --> J["原地替换"]
J --> K["记录 inline-edit recent edit"]

落地文件

- src/renderer/src/write/recent-edits.ts:recent edit 创建、裁剪、TTL、筛选和排序。
- src/renderer/src/write/term-propagation.ts:同段术语大小写/重命名传播。
- src/renderer/src/components/write/WriteMarkdownEditor.tsx:从 CodeMirror transaction 采集用户编辑。
- src/renderer/src/write/write-workspace-store.ts:保存 recent edits。
- src/renderer/src/write/inline-edit.ts:把 recent edits 放入 inline edit payload。
- src/main/services/write-inline-completion-service.ts:把 recent edits 注入编辑/补全 prompt,参与 RAG 查询,解析返回 action,并记录共享调试日志。
- src/renderer/src/components/SettingsView.tsx:展示文本编辑/补全调用日志弹窗。
- src/shared/write-inline-edit.ts:共享 payload 类型。

测试覆盖

- recent edits 的创建、TTL 过滤、当前文件过滤。
- 同段术语传播和词边界保护。
- mode: "edit"write:inline-completion payload 能携带 recent edits。
- IPC schema 接受结构化 recent edits。
- provider prompt 会包含 recent edits intent signal。

后续方向

- 把连续打字合并成更粗粒度的编辑片段,减少 prompt 噪声。
- 根据指令自动调整 recent edits 权重,例如“同上”更依赖历史,“重写这段”更依赖当前选区。
- 增加 diff preview,让用户在应用 replacement 前确认。

---

WRITE RETRIEVAL RAG.En

Work cross-text BM25 + keyword search RAG technical description

This document describes cross-text retrieval enhancements for text-writing tasks in Work. Its goal is not to build a complete knowledge base, but to find relevant fragments from the current Work workspace with very low latency before text completion occurs, helping the model maintain terminology, fact and style continuity.

Target

- When the user writes the current file, automatically reference other Markdown/text files in the same writing space.
- Prioritize service text completion scenarios, which require fast retrieval, short context, and silent degradation if failure occurs.
- Does not rely on embedding, vector databases or external services, reducing the configuration cost of local desktop applications.
- Available for both short completion and inspired long completion, but long completion can get more retrieved fragments.

non-target

- No global semantic question and answer.
- No long-term persistent indexing.
- Do not display retrieved fragments to the user.
- Do not force the model to use retrieved content; retrieved fragments only serve as reference-only context.

Overall architecture

mermaid
flowchart LR
A["CodeMirror cursor context"] --> B["completion payload"]
B --> C["main-process FIM completion service"]
C --> D["writing-space retrieval service"]
D --> E["scan Markdown / text files"]
E --> F["chunk + tokenize + BM25 index"]
F --> G["keyword/BM25 recalled snippets"]
G --> H["inject hidden Markdown comment into prompt"]
H --> I["DeepSeek FIM /completions"]

Core implementation:

- src/main/services/write-retrieval-service.ts
- src/main/services/write-inline-completion-service.ts
- src/shared/write-inline-completion.ts

Data input

The retrieval service receives a WriteInlineCompletionRequest, focusing on these fields:

- workspaceRoot: the root directory of the current Work workspace.
- currentFilePath: The file currently being edited, this file will be excluded from the search results.
- prefix: the text window in front of the cursor.
- context.currentLinePrefix: The text before the cursor in the current line.
- context.previousNonEmptyLine: the previous non-empty line.
- preview.documentTail: summary of the tail of the current document.
- mode: short or long, determines the number of recalled fragments.

File scanning strategy

The search only scans local files in the current writing space and performs hard upper limit control:

- Support extensions: .md, .markdown, .mdx, .txt
- Skip directories: .git, node_modules, dist, build, out, .next, coverage, etc.
- Maximum number of scan entries: 8_000
- Maximum number of index files: 160
- Maximum read bytes of a single file: 600_000
- Maximum number of index blocks: 720

These limits are designed to keep completion requests predictable. Even with a large writing space, retrieval does not degenerate into an expensive full search.

Text chunking

Chunking prioritizes Markdown writing structure:

- The title row updates the current block title.
- The natural paragraph after a blank line can form a boundary.
- The maximum number of characters in a single block is approximately 900.
- Clips that are too short will be discarded to avoid noise.
- Each block saves the path, relative path, title, body, and starting and ending line numbers.

The advantage of this is that the search results usually fall on "a natural segment" or "a bar fragment" rather than an arbitrary character window.

Word segmentation strategy

In order to adapt to both Chinese and English writing, lightweight rules are used for word segmentation:

- English/Number: Extract a-z0-9_- tokens whose length is greater than or equal to 2.
- Chinese: Generate 2 to 4 character n-grams for continuous Chinese fields.
- English stop words will be filtered, such as the, and, with, this.
- The text will be NFKC normalized first and converted to lowercase.

This kind of rule is not as "smart" as embedding, but it is very fast and enough to capture terms, character names, subject headings, proprietary phrases and Chinese keywords.

Query construction

Completion requests are compressed into a weighted query:

| Source | Weight | Role |
| --- | ---: | --- |
| Text before cursor in current line | 3.0 | Strongest local intent |
| Previous non-blank line | 2.0 | Current paragraph/section context |
| Previous line | 1.4 | Proximity continuity |
| Summary at the end of the current document | 1.0 | Document-level topic |
| prefix tail window | 0.7 | Wider local context |

The query retains up to 36 high-weight tokens. This can reduce the interference of the tail of long documents on retrieval and give priority to the intention near the current cursor.

Sorting formula

The final score consists of two parts:

text
score = BM25(chunk, query) + keywordBoost(chunk, query)

BM25 uses the standard form:

text
idf = log(1 + (N - df + 0.5) / (df + 0.5))
norm = (tf (k1 + 1)) / (tf + k1 (1 - b + b * chunkLength / avgLength))

Current parameters:

- k1 = 1.2
- b = 0.72

Keyword enhancements include:

- Hit the title token for extra points.
- Bonus points for hitting the file path token.
- Hit the current line/previous line phrase for extra points.
- The more hit tokens, the slight coverage bonus.

The results filter out low-scoring clips, empty clips, duplicate clips, and limit a single file to contribute up to 2 clips.

Prompt injection

The search results will not be appended directly to the user text, but will be placed in front of the FIM prompt in the form of a hidden Markdown comment:

markdown
raw prefix...

This has three advantages:

- Explicitly state to the model that these contents are reference-only.
- Don't change suffix after cursor.
- If the model behaves normally, the returned results will only be insertable text and will not expose retrieval meta-information.

The difference between short completion and long completion

The retrieval service itself is the same, but the number of recalls is different:

- short: up to 3 fragments, serving local completion with low latency and low interruption.
- long: up to 5 fragments, inspirational continuation after service pause, requiring stronger cross-text grounding.

Long completion is still not "full text generation". Retrieving fragments only helps the model catch the context, not let it diverge.

Caching and performance

The index is a short TTL memory cache:

- TTL: 30 seconds
- Cache key: writing space root directory
- Cache content: chunking, term frequency, document frequency, average chunk length

When writing, completion is often triggered multiple times in a row. A short TTL cache avoids rescanning the file on every request while allowing for faster refresh when the user modifies the file.

Security Boundary

- Only read files within workspaceRoot.
- The currently edited file will be excluded from the search results to avoid repeated completion of the current text.
- Binary files are skipped via NUL byte detection.
- There is a byte limit for single file reading.
- Failure to read the file will be ignored and completion will not be blocked.
- When there is no hit result, completion will degrade to normal FIM.

Failed to downgrade

The following situations will silently return null:

- There is no workspaceRoot.
- The current file is not in the writing space.
- Index is empty.
- The query token is empty.
- There are no segments that exceed the threshold.
- A local error occurred during file scanning or reading.

The main process completion service will catch the retrieval exception and continue to request FIM using the original prompt.

Adjustable parameters

The current parameters are concentrated at the top of write-retrieval-service.ts:

- INDEX_CACHE_TTL_MS
- MAX_SCAN_ENTRIES
- MAX_INDEX_FILES
- MAX_FILE_BYTES
- MAX_INDEX_CHUNKS
- MAX_CHUNK_CHARS
- MAX_QUERY_TERMS
- DEFAULT_MAX_SNIPPETS

If UI is needed in the future, it is recommended to expose it first:

- Whether to enable search enhancement.
- Number of fragments retrieved for long completion.
- The maximum number of index files in the writing space.

Test coverage

Related tests:

- src/main/services/write-retrieval-service.test.ts
- src/main/services/write-inline-completion-service.test.ts
- src/main/ipc/app-ipc-schemas.test.ts

Key coverage:

- Chinese and English token generation.
- Recall across document fragments.
- Exclude the current file.
- Retrieve fragment injection FIM prompt.
- Keep the original prompt when there are no search results.

---

WRITE RETRIEVAL RAG.Zh CN

Work 跨文本 BM25 + 关键词检索 RAG 技术说明

这份文档说明 Work 办公模式中文本写作场景的跨文本检索增强方案。它的目标不是做一个完整知识库,而是在文本补全发生前,用很低的延迟从当前办公空间里找出相关片段,帮助模型保持术语、事实和风格连续性。

目标

- 在用户写作当前文件时,自动参考同一工作空间里的其他 Markdown / 文本文件。
- 优先服务文本补全场景,要求检索快、上下文短、失败可静默降级。
- 不依赖 embedding、向量数据库或外部服务,降低本地桌面应用的配置成本。
- 对短补全和灵感长补全都可用,但长补全可以获得更多检索片段。

非目标

- 不做全局语义问答。
- 不做长期持久化索引。
- 不把检索片段展示给用户。
- 不强迫模型使用检索内容;检索片段只作为 reference-only 上下文。

总体架构

mermaid
flowchart LR
A["CodeMirror 光标上下文"] --> B["补全 payload"]
B --> C["主进程 FIM 补全服务"]
C --> D["工作空间检索服务"]
D --> E["扫描 Markdown / 文本文件"]
E --> F["分块 + 分词 + BM25 索引"]
F --> G["关键词/BM25 召回片段"]
G --> H["隐藏 Markdown comment 注入 prompt"]
H --> I["DeepSeek FIM /completions"]

核心实现:

- src/main/services/write-retrieval-service.ts
- src/main/services/write-inline-completion-service.ts
- src/shared/write-inline-completion.ts

数据输入

检索服务接收 WriteInlineCompletionRequest,重点使用这些字段:

- workspaceRoot:当前 Work 工作空间根目录。
- currentFilePath:当前正在编辑的文件,检索结果会排除这个文件。
- prefix:光标前文本窗口。
- context.currentLinePrefix:当前行光标前文本。
- context.previousNonEmptyLine:上一条非空行。
- preview.documentTail:当前文档尾部摘要。
- modeshortlong,决定召回片段数量。

文件扫描策略

检索只扫描当前工作空间内的本地文件,并做硬上限控制:

- 支持扩展名:.md.markdown.mdx.txt
- 跳过目录:.gitnode_modulesdistbuildout.nextcoverage
- 最大扫描条目数:8_000
- 最大索引文件数:160
- 单文件最大读取字节:600_000
- 最大索引块数:720

这些限制的设计意图是让补全请求保持可预测。即使工作空间较大,检索也不会退化成一次昂贵的全盘搜索。

文本分块

分块以 Markdown 写作结构为优先:

- 标题行会更新当前块标题。
- 空行后的自然段可以形成边界。
- 单块最大字符数约 900
- 过短片段会被丢弃,避免噪声。
- 每个块保存路径、相对路径、标题、正文、起止行号。

这样做的好处是检索结果通常落在“一个自然段”或“一个小节片段”上,而不是任意字符窗口。

分词策略

为了同时适配中英文写作,分词采用轻量规则:

- 英文/数字:提取长度大于等于 2 的 a-z0-9_- token。
- 中文:对连续汉字段生成 2 到 4 字 n-gram。
- 英文停用词会过滤,例如 theandwiththis
- 文本会先做 NFKC normalize,并转小写。

这种规则没有 embedding 那么“聪明”,但非常快,也足够捕捉术语、人物名、主题词、专有短语和中文关键词。

查询构造

补全请求会被压缩成一个加权查询:

| 来源 | 权重 | 作用 |
| --- | ---: | --- |
| 当前行光标前文本 | 3.0 | 最强局部意图 |
| 上一条非空行 | 2.0 | 当前段落/小节语境 |
| 上一行 | 1.4 | 邻近连续性 |
| 当前文档尾部摘要 | 1.0 | 文档级主题 |
| prefix 尾部窗口 | 0.7 | 更宽的局部上下文 |

查询最多保留 36 个高权重 token。这样可以降低长文档尾部对检索的干扰,让当前光标附近的意图优先。

排序公式

最终分数由两部分组成:

text
score = BM25(chunk, query) + keywordBoost(chunk, query)

BM25 使用标准形式:

text
idf = log(1 + (N - df + 0.5) / (df + 0.5))
norm = (tf (k1 + 1)) / (tf + k1 (1 - b + b * chunkLength / avgLength))

当前参数:

- k1 = 1.2
- b = 0.72

关键词增强包括:

- 命中标题 token 加分。
- 命中文件路径 token 加分。
- 命中当前行/上一行短语加分。
- 命中 token 越多,有轻微覆盖度加分。

结果会过滤掉低分片段、空片段、重复片段,并限制单个文件最多贡献 2 个片段。

Prompt 注入

检索结果不会直接追加到用户正文里,而是以隐藏 Markdown comment 形式放在 FIM prompt 前:

markdown
原始 prefix...

这样有三个好处:

- 对模型明确说明这些内容是 reference-only。
- 不改变光标后的 suffix
- 如果模型表现正常,返回结果只会是可插入文本,不会暴露检索元信息。

短补全与长补全的差异

检索服务本身是同一个,但召回数量不同:

- short:最多 3 个片段,服务低延迟、低打扰的局部补全。
- long:最多 5 个片段,服务停顿后的灵感续写,需要更强跨文本 grounding。

长补全仍然不是“全文生成”。检索片段只帮助模型接住上下文,不让它发散。

缓存与性能

索引是短 TTL 内存缓存:

- TTL:30 秒
- 缓存 key:工作空间根目录
- 缓存内容:分块、词频、文档频率、平均块长度

写作时通常会连续触发多次补全。短 TTL 缓存可以避免每次请求都重新扫描文件,同时又能在用户修改文件后较快刷新。

安全边界

- 只读取 workspaceRoot 内文件。
- 当前编辑文件会从检索结果中排除,避免重复补全当前文本。
- 二进制文件会通过 NUL 字节检测跳过。
- 单文件读取有字节上限。
- 读文件失败会忽略,不阻塞补全。
- 没有命中结果时,补全会退化为普通 FIM。

失败降级

以下情况都会静默返回 null

- 没有 workspaceRoot
- 当前文件不在工作空间内。
- 索引为空。
- 查询 token 为空。
- 没有超过阈值的片段。
- 文件扫描或读取过程中出现局部错误。

主进程补全服务会捕获检索异常,继续用原始 prompt 请求 FIM。

可调参数

当前参数都集中在 write-retrieval-service.ts 顶部:

- INDEX_CACHE_TTL_MS
- MAX_SCAN_ENTRIES
- MAX_INDEX_FILES
- MAX_FILE_BYTES
- MAX_INDEX_CHUNKS
- MAX_CHUNK_CHARS
- MAX_QUERY_TERMS
- DEFAULT_MAX_SNIPPETS

后续如果需要 UI 化,建议优先暴露:

- 是否开启检索增强。
- 长补全检索片段数量。
- 工作空间最大索引文件数。

测试覆盖

相关测试:

- src/main/services/write-retrieval-service.test.ts
- src/main/services/write-inline-completion-service.test.ts
- src/main/ipc/app-ipc-schemas.test.ts

重点覆盖:

- 中英文 token 生成。
- 跨文档片段召回。
- 排除当前文件。
- 检索片段注入 FIM prompt。
- 无检索结果时保持原始 prompt。

---

Agent Request Observability

Agent Request Observability

Kun's Agent Perspective is a local, thread-scoped HTTP inspector for model requests. It captures the effective request assembled by CompatModelClient, each retry or compatibility fallback, the HTTP response, the bounded raw response stream, and Kun's decoded output. The feature is for inspecting a conversation; it is not a runtime/process diagnostics panel and has no provider controls or replay action.

Framework review

The existing ecosystem is useful, but its standard trace model and Kun's wire-inspection requirement are different layers:

| Project | Strength | Fit for exact Kun HTTP inspection |
| --- | --- | --- |
| OpenTelemetry GenAI semantic conventions | Vendor-neutral GenAI spans, metrics, events, and export | Good metadata/export vocabulary. Standard spans do not promise the literal final HTTP URL, headers, request bytes, or raw SSE frames. |
| Langfuse | LLM traces, generations, scores, prompt management, and self-hosting | Good optional trace backend; still needs Kun-specific Fetch-boundary capture for exact wire data. |
| Arize Phoenix | OpenTelemetry/OpenInference tracing, evaluation, and a local/self-hosted UI | Good investigation backend and future exporter target; normal semantic instrumentation is not a raw HTTP recorder. |
| Opik | Open-source LLM tracing, evaluation, dashboards, and self-hosting | Good full-trace product; integrating its SDK would not remove the need to capture Kun's retries and raw stream itself. |
| LangSmith | Agent tracing, monitoring, evaluation, OpenTelemetry ingestion | Strong managed/enterprise workflow; not an offline, exact-wire default for the Electron app. |
| OpenLLMetry | OpenTelemetry auto-instrumentation for common LLM SDKs | Useful when a supported SDK owns the call. Kun uses a custom compatibility client and needs stricter content and credential handling. |
| Helicone | Gateway/proxy observability close to the HTTP wire | Can see provider traffic, but making a proxy mandatory changes the request hot path, endpoint, authentication, and deployment model. |
| BeeAI Agent Stack | Open-source agent platform and orchestration | Relevant as an agent stack, but not a drop-in per-request wire inspector for Kun's single bundled runtime. |

The implementation therefore captures locally at the existing fetch boundary and uses stable fields that can later map to OpenTelemetry GenAI attributes. Kun's existing content-free AgentObservabilityRecorder remains the opt-in span/export path; Agent Perspective does not upload sensitive content.

Capture boundary and coverage

One logical model round may produce multiple HTTP exchange records:

- initial: the first provider call.
- transport_retry: a retry after a configured transient HTTP status.
- stream_options_fallback: a compatibility retry with a changed request body after a provider rejects stream_options.

Each record includes:

- thread, turn, provider, model, endpoint format, attempt ordinal, and reason;
- final sanitized request URL, POST headers, and the exact JSON string supplied to fetch;
- response status, status text, headers, and bounded raw response text (including SSE framing);
- start, time-to-headers, finish, duration, transport/capture errors, and truncation metadata;
- the semantic text, reasoning, tool calls, usage, stop reason, or error decoded by Kun.

Coverage includes model requests routed through CompatModelClient, Gemini CLI Code Assist, Claude Agent SDK, Cursor SDK, and Antigravity CLI. Renderer requests, tool traffic, MCP traffic, Work inline completion, scheduled-task detection, and other Electron services are not captured. An empty Agent Perspective panel does not mean that no non-model network traffic occurred.

The recorder never retries, rewrites, replays, or blocks a provider request because capture failed. It serializes each request body once, observes a cloned response, and isolates recorder failures from the agent-visible result.

Capture policy

The Agent Perspective header owns a persistent capture switch for the selected conversation. The switch is off by default; enabling it starts capture at the next model request boundary. Disabling it lets a trace that already started finish, stops later requests from being captured, and keeps existing history readable.

Settings → LLM request troubleshooting includes Capture new conversations by default. That setting controls only the initial state of conversations created afterward. Existing conversations, forks, side conversations, and resumed conversations retain independent state and are not rewritten when the setting changes.

The recorder facility remains available by default so a conversation switch can take effect immediately. Advanced installations can disable the facility entirely in Kun runtime configuration:

yaml
runtime:
llmDebug:
enabled: false

Changing the effective runtime.llmDebug.enabled value through runtime config apply returns restart_required; restart Kun to apply that advanced facility policy. Changing only runtime.llmDebug.defaultThreadCaptureEnabled hot-applies to later runtime-created conversations.

Capture is not retroactive. Requests that start while the conversation switch or recorder facility is disabled cannot be reconstructed, while trace files written earlier remain readable after restart.

Security and local storage

Completed exchange records are append-only JSONL files at:

text
<Kun dataDir>/observability/model-http/<base64url-thread-id>.jsonl

The observability directory is forced to mode 0700 and trace files to 0600. Active attempts live only in bounded runtime memory and are merged with completed records when queried. Deleting a conversation waits for its lifecycle fence and deletes its trace file. Malformed JSONL lines are ignored with a warning so an interrupted final append does not make the inspector unusable.

Credential values are removed before a record enters memory or storage:

- URL user info is stripped.
- Query values whose names imply keys, tokens, secrets, signatures, authentication, passwords, credentials, or cookies become [REDACTED].
- authorization, API-key, token, secret, cookie, signature, and similar request/response header values become [REDACTED].
- A configured API key is also redacted if it appears in another header value.

Header names are retained so request construction remains diagnosable. Request and response bodies are intentionally not redacted, because prompts and provider output are the data the inspector exists to show. Treat the trace directory as sensitive user data; do not attach it to an issue without reviewing its bodies.

The default capture limit is 4 MiB for each request body and each response body. Records expose captured/original byte counts and an explicit truncation flag. Truncation affects only the retained copy, not the provider request or primary response parser. Disk JSONL currently has no age-based automatic eviction; it follows the conversation lifetime.

API

The renderer reads traces through the authenticated runtime route:

http
GET /v1/threads/{threadId}/model-requests?limit=30&cursor=<opaque>
Authorization: Bearer <runtime-token>

The route first verifies that the thread exists, then returns newest-first records, an optional opaque cursor, active count, capture limits, schema version, and storage warnings. The capture switch uses the existing thread metadata contract:

http
PATCH /v1/threads/{threadId}
Content-Type: application/json

{"modelRequestCaptureEnabled":true}

Both paths are exposed through the constrained window.kunGui.runtimeRequest allowlist. There is no trace write or replay endpoint.

Using Agent Perspective

1. Open or select a Code conversation.
2. Select Agent Perspective (the scan/perspective icon) in the right rail.
3. Turn on Capture in the panel header before sending the request you want to inspect.
4. Pick an attempt in the left request timeline.
5. Use Overview, Request, Response, and Decoded to compare the provider wire data with Kun's parsed result.
6. Switch between formatted JSON and raw text where available. Copy actions copy only the already-sanitized DTO shown by the panel.
7. Use Load older requests to page backward. While the panel is visible and the thread runs, it refreshes once per second and performs one final refresh when the turn settles.

Useful diagnosis patterns:

- 404 or wrong path: compare Final request URL with provider Base URL and Endpoint format settings.
- Provider rejects a field: compare the initial and stream_options fallback request bodies.
- A retry changes the outcome: compare status, response body, timing, and attempt reason across records.
- UI output differs from provider output: compare Raw response with Decoded.
- Missing early data: confirm the conversation switch was enabled before the request began; earlier requests are not reconstructed.
- Capture unavailable: confirm runtime.llmDebug.enabled is not explicitly false, then restart Kun after changing the setting.

Validation targets

Focused coverage lives in:

- kun/src/services/model-request-trace-safety.test.ts
- kun/src/services/model-request-trace-store.test.ts
- kun/src/adapters/model/compat-model-client.observability.test.ts
- kun/src/server/routes/model-requests.test.ts
- src/renderer/src/agent/model-request-traces.test.ts
- src/renderer/src/components/workbench/useModelRequestTraces.test.ts
- src/renderer/src/components/workbench/AgentPerspectivePanel.test.ts

Run the focused suites first, followed by top-level typecheck, build:kun, and the production build.

---

Data Migration.En

Kun Data Migration Guide

中文:Kun 数据迁移指南

Settings → Data migration creates a .kunpack containing selected workspaces, conversation history, and portable app state. The package can be imported on Windows, macOS, or Linux. Package paths are portable relative paths; the importer maps them to a user-selected destination and validates the target file system's case, Unicode, reserved-name, path-length, and space rules.

Export

Choose workspaces, Complete or Smaller package, and content categories. Review sensitive-file findings, decide how running conversations are handled, and choose an output path outside all selected workspaces and migration staging/backup directories. Encryption with a passphrase of at least eight characters is recommended. Plaintext packages require an explicit warning acknowledgement.

The passphrase is used only in memory for this operation. It is never stored in settings, logs, reports, or recovery journals, and cannot be recovered if forgotten.

Import

Select and inspect the package before choosing destinations. Inspection validates format, supported versions, checksums, size budgets, archive paths, and link safety without changing destination data. Map each workspace, then choose Keep both (default), Merge, Replace with backup, or Skip. Resolve file conflicts and review the trust reset before importing.

Kun fully stages and verifies data on each destination volume before commit. Cross-volume changes cannot share one physical file-system transaction, so Kun uses durable journals, idempotent operations, same-volume atomic renames, identity-checked backups, and rollback.

Included and excluded data

Packages can include workspace files, canonical thread/session/item/event history, reachable attachments, artifacts and memory, Design/Write/Plan/SDD/fork registries, portable UI settings, and workflow/schedule definitions.

API keys, provider credentials, OAuth/account/runtime tokens, keychain data, device identity, key material, trust or approval grants, active processes, terminals, caches, indexes, logs, crash telemetry, channel credentials, webhook secrets, and live external bindings are never included.

Imported workspaces remain untrusted. Hooks, commands, extensions, workflows, schedules, Connect channels, and external actions do not activate automatically. Workflows and schedules are imported disabled with active bindings cleared. Pending approvals and user-input gates become non-actionable expired/cancelled history.

Cross-platform limitations

Kun rewrites typed workspace paths, thread IDs, attachment scopes, and known registry references. It does not edit paths embedded in source code, documents, or prose. Windows reserved names/ADS/case collisions, macOS Unicode-normalization collisions, target path limits, and unsupported links require an explicit plan decision. Only safe internal relative symbolic links are restored; external links, junctions, reparse points, devices, sockets, and FIFOs are excluded. Unsafe POSIX permission bits are stripped.

Historical provider/model labels remain readable. Starting a new turn requires an explicitly configured destination provider/model when the original is unavailable.

Cancellation, recovery, and support

Cancellation stops immediately during inspection, removes staging data during staging, or finishes the current atomic step and rolls back during commit. After a crash, the application-level migration banner requires Resume or Roll back before another migration can start.

Rollback removes only operation-created data whose identity still matches the journal. Independently modified paths or records are preserved and listed as manual recovery work. Sanitized reports are stored under the Kun user-data directory at data-migration/reports/; they contain stable codes, mappings, counts, exclusions, decisions, and warnings, but no passphrases or credentials. Backups are retained for seven days and active/recoverable data is never removed by disk-pressure cleanup.

Rollout flag

Data migration is enabled by default in development builds and packaged releases. A managed or diagnostic launch can set KUN_DATA_MIGRATION_ENABLED=0 to disable new exports and imports without hiding recovery for an interrupted operation; 1 explicitly enables the feature.

---

Data Migration

Kun 数据迁移指南

English: Kun Data Migration Guide

Kun 的“设置 → 数据迁移”可以把工作区文件、历史会话以及可迁移的应用状态打包为一个 .kunpack 文件,再导入另一台 Windows、macOS 或 Linux 电脑。迁移包使用平台无关的相对路径;导入时由用户选择新的基础目录,Kun 再按目标文件系统的路径、大小写、Unicode、保留名和长度规则重新规划。

创建迁移包

1. 打开“设置 → 数据迁移 → 创建迁移包”。
2. 选择工作区、完整/精简预设以及要包含的数据类别。
3. 检查敏感文件提示。推荐设置至少 8 个字符的迁移包密码;如果选择不加密,必须确认明文风险。
4. 选择正在运行的会话处理方式:等待、明确中断,或不包含。
5. 选择 .kunpack 保存位置并确认创建。保存位置不能位于所选工作区、迁移暂存目录或备份目录内。

密码只用于本次加解密,不会写入设置、日志、报告或恢复 journal。忘记密码后无法恢复迁移包。

导入迁移包

1. 在目标电脑打开“设置 → 数据迁移 → 导入迁移包”。
2. 选择 .kunpack,如有需要输入密码。此时只检查格式、版本、校验和、大小预算和归档安全,不修改目标数据。
3. 选择工作区基础目录。Kun 会显示每个源工作区到目标目录的映射和所需峰值空间。
4. 选择工作区策略:
- 保留两份:默认且最安全,使用不冲突的新目录。
- 合并:逐文件处理缺失、相同和不同内容。
- 备份后替换:先把现有数据移入迁移备份,再替换。
- 跳过:不导入该工作区。
5. 解决文件冲突,确认信任重置提示,然后开始导入。

导入会先在每个目标卷的同级隐藏目录完整暂存并校验,再通过持久化事务 journal 提交。不同磁盘无法提供单个文件系统事务,因此 Kun 使用幂等步骤、同卷原子重命名、身份校验备份和回滚实现产品级的全有或全无行为。

会迁移和不会迁移的内容

默认可包含:工作区文件、线程/会话/消息/事件历史、附件、content-addressed artifact、关联 memory、Design/Write/Plan/SDD/分支注册表、便携 UI 设置,以及工作流和定时任务定义。

以下内容永远不会迁移:

- API 密钥、Provider 凭证、OAuth 会话、账号或运行时 token;
- 系统钥匙串内容、设备身份、加密密钥材料;
- 已授予的工作区信任、approval、permission grant;
- 运行进程、终端会话、缓存、索引、日志、崩溃和观测数据;
- Connect 渠道凭证、Webhook secret 或任何活跃外部绑定。

导入的工作区保持未信任;hooks、命令、扩展、工作流、定时任务、Connect 渠道和外部操作不会自动执行。工作流定义以不可由 Agent 调用的禁用状态导入,定时任务保持禁用并清空渠道/Provider 绑定。历史中的待处理 approval 和用户输入会变为不可操作的 expired/cancelled 记录。

跨系统限制

- Kun 会重写结构化字段中的工作区路径、线程 ID、附件作用域和已知注册表引用,但不会修改代码、文档或自然语言中的路径文本。
- Windows 保留名、ADS、大小写冲突,macOS Unicode 归一化冲突,以及目标路径长度限制都需要在导入计划中显式处理。
- 只恢复指向包内目标的相对符号链接;外部链接、junction、reparse point、设备、socket 和 FIFO 不会被跟随或恢复。
- POSIX 危险权限位会被移除。目标文件系统不支持符号链接时必须跳过或改为普通文件策略。
- 不可用的历史 Provider/模型标签只用于阅读;开始新一轮对话前必须明确选择目标电脑已配置的 Provider/模型。

取消、崩溃恢复和报告

检查阶段取消不会修改目标;暂存阶段取消会清理临时数据;提交阶段取消会完成当前原子步骤后回滚。关闭应用不会把未完成操作假装成失败:下次启动时,全局迁移提示会要求先继续或回滚,其他迁移在恢复完成前被阻止。

回滚只删除本次 operation 创建且身份仍与 journal 相符的数据。若用户在导入后独立修改了路径、线程、附件元数据或 memory,Kun 会保留它并在迁移报告中给出人工恢复清单,不会按路径盲删。

最近报告位于 Kun 用户数据目录下的 data-migration/reports/。向支持人员提供报告即可;报告只含稳定错误码、映射、计数、排除项、冲突决定和脱敏警告,不包含密码或凭证。备份默认保留 7 天;磁盘压力清理也不会删除活跃或可恢复操作的数据。

发布开关

该功能在开发构建和正式安装包中默认开放。通过受管启动或诊断环境将 KUN_DATA_MIGRATION_ENABLED 设置为 0,可以停止创建和导入新的迁移包,但不会隐藏或遗弃已中断操作的恢复入口;设置为 1 可以显式启用。

---

Deep Research Options

Deep Research Options / 深度调研方案对比

This note compares several open-source approaches that are relevant to issue
#311. The goal is to identify
which ideas fit Kun's existing single-runtime architecture.

本文对比与 issue
#311
相关的开源方案,重点判断哪些思路适合 Kun 当前的单运行时架构。

Comparison / 对比

| Approach | Strengths | Trade-offs | Useful idea for Kun |
| --- | --- | --- | --- |
| Agent Reach | Low-friction channel setup, health checks, and ordered fallback backends for web, search, GitHub, video, RSS, and authenticated platforms. | It is a capability and installation layer, not a research planner or report engine. Some channels depend on local login state or external CLIs. | Detect available tools before research, prefer zero-configuration sources, expose channel health, and keep authenticated access local. |
| LangChain Open Deep Research | Configurable research orchestration, multiple search/MCP options, separate summarization/research/report stages, and benchmark support. | Heavier deployment and configuration, multiple model calls, and potentially high token/API cost. Adding it directly would create a second runtime beside Kun. | Add explicit research stages, evidence compression, progress state, and later evaluation without importing the runtime. |
| dzhng/deep-research | Small recursive breadth/depth design, iterative query generation, follow-up directions, concurrency, and a source-backed Markdown report. | Relies on dedicated search/model services and has fewer desktop integration, authentication, and long-running task controls. | Give users visible breadth/depth expectations and iterate from evidence gaps instead of running one broad search. |
| Kun research brief (this change) | Reuses the current Kun agent, workspace files, browser/MCP tools, approval flow, scheduled tasks, and output tools. No second process or provider configuration is required. | It is an orchestration prompt, not yet a dedicated research state machine. Results still depend on the tools currently installed and available. | Provides a low-risk entry point and a stable evidence/output contract while the full workflow is designed. |

Proposed Delivery / 建议分阶段实现

Phase 1: Research brief / 调研任务模板

- Add /research and common aliases to the existing composer command system.
- Ask Kun to inspect available search, browser, MCP, workspace, and paper tools.
- Require iterative breadth/depth planning, an evidence ledger, independent
cross-checks, uncertainty labels, and report-ready Markdown.
- If ongoing monitoring is requested, hand off to the existing scheduled-task
system instead of claiming that monitoring is already active.

Phase 2: Research run state / 专用调研状态

- Persist the research plan, queries, evidence records, unresolved gaps, and
generated artifacts with the thread.
- Stream research progress separately from ordinary assistant prose.
- Allow pause, resume, source inspection, and bounded parallel searches.

Phase 3: Tool health and export / 工具健康与导出

- Add capability checks for configured web, browser, MCP, and authenticated
channels, following Agent Reach's health-check idea without storing cookies
in a new service.
- Add optional evaluation fixtures for citation coverage and claim support.
- Reuse existing document, PDF, slide, media, and scheduled-task tools for
exports and recurring updates.

Current Advantages / 当前优势

- Fits the existing kun serve runtime and approval boundary.
- Can combine local files with web and MCP sources in one thread.
- Does not force one search vendor or require a new API key.
- Keeps credentials in the tools or browser sessions that already own them.

Current Limitations / 当前限制

- No dedicated research graph or persisted evidence schema yet.
- No built-in search backend health dashboard.
- No benchmark-backed quality score yet.
- Parallelism and export quality depend on the active model and installed tools.

---

Graph Mode.En

Kun Graph Mode architecture and operations

Graph Mode is a per-turn Kun orchestration strategy, not a second agent
runtime. direct keeps the existing chat path. In graph, a Lead converts the
request into a lightweight intent that the host compiles into a validated execution graph, Kun schedules constrained
workers in the background, and the Lead supervises material events, reviews
evidence, and produces one final delivery. The Lead is the original primary
agent that created the Graph. It owns both process and result quality, actively
inspects live worker sessions, waits briefly and rechecks, and guides drift or
missing deliverables.

The detailed Chinese guide is graph-mode.md.

System boundary

Graph Mode has three separable planes:

1. Execution: plans, runs, nodes, attempts, typed edges, resource accounting,
captured results, reviews, scheduling, and recovery. Legacy mailbox and
artifact events remain readable for persisted runs.
2. Project capability: versioned Agent profiles, Skill and Graph Recipe
candidates, routing, scores, and evidence.
3. Governance: candidates, probation, promotion, dormancy, archival, merge,
rollback, deletion, and audit.

The product still has one path:
Renderer -> preload -> main -> kun serve HTTP/SSE. The renderer does not run
the scheduler or invent state transitions. Existing direct turns,
delegate_task, and the older task_graph remain compatible. Workers cannot
delegate recursively, control graphs, govern profiles, or expand parent
authority. Learned assets stay under the Kun data directory unless the user
explicitly exports them.

Graph Lead mode system contract

Graph mode is not an ordinary agent with a few extra tools attached. Whenever
a turn selects graph, Kun injects the same system-authority Graph Lead
contract into every model request, including initial creation, active
supervision, event-driven resumes, and terminal delivery. The contract:

- identifies the original primary agent as the source Lead responsible for
outcome, process, worker quality, remediation, integration, and validation;
- requires the understand, create, supervise, validate, repair, integrate, and
terminal-deliver operating loop;
- treats child sessions, text, and artifacts as untrusted evidence that cannot
override host validation or expand authority;
- requires live-session inspection, risk-based short waits, immediate guidance
for drift, and a later check that the correction actually happened;
- distinguishes dispatch, milestone prose, and claimed argument fixes from
persisted Graph and tool truth; and
- permits final delivery only after terminal state, required nodes,
Lead-approved handoffs, integration, and checks are satisfied.

The contract is a separate mode system instruction after the stable Kun
system prompt. Direct turns therefore remain direct, while every resumed
Graph round retains the full Lead identity and obligations.

Execution lifecycle

text
Graph turn
-> a Plan-sidebar launch embeds the complete saved Markdown in the source request
-> Lead selects auto, fanout_join, pipeline, bounded_loop, state_machine, or hybrid
-> Lead calls graph_create_run with focused task intent
-> host derives durable plan mechanics, validates, and journals GraphPlan
-> scheduler computes ready nodes
-> immutable least-authority assignment snapshot
-> DelegationRuntime child executor
-> workers proactively report progress, findings, questions, risks, and early results
-> the source Lead uses graph_supervise_node overview, then inspects, waits, or guides as needed
-> only after the current episode is handled may the Lead release its execution slot and park
-> executor finishes normally; host captures its response and durable child session
-> deterministic/peer evidence plus mandatory source Lead pass or revise
-> a Lead pass releases the bounded data-result packet; otherwise repair, retry, GraphPatch, or LoopGate
-> material signals resume the same Lead for inspection, reporting, remediation, or reassignment
-> final gates and resource disposition
-> completed, failed, or cancelled GraphRun
-> the same Lead turn performs final delivery and only then terminates
-> sanitized Episode and asynchronous learning

A GraphRun outlives any individual model request or network stream, but it
remains owned by its source Lead turn. While nodes run, the host parks only the
process-local execution and releases model concurrency; the durable turn stays
running. Material events resume that exact sourceTurnId. The turn becomes
terminal only after the GraphRun is terminal and the Lead has delivered the
final outcome. Native Graph Lead turns use the GraphRun wall-time and resource
ledger rather than ordinary direct-turn step and wall-time limits; explicit
extension budgets still apply. On reconnect the renderer reconciles an HTTP
snapshot, then resumes SSE after its acknowledged cursor.

GUI Plan files under .kunsdd/plan may be untracked and therefore absent from
isolated Git worktrees. Graph creation also requires graph_create_run before
ordinary read tools. The Plan sidebar consequently embeds the exact saved
Markdown in the source request. The Lead builds from that copy and gives every
executor a self-contained objective instead of assigning a snapshot node to
reread the GUI-only path.

Contracts and state

Contracts live in kun/src/contracts/graph.ts and graph-agents.ts, with
explicit versions. GraphPlanV1 describes topology and policy;
GraphRunV1 is the durable projection; GraphNodeAttemptV1 records an
immutable execution snapshot; GraphEventEnvelopeV1 supplies monotonic
sequence, revision, command, and idempotency metadata. GraphPatchV1,
captured executor results, reviews, legacy messages/artifacts, cleanup, profile
versions, evidence, Episodes, candidates, and audit records are strict schemas.

Edge kinds are:

- control: outcome-gated scheduling;
- data: a named result packet exposed to the successor only after the source
Lead accepts the predecessor;
- message: a legacy persisted edge shape. New executors have no peer mailbox
tools; cross-node information must use a Lead-approved data handoff.

The compiled plan also records the resolved execution strategy. fanout_join
leaves independent siblings ready together; pipeline chains real
accepted-result dependencies; bounded_loop uses an explicit LoopGate;
state_machine represents explicit state transitions while keeping cycles
bounded; and hybrid mixes parallel and serial regions in one run. auto
infers the strategy only from an already explicit task topology. All strategies
compile to the same ready-set scheduler, so one run can fan out, join, proceed
serially, and fan out again.

Run states progress from draft -> validating -> ready -> running, with
pause, supervision, and human-review branches, then
completing -> completed; failed and cancelled are terminal. Nodes move
from pending/blocked through ready, queued, running, submitted, reviewing, and
accepted, with repair, failure, cancellation, skip, and supersession branches.
The reducer rejects an event whose declared source state differs from durable
truth. Accepted history is immutable.

Validation, revisions, and loops

The host validates identity, references, reachability, completion paths, edge
kinds, assignments, scopes, reviews, risk, and every configured non-token resource limit.
Ordinary dependencies must be acyclic. A logical cycle is valid only inside a
strongly connected component with an explicit bounded LoopGate.

GraphPatch uses compare-and-swap with baseRevision, expectedRevision, and
expectedSeq. A stale request has no partial effect. A valid patch is fully
revalidated and committed as one revision while accepted facts remain as
superseded history.

A LoopGate declares a condition source, continuation, exit and exhaustion
targets, and maximum iterations. Every continuation
writes loop_iteration_advanced, resets only the host-computed cycle nodes,
preserves prior attempts, creates attempts at a new iteration, and increments
the run ledger. Unfinished lifecycle states have no outcome: a pending,
blocked, ready, queued, running, submitted, or reviewing condition source
cannot trigger a failed branch or evaluate a LoopGate. Exhaustion can never
create another attempt. Repeated identical
normalized failures pause or escalate.

Scheduling, limits, and cancellation

The host scheduler resolves dependencies and failure propagation, applies
priority and retry delay, and enforces:

- maximum concurrent runs;
- global and per-run concurrent nodes;
- attempts and capped exponential retry;
- run and node wall time;
- revisions, loops, messages, and artifact bytes.

The default GraphRun wall-time limit is seven days. The separate host-enforced
node limit remains 24 hours, and 15 minutes of quiet activity triggers a
supervision inspection without aborting the node.
The create tool may omit the entire budget or any individual mechanical
field. The host fills node/edge, concurrency, attempt, revision, loop,
run/node wall-time, message, artifact, and warningRatio values from current
Graph configuration. A plan supplies a field only when the user or project
intentionally asks for a narrower limit; every explicit value still passes
host-maximum validation.

Token usage is recorded for cost attribution and learning evidence only. Graph
plans, nodes, loops, and immutable worker assignments have no token ceiling,
and the scheduler never pauses, fails, warns, or suppresses work because of
token count.

Runs rotate fairly. Node timeout is enforced with a host AbortSignal. Cancel
first fences the run as terminal, aborts and waits for active workers, discards
late results, settles attempts and nodes, releases leases, safely disposes
worktrees, and records cleanup. Repeated cancellation and cleanup are
idempotent.

Authority and worker context

Every attempt freezes profile/version/origin, model/provider/reasoning,
system instructions, tools, Skills, MCP servers, approval, sandbox, workspace,
read/write scopes, network, and time limits. Effective authority is the
intersection of parent, graph, profile, node, and host policy.

Executors never receive delegation, Graph
creation/control/patch/review/supervision, governance, or any legacy
graph_worker_* tool. They receive ordinary tools authorized by the frozen
assignment plus the single host-owned report_to_parent capability. Their context contains the objective, acceptance criteria, scopes,
bounded repair feedback, prerequisite status, and only the named result
packets that the source Lead has already approved for this node. Control edges
convey readiness only. It excludes the full Lead history, peer mailbox
content, unrelated node results, and Lead/user-private artifacts.

An executor is not told the run, node, attempt, edge, mailbox, or artifact-store
protocol. report_to_parent infers those identities and the sole Lead
recipient from the active child session. Progress is persisted without waking
the Lead; findings, questions, risks, and early results trigger coalesced
supervision. Reports are advisory and cannot accept work or advance the graph.
The executor still finishes with a normal concise response
covering result, changed files, checks, evidence, and risks. Kun automatically
captures that response and retains the canonical child session. Retries receive
bounded host-validation errors and Lead repair feedback, but never an
instruction to publish or submit Graph state.

Worker model policy defaults to inherit, freezing the source Lead provider,
model, and reasoning effort into the attempt. Settings may select a fixed
default for implicit workers. An explicit authorized node/profile assignment
still wins, and any fixed selection outside the frozen parent model authority
fails closed before launch. Configuration changes affect future attempts only.

Review, writes, and completion

Review policies can add deterministic, peer, human, or combined evidence, but
every executable node always requires an explicit review from the owning
source Lead. Kun never synthesizes that Lead vote through a worker, peer
reviewer, or scheduler transition. A peer is a different child instance and
critical risk can additionally require a human. A pass vote cannot override
validation.valid === false; genuine missing evidence, failed checks, and
scope errors must be repaired before the Lead can accept an attempt. Absence
of a worker Graph-tool call is not a validation error.

Supervision is event driven for submission, failure, stall, conflict, resource-limit,
help, recovery, completion, user steering, and material worker reports. Normal progress does not poll a
model. Signals coalesce and resume or steer the original source Lead with
messageSource: graph_runtime; new-format runs do not create replacement
background Lead turns. The Lead has graph_supervise_node: overview pages
across every node with latest attempt, activity, report, and a small transcript
tail; inspect reads a
bounded, sanitized, cursor-based child transcript; wait performs an abortable
1-60 second wait and fresh inspection; and guide durably records
attempt-targeted guidance, acknowledges answered blocking questions, and then
steers the active child turn when possible.
Each continuation inspects durable truth and relevant live sessions, chooses
an activity-appropriate cadence such as a 30-second recheck, guides drift, and
verifies corrections before parking again. When an executor finishes, the
same Lead inspects the captured result and child session and calls
graph_review_node with pass or revise. Until a valid Lead pass exists, the
node stays under supervision and every successor remains blocked.

The Lead pass is also the data handoff. The host projects a bounded packet
under the data edge's semantic name, including the accepted summary, changed
files, checks, evidence, risks, and optional artifact references. Workers
never relay results directly to peers or advance edges themselves.

When required or completion work exhausts automatic attempts, the scheduler
keeps dependants blocked, moves the run to awaiting_supervision, and wakes the
same Lead. It does not skip the rest of the graph or immediately make the run
terminal. The Lead can inspect evidence, guide and retry, rebind, patch, or
cancel honestly. Completion, failure, and cancellation all trigger one final
delivery.

Write nodes declare normalized repository-relative scopes. serialize,
lease, and optional Git worktree policies prevent unsafe overlap.
Worktrees capture staged binary patches including new, deleted, and empty
files, verify every changed path against the immutable lease, and apply safely
with stale/dirty/conflict checks. Unknown user changes require human
disposition. Unaccepted, conflicted, or orphaned worktrees are preserved.

Completion requires accepted required/completion nodes, no active or
review-pending nodes, all required reviews including the source Lead, no
unresolved legacy mailbox blocker, safe write
integration, settled resource accounting, durable cleanup disposition, and one
persisted synthesis with evidence, changed files, checks, risks, and cost.

Project agents, scoring, and learning

Project identity prefers normalized Git remote identity, then Git common-dir,
then canonical workspace root. Profiles have immutable versions, origins
(builtin, user, ephemeral, learned), and lifecycle:

text
candidate -> probation -> trusted -> dormant -> archived -> deleted

Routing first applies hard lifecycle, task, risk, capability, tool, Skill, MCP,
network, sandbox, and scope eligibility. It then recalls a bounded set and
keeps separate task-fit, verified-quality, trust, freshness, efficiency,
confidence, availability, and load dimensions. The aggregate weights are
32/22/14/8/8/10/3/3 percent respectively.

Only eligible && recalled && !selected evidence counts as a missed relevant
opportunity and applies a bounded ranking penalty. Irrelevant conversations do
not decay a specialist. Reaching the configured threshold creates a dormant
version with rollback metadata and an auditable reason.

Terminal/checkpoint runs create redacted bounded Episodes without raw
reasoning, credentials, secrets, full source, or unbounded logs. Durable
idempotent consolidation requires minimum verified episodes across distinct
sessions and classifies reusable material as Agent, Skill, or Graph Recipe
candidates. Evidence is untrusted data. Capability synthesis is least
privilege and cannot grant credentials, risky tools, broad writes, network,
MCP trust, provider authority, or sandbox expansion.

Learning modes are off, suggest, and auto_candidate. Automatic processing
never promotes directly to trusted. Agent candidates enter probation and need
cross-run evidence plus explicit user authority for promotion. Rejection,
rollback, merge, dormancy, archive, and deletion remain reversible/audited.

Storage, recovery, and retention

text
<dataDir>/graphs/<runId>/events.jsonl
<dataDir>/graphs/<runId>/snapshot.json
<dataDir>/graphs/thread-references.json
<dataDir>/graph-resources/write-coordinator.json
<dataDir>/graph-resources/worktrees/
<dataDir>/project-agents/<projectId>/registry.json
<dataDir>/graph-learning/<projectId>/learning.json
<dataDir>/artifacts/

Journals are checksummed append-only JSONL with monotonic sequence. Snapshots
are atomic; replay starts from the latest valid snapshot plus its suffix.
Large event payloads are content-addressed artifacts. Terminal journals compact
after the configured threshold.

Startup validates storage, expires leases, identifies missing worktrees,
reconciles queued/running/waiting attempts with child sessions, turns missing
children into orphaned/interrupted state, completes interrupted pause, and
returns incomplete synthesis to supervision before scheduling resumes.

Retention removes only expired terminal unreferenced runs. It compacts Episode,
job, reference, and audit history. artifactDays deletes only expired objects
that have no GraphRun/Episode reference and whose complete ownership history
shows Graph-only origins. Content shared through deduplication with Web or
ordinary tools, and legacy metadata with unknown owners, is retained
conservatively. Forks copy immutable high-water references without sharing live
execution. Archive pauses; delete fences, cancels, waits, records cleanup, then
removes thread references.

HTTP and UI

All routes use the existing runtime Bearer authentication:

text
POST /v1/graphs/validate
GET|POST /v1/graphs
GET /v1/graphs/diagnostics
GET /v1/graphs/:id
GET /v1/graphs/:id/events
GET /v1/graphs/:id/artifacts/:artifactId?offset=N|start_line=N
POST /v1/graphs/:id/start|pause|resume|cleanup
POST /v1/graphs/:id/cancel|retry|steer|patch|reviews

GET /v1/graph-projects/identity
GET|POST /v1/graph-projects/:projectId/agents...
GET /v1/graph-projects/:projectId/evidence|scores|routing
GET /v1/graph-projects/:projectId/candidates|episodes|jobs|audit
POST /v1/graph-projects/:projectId/candidates/:candidateId/action
POST /v1/graph-projects/:projectId/consolidate|explore

Mutations use command/idempotency keys and applicable expected sequence and
revision. Responses return persisted post-command truth. graph_event also
flows through the existing RuntimeEventRecorder/SSE thread cursor.

When enabled, the composer exposes Direct | Graph. The Graph workbench tab
renders phases, typed edges, loops, revisions, minimap/navigation, state and
resource summaries, phase collapse and an accessible list fallback. Node detail
includes the immutable assignment, permissions, tools/Skills, attempts, child
session, bounded paged artifacts, checks, reviews, writes, worktrees, and
errors. Run controls include rebind and versioned CAS GraphPatch operations in
addition to the ordinary lifecycle controls.

The source Lead remains visibly active while its GraphRun is nonterminal.
Plain-text input submitted in that conversation is persisted as Lead-targeted
Graph steering and wakes the same turn instead of admitting an unrelated turn.

Artifact preview uses only the authenticated, run-scoped bounded-read route:
the server verifies that the reference belongs to the GraphRun and the
renderer retains only the current byte/line page. Every mutation reconciles
persisted server truth. Status is not color-only; keyboard, ARIA, screen
reader, localization, and reduced-motion behavior are supported.

Configuration, rollout, and safe disable

Configuration is under agents.kun.graph, grouped into scheduler, context,
mailbox, supervision, writeIsolation, routing, learning, and
retention. Compatibility defaults are:

text
enabled=false
defaultStrategy=direct
rolloutStage=stable
learning.mode=off
writeIsolation.mode=serialize
writeIsolation.allowWorktrees=false

The product always runs the complete stable Graph capability set. The legacy
rolloutStage field remains readable for downgrade compatibility, but it no
longer gates loops, supervision, or learning. Those capabilities are controlled
only by their explicit settings; promotion still requires evidence and user
authority.

Safe disable sets enabled=false and defaultStrategy=direct. It stops new
creation, automatic supervision, and automatic learning, fences and pauses
nonterminal runs, and waits for active workers to settle. Existing runs and
learned data remain inspectable; do not delete the data directory as a rollback
mechanism.

Missing Graph settings migrate to compatibility defaults, so old workspaces,
threads, and ordinary child sessions are untouched. Before downgrading, disable
Graph and ensure no live worker remains.

For backup, pause/stop Kun and copy graphs, graph-resources,
project-agents, graph-learning, and referenced artifacts together.
Restore their relative layout and let startup recovery reconcile them. Never
restore a snapshot without its journal suffix or a registry without referenced
learning/artifact data.

Incident triage

Start with GET /v1/graphs/diagnostics; it exposes sanitized aggregates, not
paths, prompts, secrets, or raw patches.

- Creation failure: check enablement, orchestration, and plan validation.
- Stuck blocked node: inspect required outcomes, the predecessor's source-Lead
review, approved data-result packets, loop back-edge, and terminal failure.
- Worker does not stop: cancel and inspect worker/lease/worktree cleanup state.
- Write conflict: preserve the worktree and resolve through review/human merge.
- Corrupt journal: preserve the directory and restore a trusted snapshot plus
suffix; never truncate the only copy.
- Orphan after restart: let recovery persist orphan/retry/supervision before
manual retry.
- Running source Lead after restart: lifecycle recovery redelivers unseen
supervision or terminal signals and resumes an interrupted continuation
using the same durable turn identity.
- Bad learned candidate: reject or roll back and inspect provenance plus audit;
do not edit registry JSON manually.

Cleanup is idempotent. Only accepted worktrees are automatically removed.
Unaccepted, conflicted, and orphaned worktrees stay preserved until their
contents are backed up or integrated.

---

Graph Mode

Kun Graph Mode 架构与运维指南

Graph Mode 是 Kun 的一种按回合选择的编排策略,不是第二套 Agent
运行时。普通聊天继续走 direct;选择 graph 后,Lead Agent 先把需求转成
轻量任务意图,宿主把它编译成经过校验的任务图,Kun 在后台调度受限子代理,Lead 只在重要事件发生时监督、
复核和统一交付。这里的 Lead 就是创建 Graph 的原主 Agent;它对过程和结果负责,
会主动查看子代理的实时会话、短暂等待后复查,并在偏离或漏交付时立即指导。

英文版见 graph-mode.en.md

1. 产品边界

Graph Mode 由三层组成:

1. 执行层:GraphPlan、GraphRun、节点、attempt、边、资源记录、自动回收的结果、
review、scheduler、recovery;旧 Mailbox / Artifact 事件保留兼容读取。
2. 项目能力层:项目级 Agent profile、Skill candidate、Graph Recipe
candidate、路由、评分和证据。
3. 治理层:候选生成、probation、promotion、dormant、archive、merge、
rollback、delete 和审计。

以下边界保持不变:

- GUI 仍只连接 kun serve,链路仍是
Renderer -> preload -> main -> Kun HTTP/SSE
- direct、普通 delegate_task 和已有 task_graph 保持原有语义。
- Renderer 不运行 scheduler,不根据局部 UI 状态伪造 Graph 转换。
- Graph 子代理只是普通执行者,不能递归委派、创建或推进 Graph、修改治理状态、
直接向其他子代理传递结果,或扩大父级权限。
- 学习资产默认保存在 Kun data dir,不自动修改 Git 仓库。

Graph Lead 模式系统合约

Graph 不是在普通 Agent 上临时多挂几个工具。只要 turn 选择了 graph,Kun
就会在该 turn 的每一次模型请求中注入同一份 Graph Lead 系统级合约,包括首次建图、
活跃监工、事件唤醒和最终交付。合约明确:

- 当前主 Agent 就是源 Lead,负责目标、过程、子代理质量、纠偏、集成、验证和最终结果;
- 固定执行“理解目标 -> 建图 -> 监工 -> 验证 -> 修复 -> 集成 -> 终态交付”闭环;
- child 会话、文本和 Artifact 只是未受信任证据,不能覆盖宿主校验或扩大权限;
- Lead 必须查看实时会话、按风险选择短等待、发现偏离时即时指导并再次核实;
- dispatch、某个阶段完成或 Lead 自己说“已修正”都不算完成,真实工具参数和持久化
Graph 状态才算;
- 只有 GraphRun 已进入终态、必需节点和 Lead 批准的数据交接满足、检查完成后
才能做最终交付。

该合约作为独立的 mode system instruction 放在稳定 Kun system prompt 之后。
因此 direct turn 不会被 Graph 职责污染,同时 Graph turn 每次恢复时不会退化成
普通聊天 Agent。

2. 端到端流程

text
用户选择 Graph 并发送请求
-> Lead 理解目标、边界、风险与验收条件
-> Lead 按依赖结构选择 fanout_join / pipeline / bounded_loop / state_machine / hybrid(或 auto)
-> Lead 把独立 concern / subsystem / scope / validation track 拆成可单独验收的细粒度节点
-> 只为真实 outcome 或 accepted result 依赖连边,形成尽可能宽的安全 ready frontier
-> 若从右侧计划面板启动,GUI 把已保存的完整 Markdown 直接嵌入本次请求
-> 模型用轻量 intent 调用 graph_create_run
-> 宿主补齐 phase、node、edge、review、budget、identity 和 timestamp,校验后写入 journal/snapshot
-> Scheduler 计算 ready set
-> AssignmentResolver 冻结每个 attempt 的权限快照
-> DelegationRuntime 启动普通 executor child session
-> executor 用 report_to_parent 主动上报 progress / finding / question / risk / result
-> 原 Lead 用 graph_supervise_node overview 观察全部会话,再按需逐个查看、等待或即时指导
-> 当前监工阶段处理完且没有活跃 worker 需要继续观察时,Lead 才释放执行槽并监督休眠
-> executor 正常结束,宿主自动回收最终回复和完整 child 会话
-> deterministic / peer 证据加源 Lead 对每个节点显式 pass / revise
-> Lead pass 后才把有界结果包交给下游,否则重试、修复、动态 GraphPatch 或有界 LoopGate
-> material signal 唤醒同一个 Lead turn 检查、汇报、修复、重试、改图或换人
-> 完成条件、阻塞消息、活跃 worker、写入集成和资源清理全部关闭
-> GraphRun 进入 completed / failed / cancelled
-> 同一个 Lead turn 最后一次醒来,生成统一交付并结束
-> 异步生成已脱敏 Episode,按策略做项目能力沉淀

GraphRun 独立于单次模型请求和网络流,但不脱离创建它的源 Lead turn。等待节点期间,
宿主只暂停该 turn 的进程内执行并释放模型并发槽,持久化 turn 仍是 running
重要事件到来时恢复同一个 sourceTurnId。只有 GraphRun 已进入终态且 Lead 完成最后
交付后,源 turn 才结束。原生 Graph Lead 不受普通 direct turn 的模型步数和墙钟上限
终止,而由 GraphRun 默认七天的墙钟和资源账本治理;显式 extension budget 仍然生效。
GUI 重连时先读取 HTTP snapshot,再从已确认的 sequence 继续 SSE replay。

从计划面板启动时,.kunsdd/plan/*.md 可能是未纳入 Git 的 GUI 文件,隔离 worktree
不会天然包含它。Graph 创建回合又必须先调用 graph_create_run,不能先用读取工具。
因此请求会携带保存后的完整计划正文;Lead 必须直接据此建图,并把每个 executor 的
目标写成自包含任务,不得创建一个只负责在 worktree 中重读该计划路径的 snapshot 节点。

3. 核心契约

所有 Graph 契约位于 kun/src/contracts/graph.ts
kun/src/contracts/graph-agents.ts,均带显式版本。

- GraphPlanV1:phase、逻辑 node、typed edge、非 Token 资源限制、completion nodes、
revision、创建信息,以及已解析的执行策略。
- GraphRunV1:当前 revision、run/node/attempt 投影、review、message、
artifact、cleanup、资源 ledger 和最终 summary。
- GraphNodeAttemptV1:不可变 assignment snapshot、attempt number、
loop iteration、child session、result、usage 和失败分类。
- GraphEventEnvelopeV1:run/thread、单调 graphSeq、revision、checksum
保护的 domain event、command 和 idempotency key。
- GraphPatchV1:base revision、requester、reason 和有限操作集合。
- GraphWorkerResultV1:summary、Artifact refs、changed files、checks、
evidence、risks 和显式消息。
- GraphAgentProfileVersionV1:项目 Agent 的不可变版本、能力、生命周期和
provenance。

边类型:

- control:按前驱 outcome 控制调度。
- data:命名的结果交接通道;只有源 Lead 验收前驱后,下游才会收到结果包。
- message:旧持久化格式仍可读取;新 executor 没有点对点 Mailbox 工具,
跨节点信息统一经过 Lead 批准的 data handoff。

Graph 事件是唯一运行时真相。模型文本、worker 自称完成、GUI 本地操作都不能直接
把节点置为 accepted。

4. 状态机

GraphRun 主状态:

text
draft -> validating -> ready -> running
running -> pausing -> paused -> running
running -> awaiting_supervision / awaiting_human -> running
running -> completing -> completed
任一允许的非终态 -> failed / cancelled

Node 主状态:

text
pending / blocked -> ready -> queued -> running -> submitted
submitted -> reviewing -> accepted
submitted / reviewing -> repair_required -> ready
可执行状态 -> failed / cancelled / superseded / skipped

Attempt 主状态:

text
queued -> running -> waiting -> submitted -> reviewing -> accepted
queued/running/waiting -> interrupted / cancelled / orphaned
running/waiting/reviewing -> failed / repair_required

Reducer 会校验声明的 from 与持久化当前状态一致,并拒绝非法跳转。
accepted attempt 永不被 revision 重写;新需求只能创建 superseding revision/node。

5. GraphPlan 校验、动态 revision 与循环

宿主在创建和每次 patch 时检查:

- ID 唯一性、edge 引用、phase、entry、reachability 和 completion path。
- node/edge/attempt/revision/并发/token/time/message/Artifact 硬限制。
- control/data/message edge 的合法性。
- assignment、read/write scope、review 和风险策略。
- 普通依赖必须是 DAG。
- 逻辑环必须位于包含显式 LoopGate 的强连通分量内。

GraphPatch 使用 compare-and-swap:

- 请求必须携带当前 baseRevisionexpectedRevisionexpectedSeq
- stale patch 返回 conflict,不产生部分修改。
- 已完成事实保留,replace/remove 通过 supersession 表达。
- patch 后整张图重新校验,通过后一次性写入 plan_revised

LoopGate 必须声明 condition source、continuation target、exit target、
exhaustion target 和最大 iteration。每次继续都会:

1. 写入 loop_iteration_advanced
2. 只重置宿主计算出的 cycle nodes。
3. 保留旧 attempt history,并为新 attempt 写入新的 iteration。
4. 增加全局 loop ledger。

pendingblockedreadyqueuedrunningsubmittedreviewing
都不是失败 outcome。condition source 尚未形成真实结果时,LoopGate 不求值,
依赖 failed 的 control edge 也保持 blocked;不能提前启动 repair、quality-gate
或 final。

达到 gate 或 run 的非 Token 资源上限时只能走 exhaustion path,不允许再创建 attempt。
重复相同的 normalized failure 达到阈值时不会自动进入持久化 paused
自动尝试仍受预算约束,run 保持在可监督状态,由 source Lead 改变策略(修补、
替换或重新绑定节点)或作出明确的终止决定;只有用户或授权 Lead 的显式控制
才使用 paused

6. Scheduler 与资源限制

Scheduler 的派发、资源和状态记录由宿主驱动,但每个节点是否验收和向下游交接
必须由源 Lead 明确决定。Scheduler 负责:

- 解析 control/data dependency 和失败传播。
- 按 priority、node id 和 retry-not-before 选择 ready nodes。
- 全局 maxConcurrentNodes、单 run maxConcurrentNodesPerRun
maxConcurrentRuns 准入。
- 跨 GraphRun 轮转,避免大型图长期占用全部容量。
- attempt、run wall time、node wall time、revision、loop、
Artifact 和 message 限制。
- capped exponential retry backoff 和失败分类。
- 不可用 DelegationRuntime 时进入 awaiting_supervision,等待恢复或 Lead 改变策略,
不自动写成持久化 paused

默认 GraphRun 总 wall time 为 7 天;单个 node 的宿主硬超时仍为 24 小时,
安静运行 15 分钟只会触发监督检查,不会单独中止节点。这三个限制彼此独立。
模型创建 Graph 时可以省略整个 budget 或其中任意机械限制字段。宿主会从当前
Graph 配置补齐 node/edge、并发、attempt、revision、loop、run/node wall time、
message、Artifact 和 warningRatio。只有用户或项目明确要求更窄的限制时,计划
才显式提供对应字段;显式值仍必须通过宿主上限校验。

Token 只记录实际用量,用于成本归因和学习证据。GraphPlan、节点、循环和冻结后的
worker assignment 都没有 Token 上限;Scheduler 不会因 Token 数量告警、暂停、
失败或停止派发。

Graph 的并发上限只有在计划暴露多个 ready node 时才有意义。Lead 不能把整个多 concern
需求塞进一个首节点,再用普通依赖把其余工作全部串在后面。对非平凡任务,节点应以
“一个可独立验收的交付”为粒度,并按互不依赖的 concern、subsystem、repository scope
或 validation track 扇出;同属一个 phase 或最后需要统一集成,不构成提前串行的理由。
只有 successor 确实需要 predecessor outcome 时才使用 control edge,确实消费其已验收
结果包时才使用 data edge。工作天然串行时仍保留真实依赖,不为了并发伪造独立性。

graph_create_run 成功结果会返回 executionShape,包含
已解析的 strategyinitialExecutableNodeIdsinitialExecutableNodeCount
effectivePerRunConcurrencymaximumImmediateDispatchCount。非平凡图只有一个
立即可派发节点时还会附带 informational diagnostic;它不否决已创建的 GraphRun,
用于让 Lead 和排障人员直接看出当前计划没有吃满并发能力。

执行策略

Graph 不是只有一种固定拓扑。Lead 根据任务选择策略,宿主再把轻量 intent 编译成同一套
可持久化 GraphPlan:

- fanout_join:多个互不依赖的节点并发执行,由后续集成或 Lead 汇总。
- pipeline:后一步确实消费前一步已验收结果;省略依赖时宿主按声明顺序串联。
- bounded_loop:显式 LoopGate 驱动的有限修复或质量循环。
- state_machine:用显式状态节点和转换依赖表达阶段切换;回环仍必须受 LoopGate 限制。
- hybrid:同一张图中同时包含并发分支、串行交接和最终集成。
- auto:只在 task 依赖已经足够明确时由宿主判断上述策略。

Scheduler 不需要为每种策略复制一套实现;所有模式最终都落实为 ready set 和显式依赖,
所以同一 GraphRun 可以先并发、再串行、之后重新扇出。

每个 node timeout 由宿主 AbortController 强制执行。用户 cancel 会先写入
terminal fence,再中止并等待活跃 worker;迟到结果不会进入已取消 GraphRun。

7. Assignment 和安全边界

每个 attempt 在派发前冻结 GraphAssignmentSnapshotV1

- profile id/version/origin/name 和 system prompt。
- model、provider、reasoning effort。
- allowed/blocked tools、Skills 和 MCP servers。
- approval policy、sandbox mode、workspace root。
- read/write scope、network 和 time limit。

有效权限始终是父 turn、Graph policy、profile、node 和宿主硬限制的交集。
任何子层只能收窄,不能扩张。worker 还会被强制屏蔽:

- delegate_taskgenerate_subagent
- graph_create_rungraph_patch_rungraph_control_run
graph_review_nodegraph_supervise_node
- 项目 Agent/候选治理工具。
- 父级未授权的网络、MCP、Skill、写路径和 provider。

Graph worker 模型策略默认是 inherit,即继承创建该 run 的源 Lead provider、model
和 reasoning effort。设置中也可以选择 fixed 的默认 worker provider/model;
该值只用于没有显式 assignment 且项目路由没有选中专用 profile 的隐式 executor。
显式节点/profile 优先,所有固定值仍必须位于冻结的父级模型权限集合内,否则 attempt
在启动前失败关闭。配置变化只影响后续 attempt,不改写历史 assignment snapshot。

Executor context 只包含 task objective、验收条件、授权 scope、上一 attempt 的
有限校验/修复反馈、前置状态,以及源 Lead 已明确批准给当前节点的数据结果包。
control edge 只传 ready 状态,不附带前驱结果;Lead/user private Artifact、点对点
Mailbox、无关 node result 和完整父对话都不会继承。宿主安全边界放在 context
开头,即使尾部被截断也保留。

子代理不需要理解 runId、nodeId、attemptId、edge、Mailbox 或 ArtifactStore。
它只使用 assignment 授权的普通工具完成任务,并可调用 report_to_parent 主动上报:
普通进度只持久化;finding、question、risk 和 result 会经过监督合并窗口唤醒 Lead。
run、node、attempt、sender 和唯一 Lead recipient 全由宿主从 child session 推断,
模型不能伪造这些字段。报告只是组织信号,不能验收节点、推进依赖或完成 GraphRun。
子代理最后仍用正常回复说明结果、改动文件、实际检查、证据和风险。宿主自动回收回复
并保留完整 child 会话给 Lead 查看。
重试只补充有界的宿主校验错误和 Lead 修复要求,不再要求子代理调用任何
graph_worker_* 工具。

8. Lead 工具与执行者边界

新 Graph attempt 只额外暴露宿主管理的 report_to_parent,不暴露
graph_worker_progress
graph_worker_publish_artifactgraph_worker_message
graph_worker_receive_messagesgraph_worker_submit_result。这些旧事件仅用于
兼容读取历史 GraphRun,不参与新流程。

Lead 工具:

- graph_create_run
- graph_control_run
- graph_patch_run
- graph_review_node:模型只填写 runIdnodeIdoutcomesummary 和可选
evidence / artifact refs / repair instructions / explicit attempt id。Kun 从持久化状态
解析最新 eligible attempt,并生成 review id、Lead provenance、timestamp、当前
revision 和 sequence;模型不再手写完整 GraphReviewResultV1
- graph_supervise_nodeinspect 读取有界、脱敏、可续游标的 child 会话;
overview 按节点游标返回全 run 的状态、最新 attempt、实时 activity、最新主动报告
和少量会话尾部;完整内容仍通过 inspect 分页读取;
wait 选择 1–60 秒可中止等待后重新检查;guide 先持久化 attempt 定向指导,
再尽可能即时 steer 正在运行的 child turn,并确认该 attempt 已被回复的阻塞问题。

所有 Graph 流程动作都留在 Lead 和宿主:Lead 查看 executor 会话、即时指导、
验收或要求返工;宿主记录状态、执行校验,并在 Lead 通过后把有界结果投影给计划中
授权的 data-edge 下游。executor 之间不直接传消息,也不自行推进节点。

9. Review、监督和完成条件

Review 可以增加 deterministic、peer、human 等证据,但每个可执行节点都必须由
创建 Graph 的源 Lead 显式验收。宿主不会让 worker、peer reviewer 或 Scheduler
替 Lead 生成这张票。critical risk 还可强制 human review;worker 自评不能绕过。
宿主 validation.valid === false 时 Lead pass 会被拒绝,review 只能收紧校验,
不能覆盖真实的缺证据、检查失败或 scope 错误;但“没有调用 worker Graph 工具”
不再是错误。

GraphSupervisor 只响应 material signals:

- submitted、failure、stall、conflict、resource-limit、help、recovery、
completion、user steering、worker report。

普通 progress heartbeat 只更新图,不触发模型轮询。相同信号按窗口合并;
宿主使用 messageSource: graph_runtime 恢复或 steer 原始 Lead turn,不再为新格式
GraphRun 创建独立的后台 Lead turn。Lead 每次创建或被唤醒后先读取持久化 Graph
truth,再主动查看有关 worker 的 child 会话;健康时可自行决定例如等待 30 秒后复查,
发现跑偏、漏证据或方案错误时则即时 guide 并验证纠正结果。worker 正常结束后,
Lead 必须查看回收的结果和会话,再对该节点调用 graph_review_node 做 pass/revise。
没有有效 Lead pass,节点和全部下游保持 blocked。Lead 向用户回报关键进展,并按
证据执行 retry、repair、GraphPatch 或 rebind;只有当前监工阶段已经处理完,且没有
活跃 worker 需要继续观察时,才再次进入监督休眠。

Lead pass 同时就是数据交接决定。宿主会用 data edge 的语义名称,把前驱的 summary、
changed files、checks、evidence、risks 和可选 Artifact 引用组成有界结果包,再注入
被授权的下游 executor。子代理不负责回传协议、同伴通信或流程传递。

必需节点或 completion node 耗尽自动 attempt 时,Scheduler 会保留下游为 blocked,
把 run 置为 awaiting_supervision 并唤醒原 Lead,不再先把下游全部 skipped 或直接
结束。Lead 查看会话和校验证据后,可指导并 retry、rebind、patch,或明确 cancel。
完成、失败和取消都会触发最后一次唤醒和交付。

GraphRun 只有同时满足以下条件才进入 completed:

- required 和 completion nodes 已 accepted/superseded。
- 不存在 pending/ready/queued/running/submitted/reviewing node。
- 所有 required review(包括每个节点的源 Lead review)已通过。
- 旧 GraphRun 中遗留的 blocking Mailbox 已解决。
- 写入已安全集成或有明确的人类处置。
- 资源记录已收敛。
- final synthesis 已持久化。
- lease/worktree/journal cleanup disposition 已持久化。

最终 summary 包含统一答案、evidence refs、changed files、checks、风险、
token/time 和 revision 信息,而不是简单拼接 worker 文本。

10. 写入隔离与冲突处理

每个 node 必须声明 repository-relative read/write scopes。路径遍历、绝对路径和
超出 scope 的变更会被拒绝。

三种策略:

- serialize:写节点串行。
- lease:不重叠 scope 可并发,重叠 scope 等待。
- worktree:配置允许且 workspace 为 Git repository 时,为并发写节点创建
隔离 worktree。

Worktree capture 会 stage 全部新增、修改、删除和空文件,再生成相对 base
revision 的 binary patch。集成前检查 changed files 均在冻结 scope 内,并执行
stale/dirty/conflict 检查。安全 patch 幂等 apply;不重叠的用户 dirty changes 会原样
保留且不改变其暂存状态,只有与 Graph patch 路径重叠的未知 dirty changes 或冲突才进入
needs-human。未 accepted、conflict、orphaned 或唯一含未合并变更的 worktree 永不自动删除。

11. 项目 Agent、路由与评级

项目身份按以下顺序稳定解析:

1. 规范化 Git remote identity hash。
2. Git common dir。
3. canonical workspace root。

因此同一 repository 的多个 worktree 可共享项目 Registry。资产默认保存在 data
dir,运行时 attempt 始终引用不可变 profile version。

Profile origin:builtin | user | ephemeral | learned
生命周期:

text
candidate -> probation -> trusted -> dormant -> archived -> deleted

恢复、promotion、demotion、merge、archive 和 delete 会创建新版本或 tombstone,
不修改历史 attempt snapshot。

路由先执行硬过滤:

- lifecycle、task type、risk、model capability。
- tools、Skills、MCP、network、sandbox。
- read/write scope 和父级 authority。

之后只保留有界 recall 集,再按以下独立维度评分:

- task fit 32%
- verified quality 22%
- trust 14%
- freshness 8%
- efficiency 8%
- confidence/sample support 10%
- availability 3%
- current load 3%

每次“相关但未选中”的机会最多产生一条 missed_opportunity evidence,并施加
有上限的排序 penalty。只有 eligible && recalled && !selected 才计数;
无关、未召回或无权限的会话不会衰减。达到
dormantMissedOpportunityThreshold 后 trusted profile 自动生成 dormant
版本,并记录原因、before/after hash、rollback version 和 system audit。

12. 异步自进化与治理

terminal 或显式 checkpoint GraphRun 会生成脱敏 Episode。Episode 只保存:

- task/graph fingerprint、图形摘要、assignment 摘要。
- accepted/failed outcome、review/failure 摘要。
- token、time、attempt 和 Artifact reference。
- 用户干预的有限摘要。

它不保存 raw reasoning、credential、secret-like value、无限日志、完整源文件或
未受信任 prompt。文本经过 secret pattern redaction 和长度限制。

Learning mode:

- off:不生成资产。
- suggest:保留建议,用户决定是否进入候选。
- auto_candidate:可异步创建可逆 candidate,但不能直接 trusted。

Consolidator 按时间、run count、evidence threshold 或手动请求创建 durable、
idempotent job。只有达到最少 verified episodes 和 distinct sessions 的 cluster
才生成:

- agent_profile:稳定职责和输出边界。
- skill:跨角色复用的方法。
- graph_recipe:多节点协作/依赖 motif。

候选把 Episode 当不可信数据,能力取观测交集并默认 least privilege。自动流程不授予
credential、高风险 tool、广泛写 scope、网络、MCP trust、provider 或 sandbox
扩权。Agent candidate 先进入 probation;达到跨 run 正向证据门槛后,仍需显式
user authority 才能 promotion。reject、rollback、merge、dormant、archive 和
delete 均有审计。

13. 持久化布局、恢复与保留

<dataDir> 为根:

text
graphs/<runId>/events.jsonl
graphs/<runId>/snapshot.json
graphs/thread-references.json
graph-resources/write-coordinator.json
graph-resources/worktrees/
project-agents/<projectId>/registry.json
graph-learning/<projectId>/learning.json
artifacts/

Journal 是带 checksum 的 append-only JSONL;sequence 单调递增。snapshot 原子写入,
启动时从最新有效 snapshot 加 journal suffix 重放。终态日志达到阈值后保留 snapshot
和最近 suffix。大 event payload 外置到 content-addressed ArtifactStore。

启动恢复顺序:

1. 校验 journal/snapshot,记录 corrupt/missing/invalid diagnostics。
2. 过期 lease,标记缺失 worktree。
3. 对 queued/running/waiting attempt 与 child session 对账。
4. 缺失 child 变为 orphaned/interrupted,并按剩余 attempt 次数重试或升级。
5. pausing 按持久化的 pendingControlIntent 收敛:pause 进入 paused
cancel 或旧 journal 的 cancellation dispatch fence 完成幂等 cleanup 后进入
cancelled。缺 final summary 的 completing 回到 supervision。
6. 写入 cleanup 和 recovery signal,再启动 scheduler。

Retention 只删除超过期限、terminal 且未被 thread reference 引用的 GraphRun。
Episode/job/audit 按各自策略压缩。artifactDays 只清理过期、无 GraphRun/Episode
引用且 ownership history 完整并确认仅属于 Graph 的对象;内容曾被 Web、普通工具
等非 Graph origin 去重共享,或旧 metadata 无法证明完整 ownership 时,保守保留。

Fork 复制不可变 Graph reference/high-water snapshot,不共享 live execution。
Archive 会暂停 active run。Delete 会 fence 新派发、取消并等待 worker、写 terminal
和 cleanup,再删除 thread 引用。

14. HTTP、SSE 与工具接口

所有 /v1 route 使用现有 runtime Bearer auth。主要 GraphRun route:

text
POST /v1/graphs/validate
GET /v1/graphs/diagnostics
GET /v1/graphs
POST /v1/graphs
GET /v1/graphs/:id
GET /v1/graphs/:id/events?since_seq=N
GET /v1/graphs/:id/artifacts/:artifactId?offset=N|start_line=N
POST /v1/graphs/:id/start|pause|resume|cleanup
POST /v1/graphs/:id/cancel
POST /v1/graphs/:id/retry
POST /v1/graphs/:id/steer
POST /v1/graphs/:id/patch
POST /v1/graphs/:id/reviews

项目能力 route:

text
GET  /v1/graph-projects/identity?workspace=...
GET /v1/graph-projects/:projectId/agents
POST /v1/graph-projects/:projectId/agents/route
POST /v1/graph-projects/:projectId/agents/import
POST /v1/graph-projects/:projectId/agents/merge
GET /v1/graph-projects/:projectId/agents/:profileId/export
POST /v1/graph-projects/:projectId/agents/:profileId/lifecycle
GET /v1/graph-projects/:projectId/evidence|scores|routing
GET /v1/graph-projects/:projectId/candidates|episodes|jobs|audit
POST /v1/graph-projects/:projectId/candidates/:candidateId/action
POST /v1/graph-projects/:projectId/consolidate
POST /v1/graph-projects/:projectId/explore

Mutation 请求使用 portable commandIdidempotencyKey 和适用的
expectedSeq/expectedRevision。成功响应返回持久化后的 GraphRun,不返回乐观
预测状态。graph_event 同时写入 RuntimeEventRecorder;SSE 重连使用现有 thread
event cursor,Graph 专用 events route 可按 graphSeq 补齐。

15. Workbench 与可访问性

Composer 在 Graph 开启时显示 Direct | Graph,选择随 turn 请求发送。
源 Lead turn 在 GraphRun 非终态期间持续显示为 active。此时同一会话里提交的纯文本
不会创建另一个 turn,而是作为该 GraphRun 的 Lead steering 持久化,并唤醒原 Lead;
右侧 Graph tab 提供:

- phase 分组、typed edges、LoopGate/revision 标记。
- pan/zoom、minimap、progressive collapse 和大图 list fallback。
- 状态计数、资源使用、critical path、attempt 和当前 Agent。
- node objective、assignment version、tools/Skills、attempt history、
child session、messages、分页 Artifact 预览、checks、review、writes、worktree 和 error。
- steer、pause/resume、cancel、retry、review、rebind、带 CAS 的通用 GraphPatch、
candidate governance 和 cleanup。

Artifact 预览只通过带 Bearer auth 的 run-scoped bounded-read route 读取;服务端先
确认 Artifact reference 属于该 GraphRun,再按 byte/line cursor 分页,renderer
只保留当前页。所有 mutation 完成后使用 Kun 返回的持久化 truth,不做乐观拓扑
变更。状态不只靠颜色,节点和控件有 ARIA label、键盘焦点和 screen-reader
summary;系统启用 reduced motion 时关闭动态边。英文和中文 label 均由 locale
资源提供。

16. 配置与发布

配置位于 agents.kun.graph。默认:

- enabled: false
- defaultStrategy: direct
- rolloutStage: stable
- learning.mode: off
- writeIsolation.mode: serialize
- allowWorktrees: false

其余分组为 schedulercontextmailboxsupervision
writeIsolationroutinglearningretention。Settings UI 会校验
Graph disabled 时不能把 default strategy 设为 graph,per-run 并发不能高于
全局并发,learning off 时不能启用自动探索。

产品始终按完整的稳定版 Graph 能力运行。旧的 rolloutStage 字段仅为降级兼容保留,
不再限制 LoopGate、自动监督或学习;这些能力只由各自的显式设置控制。候选 Agent 的
promotion 仍需要证据和用户授权。

紧急关闭只需设置 enabled: falsedefaultStrategy: direct。这会停止新 Graph
创建、自动监督和自动学习,fence 并暂停非终态 run、等待 active worker 收敛;
已有 journal、snapshot、Agent 和 Episode 保持可读。不要通过删除 data dir 做回滚。

17. 迁移、降级、备份与恢复

旧 settings 缺少 graph 时会补兼容默认值,不创建 GraphRun。旧 thread、普通
child session 和 task DAG 不迁移、不重写。新版 settings 写回时只保留
agents.kun.graph 的已知规范字段。

备份前:

1. 暂停 active GraphRuns 或退出 Kun。
2. 复制 graphs/graph-resources/project-agents/
graph-learning/ 和被引用的 artifacts/
3. 保留文件权限和目录相对关系。

恢复时先还原到同一 data dir,再启动 Kun;RecoveryService 会重放和对账。不要只
恢复 snapshot.json 而遗漏 journal suffix,也不要只恢复 registry 而遗漏它引用的
Episode/Artifact。

降级到不识别 Graph 的版本前先关闭 Graph。旧版本应忽略新增 settings 字段,但不会
维护 active GraphRun,因此必须确保没有 live worker。重新升级后 journal 仍可恢复。

18. 事故排查与 orphan cleanup

先调用 GET /v1/graphs/diagnostics,再检查对应 run snapshot/events。诊断输出只含
聚合计数和已脱敏错误,不返回 workspace path、prompt、secret 或原始 patch。

常见情况:

- Graph 不创建:检查 enabled、turn 的 orchestration、rollout settings 和
graph_create_run validation error。readScopes/writeScopes 必须是仓库相对
路径;机械 budget 字段可省略并交给宿主补齐,不应靠模型复写配置默认值。
- 计划面板 Graph 的首节点报计划文件 ENOENT:确认构建请求中包含
<implementation_plan>;节点目标应自包含,不应在隔离 worktree 重读
.kunsdd/plan
- Node 永久 blocked:检查 required outcome、data Artifact、LoopGate back edge
和前驱 terminal failure。
- Worker 不退出:cancel Graph;确认 child 收到 abort;查看 cleanup 中 worker/
lease/worktree 是否 orphaned/preserved。
- Write conflict:不要手动删除 worktree;查看 changedFiles、base revision 和
integration reason,由人类合并或保留。
- Journal corruption:保留原目录,使用 diagnostics 定位首个坏 record;从可信备份
恢复 snapshot+journal,不截断唯一副本。
- 重启后 attempt orphaned:RecoveryService 会写入 orphaned 和 retry/supervision;
确认没有同一 scope 的 live lease 后再手动 retry。
- 重启后源 Lead 仍为 running:启动恢复会读取其 lifecycle cursor,重投未交付的
supervision/terminal 信号,或恢复崩溃时正在执行的同一个 turn,不创建替代 Lead。
- Learning 候选异常:reject/rollback candidate;检查 provenance Episode 和 audit;
不要直接编辑 registry JSON。

Cleanup 是幂等操作。accepted worktree 可清理;unaccepted/conflict/orphaned worktree
只会标为 preserved。确认内容已备份或合并后,才可使用正常治理/人工文件操作处理。

19. 验证清单

自动检查:

bash
npm run build:kun
npm run typecheck
npm run test
npm run lint
npm run build

发布前手动冒烟:

1. Direct turn 不创建 GraphRun,普通 delegation 不变。
2. Graph turn 创建 attached run;源 Lead 保持 running、休眠时释放执行槽,GUI 收到
snapshot 和 SSE。
3. 独立节点并行,依赖节点等待;多 run 公平并发。
4. pause/resume/cancel/retry/steer/review/cleanup 均返回 durable truth。
5. cancel 中止 worker,重启可恢复 orphan,重复命令不重复副作用。
6. LoopGate 在上限退出,GraphPatch stale revision 被拒绝。
7. lease/worktree 冲突不覆盖用户改动,未合并 worktree 被 preserved。
8. final review、blocking message、cleanup 未关闭时不能完成;completed、failed、
cancelled 都由原 Lead turn 做最终交付后才结束。
9. 多会话 Episode 达阈值后生成候选,promotion 需要用户,回滚和审计可见。
10. 关闭 Graph 后 Direct 可用,旧 Graph/Agent/Episode 仍可查看。

---