Skip to content

Commit 93c934a

Browse files
committed
feat: Show live agent activity and hand-offs in the desktop office
- Activity snapshots infer `responding` from token events (the default coding mode never emits it) and keep a bounded streaming tail plus each running tool's one-line call preview. - The office controller overlays WebSocket token/reasoning events between snapshots (sequence-gated, throttled) without extra HTTP requests. - Busy agents keep a status bubble (icon, verb, latest words or call preview) with priority-based collision avoidance; typing, reading, thinking and waiting have distinct motions. - Members walk in from the door, collect a task envelope at their dispatcher's desk, and walk back to hand in a report before leaving; mesh messages fly as labelled envelopes. Reduced motion keeps the envelopes without walking. - The page reports bubble rectangles so the Windows window region no longer clips bubbles above the walls. - Add a macOS host: non-activating NSPanel with WKWebView, a chrome.webview shim, hit-test click-through, drag, resize, zoom and context menu. Not yet compiled on macOS.
1 parent 1f95b89 commit 93c934a

30 files changed

Lines changed: 1856 additions & 138 deletions

‎assets/desktop_pet/DESIGN.md‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,11 @@ components:
102102

103103
### 场景状态
104104

105-
角色动作、气泡和工具图标共同呈现状态。等待授权、等待回答、失败、停止均有明确文案;当前工具图标只在执行工具时显示。上下文文件堆属于相应工位,依据当前上下文使用率呈现。
105+
角色动作、气泡和工具图标共同呈现状态。忙碌期间气泡常驻,一行「图标 动作 · 细节」:动作用短的现在进行时动词,细节用系统界面字体(路径、命令、英文推理在像素字体里难以辨认),放不下时从开头裁掉,保留最新的字和文件名;正在输出的气泡带闪烁光标。等待授权、等待回答、失败、停止均有明确文案;工具图标只在执行或准备工具时显示。上下文文件堆属于相应工位,依据当前上下文使用率呈现。
106+
107+
### 交接
108+
109+
成员加入、完成和 agent 间消息用走路与信封表达:信封带两格残影的抛物线和黄色标签(任务 / 回报 / 消息 / 新任务),落地时收件人头顶冒「!」。走路沿 2 单位网格寻路、不穿家具。
106110

107111
### 溢出成员
108112

@@ -114,7 +118,7 @@ components:
114118

115119
- **Do** 保留原作家具、透明房间、等距像素构形与稳定会话布局。
116120
- **Do** 让控制条、提示和成员列表保持可读,并把状态变化绑定到真实快照。
117-
- **Do** 为会话按钮和跟随开关保留焦点及可访问状态,减少动态效果偏好下抑制消息传递动效。
121+
- **Do** 为会话按钮和跟随开关保留焦点及可访问状态;减少动态效果偏好下不走路,但保留交接信封(它承载信息)。
118122

119123
### Don't:
120124

‎assets/desktop_pet/agent_office_pet.html‎

Lines changed: 356 additions & 65 deletions
Large diffs are not rendered by default.

‎assets/desktop_pet/surface.md‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,20 @@
1818
| 真实状态 | 场景表达 |
1919
| --- | --- |
2020
| 尚未开始执行 | 待命;没有演示角色补位 |
21-
| 推理、调用工具 | 思考或工作;工具执行时显示与聊天共源的图标 |
21+
| 推理 | 思考点轮流跳;气泡显示「思考中」或具体进度标题,以及推理最后几个词 |
22+
| 输出正文 | 打字动画;气泡显示「撰写回复」与最新几个字和光标,两次快照之间由 WebSocket token 实时补字 |
23+
| 调用工具 | 读类工具看屏幕点头、写类工具敲键盘;气泡显示与聊天共源的图标、动作和调用预览 |
24+
| 等成员回报 | 沙漏;气泡显示「等成员回报」 |
2225
| 等待授权、等待回答、重试 | 保留各自的明确状态文案 |
2326
| 主 agent 成功完成 | 留在主工位,显示 `zzz`;下一轮开始后恢复执行状态 |
24-
| 子 agent 成功完成 | 离开工位;后续任务出现时再按真实成员恢复 |
27+
| 子 agent 加入 | 从门口走进,到派活的人桌前领「任务」信封,再回工位 |
28+
| 子 agent 成功完成 | 走到派活的人桌前递「回报」信封后出门;后续任务出现时再按真实成员恢复 |
2529
| 执行失败或停止 | 本轮保留失败或停止状态,不能表现为成功休息 |
2630
| 整理上下文 | 显示整理状态及机器人提示;文件堆随后跟随新上下文比例 |
2731

