Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ The Superpowers plugin is disabled for this repository. Do not invoke or follow
- **回复中的三种发送**(对标 Claude 桌面版 next / later / now):Enter = 软插话 `/turn/steer`,先画在对话末尾(`SteeredMessages`,排队状态 `lane: 'steer'`),气泡上「立即发送」走 `/turn/steer/send-now`(服务端把这条未读插话提成优先回合并打断,**不要**改成再调 `/turn/interrupt` 重发,会发两遍)、撤回走 `/turn/steer/withdraw` 且**消息退回输入框不删**(已有内容时放前面换行,`steerWithdrawal.js`);回合结束仍未读的插话转回排队卡片;Ctrl+Enter = 排到回合结束(原排队卡片),Ctrl+Alt+Enter = `fork` 带 `whole: true` 复制整个会话后在新会话发送。见 [docs/daemon-api.md](docs/daemon-api.md)。
- **侧边对话只放只读工具**(`side_chat.cpp` 小工具循环):白名单 `file_read` / `grep` / `glob` / `lsp` 与会话策略取交集,MCP 一律不放,**不要**改成按 `is_read_only` 筛;凡要弹确认的一律拒绝;在 `MtimeTracker::DetachedReadScope` 里执行;Web 侧边多一个 `send_to_main_session`,用户确认之前绝不送达,送达走 `send_side_chat_message`(有可插话回合就 steer,否则普通 send)。归档:侧边对话只放只读工具。
- **后台任务**:会话级登记表(`tool/background_tasks`,AgentLoop 持有)管后台命令与后台子代理,结束时经邮箱通知会话(在跑就模型边界插入,空闲就开新回合);bash 前台超时**转后台不杀**、`run_in_background` / `monitor`、开头长 sleep 拒绝;用户插话 / 「转到后台」/ TUI Ctrl+B 让前台工作让路;通知落盘为 user + `metadata.background_task`,按内部上下文处理(不计回合、不当标题)。Web 对话里任务状态挂在发起它的工具行上:工具行照常折在「已处理」里,任务没结束时包着它的每层折叠条只带炫光(不转圈、文案不变),会话空闲时末尾**不画**「N 个后台任务正在运行」(那行转圈让用户以为回合没结束,别加回去)。全文 [docs/background-tasks.md](docs/background-tasks.md)。
- **子代理 / 蜂群**:`spawn_subagent` / `wait_subagent` 双端注册,子会话是普通 SessionRegistry 会话(父 cwd + 权限模式),不能再派子代理;子会话从会话列表隐藏、始终跟随主会话(无单独归档 / 删除,主会话永久删除才级联);蜂群 `off | star | mesh` 两套工具互斥,邮件进 AgentLoop 邮箱,信封落盘为 user 角色 + `metadata.inter_agent`,子 agent 完成不唤醒空闲父 agent,Web 芯片只在与服务端模式不同时才随消息提交 `swarm_mode`。Web 里每个子代理前面一个 16px 头像(绑定专家用专家的,否则按子会话 id 生成,对话 / 摘要面板 / 后台任务页签是同一张脸),右上角的状态图标与桌面像素办公室共用 `office_characters.js` 的 `miniSprite`(运行中三个点 + 点头,完成是星,中止是红色感叹号),只有运行中的头像订阅 `avatarStatus.js` 那只共用的钟;后台任务页签里子代理是单行无边框的行(标题、token 数、时间靠右,只有运行中的多一行状态,别的不显示),后台命令仍是卡片。全文 [docs/subagents.md](docs/subagents.md)。
- **会话产物**(摘要面板「产物」分组,`GET /api/sessions/:id/outputs`):列表由会话记录推导、不另存状态,也**不改写文件的工具**(`file_write` 看 `tool_summary` 的 Created / Wrote,`apply_patch` 看 `metadata.files` 的 add,子代理跟 `subagent_session_id`);bash 跑脚本生成的文件由 `shell_output_files` 在命令结束后尽力找(提到的路径 + 扫工作目录里刚改过的文档类文件),记进 `metadata.output_files`,只读命令与纯构建 / 包管理 / git 命令不找,一条命令动了 12 个以上文档当批量操作作废;工作目录内只算文档类扩展名(代码归「变更」),`.acecode` 判定要先去掉会话文件夹自身那段(worktree 会话的工作目录就在它下面)。全文 [docs/session-outputs.md](docs/session-outputs.md)。
- **子代理 / 蜂群**:`spawn_subagent` / `wait_subagent` 双端注册,子会话是普通 SessionRegistry 会话(父 cwd + 权限模式),不能再派子代理;子会话从会话列表隐藏、始终跟随主会话(无单独归档 / 删除,主会话永久删除才级联);蜂群 `off | star | mesh` 两套工具互斥,邮件进 AgentLoop 邮箱,信封落盘为 user 角色 + `metadata.inter_agent`,子 agent 完成不唤醒空闲父 agent,Web 芯片只在与服务端模式不同时才随消息提交 `swarm_mode`。Web 里每个子代理前面一个 16px 头像(绑定专家用专家的,否则按子会话 id 生成,对话 / 摘要面板 / 后台任务页签是同一张脸),右上角的状态图标与桌面像素办公室共用 `office_characters.js` 的 `miniSprite`(运行中三个点 + 点头,完成是星,中止是红色感叹号),只有运行中的头像订阅 `avatarStatus.js` 那只共用的钟;生成的头像默认是花瓣(`geometricAvatar.js`,按身份哈希生成的花形矢量图,状态靠转 / 停 / 变灰,不叠角标),设置 > 外观 > 子代理头像可换成像素小人(`web_ui.subagent_avatar_style` = geometric / pixel,走外观偏好那条 daemon 持久化的路,不能只存浏览器本地;用户明确不要单独的「子代理」设置栏);后台任务页签里子代理是单行无边框的行(标题、token 数、时间靠右,只有运行中的多一行状态,别的不显示),后台命令仍是卡片。全文 [docs/subagents.md](docs/subagents.md)。
- **写边界**:`AgentLoop::write_root()`(会话 worktree > LOOP 策略 > 继承)非空即有边界,Yolo 不免除,只有 dangerous 放行;子会话共享父会话 worktree 并继承写边界;bash 走 `shell_write_guard` 可证明写目标守卫(**守卫不是沙箱**,事后用 `workspace_touched` 快照兜底);附加文件夹不能打穿写边界。归档:写边界与子代理继承、多文件夹项目。
- **Worktree**:落在主仓根 `.acecode/worktrees/<slug>`,分支 `worktree-<flatten(slug)>`;`EnterWorktree` / `ExitWorktree` 只在用户明确提到 worktree 时可调,会话存储位置不动;合回主干必须再调 `ExitWorktree`;`ExitWorktree remove` fail-closed;`--worktree [name]` 时 worktree 就是项目根。纯逻辑 [worktree_core](src/domain/worktree/worktree_core.hpp),git 层 [worktree_manager](src/domain/worktree/worktree_manager.hpp)。归档:Worktree 隔离。
- **Goal**:每会话至多一个,存项目级 `state.sqlite3`,只有 `active` 自动 continuation(Plan mode 不触发);unattended 下写工具自动放行、bash 仍取决策表沙盒;provider 终止错误把 goal 转 usage_limited(429)/ blocked,用户 abort 转 paused。归档:Thread Goals。
Expand Down Expand Up @@ -88,6 +89,7 @@ The Superpowers plugin is disabled for this repository. Do not invoke or follow
- **流式渲染**:`renderMarkdownBlocks` 分块 + `useStreamingRenderThrottle` 100ms 降频,未闭合代码块不高亮;`.ace-chat-transcript-scroll` 关 overflow-anchor。归档:流式渲染防跳动。
- **对话内可视化与画图说明**:顶层 ```visual 代码块画在只有 `allow-scripts` 的 srcdoc iframe 里(CSP 只放行四个 CDN,本机 daemon 请求不到),同一组件从流式用到定稿,框内运行时被序列化进 iframe、**不能写中文字面量**;Chart.js 4 / d3 7 内置,由外层把本地代码递进框(不放宽 CSP);系统提示末尾的画图说明 mermaid 部分 TUI + Web 都有,visual 部分只在 daemon 且 `inline_visuals.enabled`,headless / 子会话 / 群聊代答不加,文本字节稳定;详细规范在内置技能 `inline-visuals`,改变量名、类名要与外壳一起改。见 [docs/inline-visuals.md](docs/inline-visuals.md)。
- **交互约定**:对话框键盘走 `components/Modal.jsx` + `lib/dialogKeyboard.js`(Esc / Tab 循环 / Enter 默认操作 / auto-repeat Enter 拦下),不要手写 `fixed inset-0` 遮罩;排队卡片在 transcript 外(`QueueCardList`),中断后队列暂停不自动出队;粘贴 ≥ 20 行 / 2000 字符折成卡片、≥ 128 KiB 落文件;`@` 会话引用有查询词必须走服务端 `q=`;侧栏拖进输入框 = @ 引用;Slate 选区在 compositionstart 前 `adoptDomSelection`。归档:对话框键盘约定、排队卡片栈、粘贴的文本块、`@` 会话引用、输入框光标。
- **右侧文件树多选**(「文件」页签):Shift / Ctrl 选区规则在 `lib/fileTreeSelection.js`(只算画出来的行,折叠 / 刷新后看不见的行从选区裁掉;还没有选区时预览中的文件算已选),交互在 `lib/useFileTreeSelection.js`;多选右键走 `data-desktop-context-menu-delegate="file-tree-selection"` 委托(点在选区外就退回单行菜单),菜单每项后面的数字是它实际处理的条数;拖动是自绘的指针拖动,**不要**改成 HTML5 拖放(桌面壳会当成系统文件拖放拦走),松手派发 `FILE_TREE_DROP_EVENT`(`lib/fileTreeDrop.js`):输入框在光标处插 @路径(目录和它里面的行一起选中时只给最外层),控制台贴加好引号的绝对路径。
- **搜索与批量**:`Ctrl+K` 快捷键只有一个定义点 `lib/searchPaletteShortcut.js`(xterm 要放行);前端纯聚合所有 workspace,后端零路由变更;侧栏多选批量操作只发一次 `POST /api/session-batch`。
- **git 感知**:`/api/git/info` 必须走 `gitInfoCache.js`(一次 spawn 5~7 个 git),`GitSessionPill` 用 `pillCouldRender` 在请求前短路;`src/domain/gitinfo/` 的 ref 名进 prompt / REST / argv 前过 `is_safe_ref_name`;SidePanel「变更」tab 在 git 仓库走 git 级视图。归档:git 感知三件套。
- **HUD 主题**(荒坂,工坊专用、默认不启用,**不写进版本更新记录**):`themeHud.css` 每条选择器以 `:root[data-theme-chrome="hud"]` 开头(滚动条部件用 `html[...]`),`themeHud.test.js` 守着;音效 / 异形窗口 / 背景染色只影响声明它们的主题;动效不跟随系统「减少动态效果」(用户常在 RDP 里用)。归档:HUD 主题外观与多风格。
Expand Down
49 changes: 48 additions & 1 deletion docs/daemon-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ download when nothing changed.
| GET | `/api/sessions/:id/background-tasks/:task_id/output` | tail of a background command's output |
| POST | `/api/sessions/:id/background-tasks/:task_id/stop` | stop a running background task |
| POST | `/api/sessions/:id/background` | move the running foreground command / subagent wait to the background |
| GET | `/api/sessions/:id/outputs` | files the conversation produced (documents, spreadsheets, images) |
| GET | `/api/files` | list directory |
| GET | `/api/files/content` | read text file |
| GET | `/api/files/blob` | read previewable binary file |
Expand Down Expand Up @@ -2580,6 +2581,45 @@ Asks the running foreground `bash` command or blocking subagent wait to hand its
the background registry without interrupting it. `200 {"ok": true, "busy": bool}`; `busy`
is false when no turn was running (the request has no effect then).

### Session outputs

#### `GET /api/sessions/:id/outputs[?workspace=<hash>]`

Files this conversation produced for the user to look at: documents, spreadsheets,
images, media, archives. Code changes are not outputs; they belong to the change views.
The summary panel's Outputs group is the consumer. Sources and filtering rules are in
[session-outputs.md](session-outputs.md).

```json
{
"session_id": "sid",
"outputs": [
{
"path": "C:\\work\\docs\\plan.md",
"relative": "docs/plan.md",
"source": "write",
"size": 1820,
"modified_ms": 1794300000000
}
],
"has_more": false
}
```

- Derived from the session JSONL (plus the JSONL of its subagent sessions) and checked
against the disk on every request: files that no longer exist are omitted. The scan
is incremental per file, so polling is cheap.
- Ordered by file modification time, newest first, at most 50; `has_more` is true when
more exist.
- `path` is absolute. For files inside a session folder it is spelled with that folder's
own form (the one the client already knows as the workspace cwd) and `relative` is the
path below that folder with forward slashes; outside every session folder `relative`
is empty.
- `source`: `write` (`file_write`), `patch` (`apply_patch` added file), `shell` (found
after a `bash` command finished).
- Works for sessions that are not loaded: `workspace` is the same lookup hint as on the
history route. An unknown session returns an empty list, not `404`.

---

## 6. Opencode Import and Pins
Expand Down Expand Up @@ -3991,7 +4031,8 @@ Returns:
"message_auto_collapse": true,
"theme_variant": "",
"theme_effects": true,
"theme_sounds": true
"theme_sounds": true,
"subagent_avatar_style": "geometric"
}
```