28-
文件堆反映当前上下文使用量相对窗口容量的比例,不使用会话累计消耗量。容量未知时不绘制文件堆,不能把未知当作零用量。消息信封由真实成员变化或消息记录触发;减少动态效果偏好下抑制该动效。
32+
文件堆反映当前上下文使用量相对窗口容量的比例,不使用会话累计消耗量。容量未知时不绘制文件堆,不能把未知当作零用量。消息信封由真实成员变化或消息记录触发;减少动态效果偏好下不走路,信封仍然飞过(它表达谁和谁在交接,不是装饰)。
33+
34+
状态气泡常驻于忙碌的 agent 头顶,按「求助 > 主 agent > 正在输出 > 刚变化」的优先级摆放:撞上就左右错开或抬高(引线连回头顶),再放不下只显示动作。有人忙时机器人不闲聊。
2935

3036
## 桌面交互
3137

‎cmake/acecode_desktop.cmake‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ FetchContent_MakeAvailable(webview)
7171
# acecode_testable 把 agent/TUI/web assets 全部拖入桌面壳。
7272
if(APPLE)
7373
set(ACECODE_AGENT_BROWSER_HOST_SOURCE ${ACECODE_AGENT_BROWSER_HOST_MAC_SOURCE})
74+
# The macOS office host is the only ARC translation unit in this target.
75+
set(ACECODE_DESKTOP_PET_SOURCE ${ACECODE_DESKTOP_PET_MAC_SOURCE})
76+
set_source_files_properties(${ACECODE_DESKTOP_PET_MAC_SOURCE}
77+
PROPERTIES COMPILE_OPTIONS "-fobjc-arc")
7478
endif()
7579

7680
set(ACECODE_DESKTOP_SOURCES
@@ -132,6 +136,9 @@ if(WIN32)
132136
target_link_libraries(acecode-desktop PRIVATE
133137
ole32 shell32 user32 gdi32 dwmapi
134138
)
139+
endif()
140+
141+
if(WIN32 OR APPLE)
135142
set(ACECODE_DESKTOP_PET_PAGE "${CMAKE_SOURCE_DIR}/assets/desktop_pet/agent_office_pet.html")
136143
set(ACECODE_DESKTOP_PET_PAGE_CPP "${CMAKE_BINARY_DIR}/generated/desktop_pet_page_embedded.cpp")
137144
add_custom_command(

‎cmake/acecode_layer_libraries.cmake‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ set(ACECODE_DESKTOP_BINARY_ONLY_SOURCES
164164
${ACECODE_DEEPIN_WINDOW_EFFECTS_SOURCE} ${ACECODE_DESKTOP_LINUX_SOURCE}
165165
${ACECODE_AGENT_BROWSER_HOST_SOURCE} ${ACECODE_AGENT_BROWSER_HOST_MAC_SOURCE}
166166
${ACECODE_DESKTOP_MAIN_SOURCE} ${ACECODE_DESKTOP_SPLASH_SOURCE}
167-
${ACECODE_DESKTOP_WEB_HOST_SOURCE} ${ACECODE_DESKTOP_PET_SOURCE})
167+
${ACECODE_DESKTOP_WEB_HOST_SOURCE} ${ACECODE_DESKTOP_PET_SOURCE}
168+
${ACECODE_DESKTOP_PET_MAC_SOURCE})
168169
acecode_collect_group_sources(_desktop apps/desktop)
169170
list(REMOVE_ITEM _desktop ${ACECODE_DESKTOP_BINARY_ONLY_SOURCES})
170171
acecode_add_layer_library(acecode_desktop_support apps ${_desktop})

‎cmake/acecode_source_paths.cmake‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ set(ACECODE_DESKTOP_MAIN_SOURCE "${CMAKE_SOURCE_DIR}/src/apps/desktop/main.cpp")
88
set(ACECODE_DESKTOP_SPLASH_SOURCE "${CMAKE_SOURCE_DIR}/src/apps/desktop/splash_screen.cpp")
99
set(ACECODE_DESKTOP_WEB_HOST_SOURCE "${CMAKE_SOURCE_DIR}/src/apps/desktop/web_host.cpp")
1010
set(ACECODE_DESKTOP_PET_SOURCE "${CMAKE_SOURCE_DIR}/src/apps/desktop/desktop_pet.cpp")
11+
set(ACECODE_DESKTOP_PET_MAC_SOURCE "${CMAKE_SOURCE_DIR}/src/apps/desktop/desktop_pet_mac.mm")
1112
set(ACECODE_DESKTOP_LINUX_SOURCE "${CMAKE_SOURCE_DIR}/src/apps/desktop/linux_desktop.cpp")
1213
set(ACECODE_AGENT_BROWSER_HOST_SOURCE "${CMAKE_SOURCE_DIR}/src/apps/desktop/agent_browser_host.cpp")
1314
set(ACECODE_AGENT_BROWSER_HOST_MAC_SOURCE "${CMAKE_SOURCE_DIR}/src/apps/desktop/agent_browser_host_mac.mm")
@@ -87,6 +88,7 @@ acecode_require_sources("shared target source paths"
8788
${ACECODE_DESKTOP_SPLASH_SOURCE}
8889
${ACECODE_DESKTOP_WEB_HOST_SOURCE}
8990
${ACECODE_DESKTOP_PET_SOURCE}
91+
${ACECODE_DESKTOP_PET_MAC_SOURCE}
9092
${ACECODE_DESKTOP_LINUX_SOURCE}
9193
${ACECODE_AGENT_BROWSER_HOST_SOURCE}
9294
${ACECODE_AGENT_BROWSER_HOST_MAC_SOURCE}

‎docs/daemon-api.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -566,9 +566,11 @@ disabled, the write is silently ignored.
566566

567567
`offices` 最多五个:跨工作区按 `last_user_message_at` 降序,排除归档、子会话和没有真实用户输入的会话。打开会话、模型输出、隐藏提示及 agent 间消息不推进此时间;旧记录从 JSONL 逆向读取后缓存。目录或旧记录仍在加载时 `complete=false`,客户端可继续读取;空目录返回空数组及 `selected:null`。分叉继承保留消息的原始时间。
568568

569-
`selected` 和 `agents` 只传身份、标题、所属工作区、真实状态和当前上下文用量等展示字段;不含输入草稿、工具参数、工具输出或历史正文。`agents` 包含根会话和当前 daemon 已装载的直属/网状子会话;网状子会话的 `parent_session_id` 是树根,`agent_path` 保留实际层级。已成功结束的子会话可仍出现在原始快照中,由桌宠投影立即离座。标题/摘要限 240 UTF-8 字节。
569+
`selected` 和 `agents` 只传身份、标题、所属工作区、真实状态和当前上下文用量等展示字段;不含输入草稿、原始工具参数、工具输出或历史正文(下文 `activity.detail` / `activity.text` 两个有界展示片段除外)。`agents` 包含根会话和当前 daemon 已装载的直属/网状子会话;网状子会话的 `parent_session_id` 是树根,`agent_path` 保留实际层级。已成功结束的子会话可仍出现在原始快照中,由桌宠投影立即离座。标题/摘要限 240 UTF-8 字节。
570570

571-
运行中条目的 `activity` 来自事件分发器维护的紧凑状态,不依赖有限的重放缓冲:`known`、`busy`、`turn_id`、`outcome`、`phase`、`label`、`tool`、`seq`、`compact_id`、`transfers`。待授权/待回答优先于工具和模型阶段;`outcome` 保留 `completed`/`aborted`/`error` 差别。`transfers` 最多保留 16 个带序号的 agent 间交接标识,不含消息内容。持久化 `last_turn_outcome` 使恢复后的完成主会话继续显示睡眠状态。
571+
运行中条目的 `activity` 来自事件分发器维护的紧凑状态,不依赖有限的重放缓冲:`known`、`busy`、`turn_id`、`outcome`、`phase`、`label`、`tool`、`detail`、`text`、`seq`、`compact_id`、`transfers`。待授权/待回答优先于工具和模型阶段;`outcome` 保留 `completed`/`aborted`/`error` 差别。`transfers` 最多保留 16 个带序号的 agent 间交接标识,不含消息内容。持久化 `last_turn_outcome` 使恢复后的完成主会话继续显示睡眠状态。
572+
573+
流式正文会把 `phase` 置为 `responding`(默认「用于编程」模式下服务端不发该进度,由 token 事件推断),推理片段对应 `reasoning`,`text` 是本段正文或推理最后不超过 160 UTF-8 字节的尾巴,换阶段或开始工具调用即清空;`detail` 是正在运行工具的一行调用预览(`display_override`,为空时取 `command_preview`),最多 120 字节,工具结束清空。桌宠页面在两次快照之间直接用 WebSocket 的 `token` / `reasoning` 事件按 `seq` 叠加实时文字(约 150ms 节流),快照追上后以快照为准。
572574

573575
`token_usage` 是最近请求上下文统计,配合 `context_window` 计算文件堆厚度;未知窗口不伪造比例。休眠会话可通过已有 model 端点补充窗口大小。该端点不会发起模型请求或改变会话运行状态。
574576

‎docs/desktop-agent-office.md‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11
# Agent 桌面办公室
22

3-
Windows Desktop 会显示一个透明的像素办公室。单会话有一个主角色 Maestro,多 agent 任务按真实子会话入座;无需启动额外模型,也没有演示任务循环。美术与拖动缩放来自原有 Claude 桌宠,旧的 `design-prototypes/agent-office` 事务所原型已移除。
3+
Windows 与 macOS Desktop 会显示一个透明的像素办公室。单会话有一个主角色 Maestro,多 agent 任务按真实子会话入座;无需启动额外模型,也没有演示任务循环。美术与拖动缩放来自原有 Claude 桌宠,旧的 `design-prototypes/agent-office` 事务所原型已移除。
44

55
## 操作
66

77
- 顶部显示跨工作区最近发过消息的五个主会话。顺序只看用户最后发送消息的时间,打开、切换和 agent 输出不会把会话顶到最前面。
88
- “跟随”默认开启,办公室随主窗口当前会话切换。点击门牌会关闭跟随并固定办公室;再次打开跟随后恢复跟随。查看最近五个之外的当前会话不改变门牌排序。
99
- 主 agent 成功完成后留在座位显示 `zzz`,下一轮自动醒来;成功完成的子 agent 立即离座,重新工作时以同样的外观返回。
10+
- 忙碌的 agent 头顶常驻状态气泡:「图标 动作 · 细节」。正在输出时显示「撰写回复」和最新的几个字(打字动画 + 光标),推理时显示思考点和推理尾巴,调用工具时显示动作(读取、运行命令、修改代码…)和调用预览(路径、命令)。默认「用于编程」模式下服务端不发正文进度,桌宠由 token 事件推断。气泡互相避让:先左右错开,再抬高并用引线连回头顶,放不下时只留动作。
11+
- 子 agent 出现时从门口走进来,先到派活的人桌前领「任务」信封再回工位;完成后走到派活的人桌前递「回报」信封(派活的人显示「收到…的结果」)再出门;失败或停止的成员直接出门。网状协作中 agent 间消息以带「消息 / 新任务 / 回报」标签的信封在工位间飞过。系统开启「减少动态效果」时不走路,交接信封仍然显示。
1012
- 等待授权、等待回答、重试、压缩、失败和取消有各自状态。超过七个子角色时点击 `+N` 查看成员并打开对应会话;列表随状态更新,Escape 收起。选中房间人物后双击可打开其会话。
1113
- 文件堆随当前上下文占用增长,压缩后依据实际新统计变薄。工具符号与聊天工具行共用 SVG 来源。
1214
- 拖动房间空地移动窗口;滚轮、右下角把手和右键菜单可缩放。窗口不抢主界面焦点,透明角落可穿透点击;尺寸按原实现保存。
1315

1416
## 构建与数据流
1517

16-
由当前工作区的 `scripts/dev_desktop.bat` 启动开发版。先构建 `web/`,再配置/构建 CMake Desktop,使内嵌主界面与桌宠资源保持一致。`ACECODE_DESKTOP_PET=0` 可对本次进程禁用桌宠;macOS/Linux 当前没有原生桌宠窗口实现。
18+
由当前工作区的 `scripts/dev_desktop.bat` 启动开发版。先构建 `web/`,再配置/构建 CMake Desktop,使内嵌主界面与桌宠资源保持一致。`ACECODE_DESKTOP_PET=0` 可对本次进程禁用桌宠;Linux 当前没有原生桌宠窗口实现。
19+
20+
macOS 版(`src/apps/desktop/desktop_pet_mac.mm`,ARC 编译)用非激活 NSPanel + WKWebView 承载同一份页面:文档开始前注入 `window.chrome.webview` 垫片,页面代码不分平台;透明区域的点击穿透靠定时检测鼠标是否落在房间、控制条或页面上报的浮层里,再切换 `ignoresMouseEvents`;拖动与把手缩放由定时器跟随鼠标。默认 1.25 倍(约 430×315 点),设置存 `~/Library/Application Support/ACECode/desktop-pet/settings.json`。
1721

1822
数据链为:会话元数据/事件分发器 → `GET /api/desktop-office` → App 级办公室控制器 → 原生 WebView 桥 → 像素场景。控制器复用现有引用计数 WS 订阅,事件触发刷新并以 3 秒轮询兜底;正在加载旧记录时短间隔继续读取。快速切换以请求代次和取消信号隔离迟到响应;页面卸载释放订阅和计时器,重载从原生缓存恢复当前选择与跟随状态。
1923

24+
页面把成员列表、提示和状态气泡的矩形(节流后)上报给原生窗口:Windows 的窗口区域同时裁剪绘制,冒出墙顶的气泡必须并入区域才不会被切掉;macOS 用同一份矩形判断点击穿透。
25+
2026
断线时明确显示正在重连,恢复后重取真实快照。错误和取消不会被当作成功睡眠。只同步小型展示字段,不通过动画触发或控制 agent 任务。
2127

2228
视觉规则见 [桌宠设计](../assets/desktop_pet/DESIGN.md),协议见 [daemon API](daemon-api.md)。验收命令及本次实测范围见 [交付记录](../openspec/changes/integrate-desktop-agent-office/verification.md)。
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-10-06
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
## Context
2+
3+
见 proposal.md。前一变更 `integrate-desktop-agent-office` 已交付 Windows 办公室;本变更只增强表现层与平台覆盖。
4+
5+
## Decisions
6+
7+
### 1. 状态来源
8+
9+
服务端 `SessionActivityState` 在分发器锁内折叠事件:token → `responding`,reasoning → `reasoning`,各自保留有界尾巴,换阶段或开始工具即清空;工具开始记录 `display_override`(为空取 `command_preview`)。这样快照在任意时刻都说得清「正在做什么」,打开办公室或断线重连后不依赖重放。实时性由 Web 控制器负责:token/reasoning 事件按会话写入叠加层,`seq` 大于快照才生效,快照追上即丢弃;连续正文在快照尾巴后拼接。不为每个 token 发 HTTP 请求。
10+
11+
### 2. 文案
12+
13+
动作动词与工具图标同表(`toolIcons.js`);「适合日常工作」模式下的具体进度标题优先,引擎默认的笼统文案(等待模型响应、正在调用工具 X…)不进气泡。细节按半角宽度从开头裁剪,保留最新的字和文件名。
14+
15+
### 3. 交接动画
16+
17+
走路沿 2 单位网格 BFS(8 向、不斜穿桌角),家具占地外扩 1 单位;先到派活的人(`agent_path` 的上一级,找不到时是主 agent)桌前停顿并交接信封,再回工位或出门。离开中的成员又接到任务时掉头回座。只在本办公室已显示过真实快照后演,切换办公室和重连恢复直接落座。减少动态效果时不走路但保留信封。
18+
19+
### 4. 原生窗口区域
20+
21+
Windows `SetWindowRgn` 同时裁剪绘制,气泡冒出墙顶会被切掉,所以页面把气泡矩形(含尾巴与引线)节流上报,原生端与成员列表/提示一起并入区域。macOS 没有窗口区域,定时检测鼠标是否命中房间、控制条或这些矩形,切换 `ignoresMouseEvents`。命中判定与消息解析是纯函数,跨平台单测。
22+
23+
### 5. macOS 宿主
24+
25+
NSPanel(无边框、非激活、不可成为 key window、浮动层级、所有空间可见),WKWebView 关闭背景绘制并重载 `acceptsFirstMouse`。页面按 WebView2 接口编写,宿主在文档开始前注入同名垫片,消息经 `WKScriptMessageHandler` 回到 C++,宿主经 `evaluateJavaScript` 派发。拖动与把手缩放由 60Hz 定时器跟随鼠标。ObjC 回调只持有控制器 weak_ptr;关闭时移除脚本消息处理器(否则内容控制器强引用导致泄漏)。默认 1.25 倍逻辑尺寸,设置不写入 ACECode 数据目录。
26+
27+
## Risks / Trade-offs
28+
29+
- [macOS 未在本机编译] → 只用稳定的 AppKit/WebKit 公共接口与 ARC;需 macOS CI 或实机构建确认。
30+
- [气泡拥挤] → 优先级 + 错开/抬高/压缩三级避让;极端满员时仍可能重叠。
31+
- [流式尾巴进入快照] → 只保留 160 字节、换阶段清空,接口仍需鉴权,与聊天页可见内容一致。

0 commit comments

Comments
 (0)