Expand All @@ -4016,6 +4057,12 @@ off and does not follow the OS reduced-motion setting. `theme_sounds` (default
All three are persisted, included in the Desktop appearance bootstrap, and
rejected with `400` when malformed.

`subagent_avatar_style` is `geometric` (default, a generated petal rosette) or
`pixel` (the Agent Office character): the portrait drawn for sub-agents that
have no expert attached (Settings > Appearance). It is persisted, included in
the Desktop appearance bootstrap, and any other value is rejected with `400`; a
config file without the key or holding an unknown value loads as `geometric`.

### `PUT /api/config/ui-preferences`

Body:
Expand Down
76 changes: 76 additions & 0 deletions docs/session-outputs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# 会话产物(摘要面板的「产物」分组)

列出这次对话生成、给用户看的文件:报告、表格、图片、音视频、压缩包。代码改动不算
产物,归「变更」。做法参照 Claude 桌面版的 Outputs 面板(界面包源码已核对):列表
不是另存的状态,而是从会话记录推导出来的。Claude 只认 Write 工具和显式交付,这里
多做了一步:bash 跑脚本生成的文件尽力找出来。

## 组成

| 位置 | 职责 |
|---|---|
| `src/domain/session/session_outputs.{hpp,cpp}` | 纯逻辑:从一条消息的元数据取候选、判扩展名、JSONL 行预筛、过滤去重 |
| `src/domain/session/session_outputs_index.{hpp,cpp}` | 读盘:增量扫描会话记录、跟到子代理会话、归一路径、核对文件还在不在、按修改时间排序 |
| `src/adapters/tool/shell_output_files.{hpp,cpp}` | bash 命令结束后找它生成的文件(下面「bash 探测」) |
| `src/adapters/tool/bash_tool.cpp` / `background_tasks.cpp` | 前台命令结束时、后台命令完成通知发出前各调一次探测,结果记进元数据 |
| `src/apps/web/routes/routes_session_outputs.cpp` | `GET /api/sessions/:id/outputs`(见 docs/daemon-api.md「Session outputs」) |
| `web/src/lib/sessionOutputs.js` | 前端纯整形:收起显示前 5 个、重名文件带所在目录、轮询结果没变不重渲染 |
| `web/src/components/SummaryPanel.jsx` | 「产物」分组:点一行在右侧预览打开,右键是「打开文件夹 / 复制文件路径」 |

## 来源

全部取自会话记录里已有的工具结果元数据,**不改写文件的工具**,所以功能上线前的旧
会话也有产物列表:

| 工具 | 记录里的依据 | 算不算 |
|---|---|---|
| `file_write` | `metadata.tool_summary` 的动词 `Created` / `Wrote`,`object` 是路径 | 新建、覆写都算 |
| `apply_patch` | `metadata.files` 里 `type == "add"` 的条目 | 只算新建 |
| `file_edit`、`apply_patch` 的 update / move | — | 不算,相当于编辑 |
| `bash` | `metadata.output_files`;后台命令在完成通知的 `metadata.background_task.output_files` | 探测到的都算,命令失败也算 |
| 子代理 | 派生结果的 `metadata.subagent_session_id` | 跟到子会话的记录,它的产物并进主会话 |

## 过滤

- 路径里有 `.acecode` / `.git` 这一级的不算。会话文件夹自身那一段除外:worktree 会话
的工作目录就在 `.acecode/worktrees` 下面,只按「路径里有 .acecode」判会让它的列表
永远是空的。
- 会话文件夹(工作目录、worktree、附加文件夹)内:只留文档类扩展名(md、txt、pdf、
docx、xlsx、pptx、csv、html、图片、音视频、zip 等,清单在 `session_outputs.cpp`)。
- 会话文件夹外:系统临时目录里的不算,其余任何扩展名都算。
- 已经不在磁盘上的不返回;同一路径只出现一次;按文件修改时间新 → 旧,最多 50 个。

路径比较前两侧都过 `weakly_canonical`(用户目录是目录联接时盘符会不同)。交给界面
的路径再用会话文件夹的原始写法拼回去:界面的预览按它自己知道的工作目录定位文件。

## bash 探测

脚本写文件不经过文件工具,只能事后找。两条线索,都只认文档类扩展名,并且要求文件
修改时间不早于命令开始(放宽 2 秒):

1. **提到的路径**:命令行里的单词(`-o report.pdf`、`--out=a.xlsx`、重定向目标)和
输出末尾 32 KB 里的路径片段(`Saved to D:\x\a.xlsx`,认中文全角标点;路径带空格
时从同一行左边的绝对路径起点取整段)。工作目录外的文件只能靠这条。网址、UNC
路径、带 `~` `$` `%` 的不查。
2. **扫工作目录**:广度优先,点开头的目录和 `node_modules`、`__pycache__`、`venv`
整个跳过;`build` / `dist` / `out` 这类构建目录只看第一层;不跟符号链接和目录联接;
上限 6000 个目录项、120 毫秒、8 层。

不找的情况:

- 只读命令(命令分类器判 KnownSafe),所以 `ls`、`cat`、`git status` 没有额外开销。
- 整条命令只有构建 / 包管理 / 版本控制工具(`git pull`、`pnpm build`、`cmake --build`):
它们一次改动一批文档类文件,不是产物。`uv run`、`cargo run` 这类是在跑用户的
程序,照常找;带输出重定向的命令一律找。
- 一条命令改动的文档类文件超过 12 个:当作批量操作,扫描结果整个作废,明确提到的
路径仍保留。

这是尽力而为,会漏:脚本悄悄写到工作目录外、写进被跳过的目录、只是复制了一个旧
文件(修改时间没变)、一次生成十几个以上的文件。也会多:命令运行期间别的会话或
用户自己改了工作目录里的文档,会被算到这条命令头上。

## 界面刷新

界面收不到「bash 写了文件」的事件。摘要面板可见时:打开 / 换会话 / 回合开始和结束
各取一次;回合进行中每 6 秒取一次。服务端每个记录文件只在第一次整读,之后只读新
追加的部分,行先过子串预筛,绝大多数行不解析 JSON。
Loading
Loading