dsh-mutipleagent 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/README.en.md +34 -4
- package/README.md +34 -5
- package/assets/kenney/LICENSE-mini-characters.txt +27 -27
- package/client.js +62 -8
- package/package.json +4 -2
- package/public/app.js +1 -1
- package/public/chunks/{chunk-RQUUGNU7.js → chunk-MU3LEDFF.js} +2 -2
- package/public/demo.css +55 -3
- package/public/demo.html +7 -4
- package/public/demo.js +6 -6
- package/public/vendor/office.js +1631 -1631
- package/vendor/licenses/PLAYWRIGHT-LICENSE.txt +202 -202
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,70 @@
|
|
|
2
2
|
|
|
3
3
|
本文件记录 `dsh-mutipleagent` 每个版本的变化。npm 版本号与 Git 标签一一对应(`0.1.x` ↔ `v0.1.x`),同一份说明也会发布在 [GitHub Releases](https://github.com/lkuliuying/dsh-mutipleagent/releases) 页面,插件市场里的版本说明同样读取这些 release 正文。
|
|
4
4
|
|
|
5
|
+
## v0.4.0 — 2026-11-10
|
|
6
|
+
|
|
7
|
+
办公室从「四个人围着几张桌子」变成一整层真实办公空间。
|
|
8
|
+
|
|
9
|
+
### 新增
|
|
10
|
+
|
|
11
|
+
- 整层办公室场景:`collaborationScene` 重写为 4 列 × 3 行「对坐工位」,12 组共享长桌共 24 个座位铺满 23×21 的房间。每组一张 3.3×2.0 长桌,两位成员隔桌面对面就座(主 Agent 与第一位子 Agent 落在同一组);桌心背靠背双屏、键鼠与副屏各朝自己一侧,桌心再加绿植与文件分隔。
|
|
12
|
+
- 房间补齐家具:前区玻璃会议室(长桌、四把椅、投屏)、沿墙设备柜与 SSD 指示灯阵列、左墙书架与茶水吧、右墙资料柜、后区沙发与茶几,四周铺绿植。
|
|
13
|
+
- 新增「隐藏面板」开关:`web/shell.mjs` 的 `initShell()` 绑定 `#toggle-shell`,一键把顶栏、统计卡、Agent 详情、工具条与任务协作流程全部收起,只留 3D 场景;右下角「显示面板」、再次点按钮或按出 `Esc` 恢复,`H` 键随时切换,状态存于 `localStorage` 的 `office.shell`。
|
|
14
|
+
|
|
15
|
+
### 改进
|
|
16
|
+
|
|
17
|
+
- 镜头对准房间中心并整体拉远(`fitCamera3D` 目标 `(11.5, 0.35, 11.5)`、可视高度 31),整层办公室都入画,底部工位不再被协作流程面板挡住。
|
|
18
|
+
- 室外地坪从 44×44 扩到 440×440 并与背景同色(`#dde5ef`),去掉「悬空平台」的硬边;室内地面、地毯与工位色阶重新分层。
|
|
19
|
+
- 工位台面改为浅色台面 + 蓝色前后沿 + 蓝色端板,12 组并排时不再糊成一片蓝。
|
|
20
|
+
- 地面丝印文案(`COORDINATION` / `AGENT WORKSPACE` / `COLLABORATION LANE` / `VERIFICATION`)与斜纹按新走廊重新定位。
|
|
21
|
+
- 场景里的成员标签会避让顶部统计卡,并按纵向顺序错开重叠,对坐工位上的四个名字不再互相遮挡。
|
|
22
|
+
|
|
23
|
+
### 验证
|
|
24
|
+
|
|
25
|
+
- 新增 `.artifacts/shell-check.cjs`:演示模式截图 4 位成员分坐两组对坐工位;断言点击「隐藏面板」后 `header.nav`、`.stats`、`#agent-details`、`#workflow-dock`、`.scene-tools` 全部不可见、`body[data-shell="scene"]` 生效、`#shell-restore` 出现,`Esc` 后恢复且刷新仍保持。
|
|
26
|
+
- `.artifacts/embed-check.cjs`、`.artifacts/details-check.cjs` 继续通过;`npm run test:plugin:ui` 全部检查项通过(1500×1000 / 900×720 / 390×844 三档视口);`npm test` 84 passed。
|
|
27
|
+
|
|
28
|
+
## v0.3.1 — 2026-11-10
|
|
29
|
+
|
|
30
|
+
会话内标签从「一格里的小画面」变成「整页画面」。
|
|
31
|
+
|
|
32
|
+
### 改进
|
|
33
|
+
|
|
34
|
+
- 打开「多 Agent 办公室」标签时收起会话页底部输入框(宿主源码里的 `[data-conversation-region="composer"]`,`flex: none` 无固定高度,收起后高度直接让给视图区),切回「对话」自动恢复;标签页内不需要打字。
|
|
35
|
+
- 嵌入模式(`embed=1`)收起办公室自己的整条顶栏(搜索、会话下拉、计时、铃铛),协作统计与「Agent 详情」上移到标签页顶部;搜索与通知改用宿主自带的。
|
|
36
|
+
|
|
37
|
+
- 「Agent 详情」面板把「查看任务详情」与「查看屏幕 / 查看任务原文」移到标题下方,左侧任务信息再长也不必先滚到面板底部;底部的「只读真实记录 · 在 DeepSeek Harness 中执行任务」小字删除。
|
|
38
|
+
- 场景左下角的「拖动旋转 · 滚轮缩放 · 点击工位或显示器查看详情」在真实会话里隐藏(与左下角工具条重复);任务协作流程标题下的说明改为单行省略,不再被硬裁。
|
|
39
|
+
|
|
40
|
+
### 验证
|
|
41
|
+
|
|
42
|
+
- 新增 `.artifacts/composer-check.cjs`:以最小 React 桩加载真实 `client.js`,断言挂载时 body 打标、输入框 `display:none`、视图区接管让出的高度、卸载后恢复。
|
|
43
|
+
- `.artifacts/embed-check.cjs` 增加断言:`header.nav` 不可见、`.stats` 顶边对齐 14px。
|
|
44
|
+
- 新增 `.artifacts/details-check.cjs`:1200×820 与 900×640 下打开详情面板,断言动作按钮落在面板可视范围内、`#detail-disclaimer` 已移除、面板未滚动。
|
|
45
|
+
- `tests/plugin-browser.cjs` 增加 `assertDetailsActionsReachable`:紧凑视口打开面板后校验动作区无需滚动。
|
|
46
|
+
|
|
47
|
+
## v0.3.0 — 2026-11-10
|
|
48
|
+
|
|
49
|
+
办公室从「侧栏打开的面板」变成「会话页顶部标签行里的一个视图」,打开即绑定当前会话。
|
|
50
|
+
|
|
51
|
+
### 新增
|
|
52
|
+
|
|
53
|
+
- 注册 `conversation.view`(`id: dsh-mutipleagent`、`order: 30`、标签「多 Agent 办公室」),与「对话 / 轨迹 / 上下文」并列;标签行放不下时进入右侧「更多会话视图」菜单,可在菜单里固定或取消固定。
|
|
54
|
+
- 视图拿到 `inject(sessionId)` 后以 `?mode=live&source=harness&rootId=<会话 ID>&embed=1` 打开办公室页面,切换会话或切回标签时按新会话重载 iframe。
|
|
55
|
+
|
|
56
|
+
### 改进
|
|
57
|
+
|
|
58
|
+
- 嵌入模式(`embed=1`)隐藏工作目录选择器,标题位显示当前会话名;与宿主重复的品牌、模式切换、语言切换、账号区一并隐藏,只留搜索 / 会话 / 通知。
|
|
59
|
+
- 深链 `rootId` 指向的会话不在默认根列表(例如子 Agent 会话)时,以全量列表重试一次,不再停在「未找到该会话」。
|
|
60
|
+
- `dsh.client.inject` 补充 `@deepseek-ai/dsh-client-ui-chat` 与 `@deepseek-ai/dsh-client-ui-conversation`,与被依赖的官方插件保持同样的加载顺序声明。
|
|
61
|
+
- 侧栏 `sidebar.panellist` 入口与全局面板 `main` 注册保持不变,跨项目浏览仍走原来的路径。
|
|
62
|
+
|
|
63
|
+
### 验证
|
|
64
|
+
|
|
65
|
+
- `npm test` 84 通过;`tests/plugin.test.js` 新增会话视图注册断言(id、标签文案、`inject()` 回传会话 ID)。
|
|
66
|
+
- `npm run test:plugin:ui`(1500 / 900 / 390 三档视口)通过。
|
|
67
|
+
- 新增 `.artifacts/embed-check.cjs`:以夹具服务加载嵌入地址,断言目录选择器隐藏、标题为当前会话名、`#scenario` 为对应 ID,并确认不带 `embed` 的深链仍显示目录选择器。
|
|
68
|
+
|
|
5
69
|
## v0.2.1 — 2026-11-10
|
|
6
70
|
|
|
7
71
|
只读真正变化的会话:功能与 v0.2.0 相同,读取路径更省。
|
|
@@ -15,6 +79,7 @@
|
|
|
15
79
|
### 验证
|
|
16
80
|
|
|
17
81
|
- 84 个测试全绿,新增两条覆盖增量路径:游标未变时不物化事件、租约每次都释放且观察失败时回退整份读取。
|
|
82
|
+
- 已发布到 npm:`npm i dsh-mutipleagent@0.2.1`;安装后 `dsh` 元数据为 `bundle.patch=./cordis.patch.yml`、`client` 存在。
|
|
18
83
|
|
|
19
84
|
## v0.2.0 — 2026-11-10
|
|
20
85
|
|
package/README.en.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# dsh-mutipleagent
|
|
2
2
|
|
|
3
3
|
[](https://github.com/lkuliuying/dsh-mutipleagent/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/dsh-mutipleagent)
|
|
4
5
|
[](LICENSE)
|
|
5
6
|
|
|
6
7
|
English · [简体中文](README.md)
|
|
7
8
|
|
|
8
|
-
A 3D office for DeepSeek Harness multi-agent work.
|
|
9
|
+
A 3D office for DeepSeek Harness multi-agent work. Every conversation page grows a **Multi-Agent Office** tab next to Chat / Trajectory / Context — open it and the office is already showing that conversation, with no workspace or session picker in the way. The sidebar entry stays for cross-project browsing.
|
|
9
10
|
|
|
10
11
|
**Read-only.** It never starts, pauses or approves tasks, never calls a model, never writes to a session, and never sends anything off the machine.
|
|
11
12
|
|
|
@@ -17,7 +18,26 @@ A 3D office for DeepSeek Harness multi-agent work. Open it from the Harness side
|
|
|
17
18
|
|
|
18
19
|
Generated from demo data with `npm run screenshots`. The interface ships in Chinese and English — switch with the 中 / EN control in the top bar. In live mode, task text, agent names and event previews stay exactly as they appear in your session logs, so they are not translated.
|
|
19
20
|
|
|
20
|
-
Current version `v0.
|
|
21
|
+
Current version `v0.4.0`, package `dsh-mutipleagent`. Compatible with DeepSeek Harness `^0.2.2-rc.1` and Node.js `^22.19.0 || >=24.0.0`. See [CHANGELOG.md](CHANGELOG.md) for per-version notes and [Releases](https://github.com/lkuliuying/dsh-mutipleagent/releases) for the published notes.
|
|
22
|
+
|
|
23
|
+
## What's new in v0.4.0
|
|
24
|
+
|
|
25
|
+
- The office now fills a whole floor: a 4 × 3 grid of face-to-face pods (24 seats) where the lead agent and the first sub-agent share a table and sit across from each other, with back-to-back monitors down the middle and each side's keyboard, mouse and side screen facing its own seat. The room also gains a glass meeting room, equipment racks, bookshelves, a kitchen counter and a lounge corner.
|
|
26
|
+
- Hide everything but the scene: click the button in the scene toolbar (or press `H`) to collapse the top bar, stat cards, agent details and the workflow dock; use the "Show panels" pill or press `Esc` to bring them back. The choice is stored locally and survives reloads.
|
|
27
|
+
- The camera is pulled back and centred so the whole floor is in frame, and the outdoor ground now matches the background colour, so the office no longer floats on a hard-edged slab and the bottom row of desks stays clear of the workflow dock.
|
|
28
|
+
|
|
29
|
+
## What's new in v0.3.1
|
|
30
|
+
|
|
31
|
+
- Opening the Multi-Agent Office tab hides the conversation composer, and returning to Chat restores it — the picture gains a whole block of height.
|
|
32
|
+
- Embed mode also drops the office's own top bar (search, Session dropdown, clock, bell); the collaboration stats and Agent details move up to the top of the tab.
|
|
33
|
+
- Inside the details panel the primary action and the "view screen / view task source" links now sit right under the header, so nothing needs scrolling; the footer note and the scene drag hint are gone, and the workflow caption is clamped to one line.
|
|
34
|
+
|
|
35
|
+
## What's new in v0.3.0
|
|
36
|
+
|
|
37
|
+
- The office is a conversation view: it registers `conversation.view` and sits in the tab row beside Chat / Trajectory / Context, folding into the "More views" menu when the row is narrow. No panel-first, no picking a directory and then a session.
|
|
38
|
+
- Opening the tab pins the current session: the frame loads with `?mode=live&source=harness&rootId=<session id>&embed=1`, the directory picker is hidden and the title becomes the session title. Switching conversation or tab reloads the frame for the new session, and a session missing from the default root list (sub-agents, for example) falls back to the full list instead of failing.
|
|
39
|
+
- Embed mode trims the chrome the host already provides: brand, mode switch, language switch and account section are hidden, leaving search, sessions and notifications.
|
|
40
|
+
- The sidebar entry and the global panel are unchanged.
|
|
21
41
|
|
|
22
42
|
## What's new in v0.2.1
|
|
23
43
|
|
|
@@ -34,6 +54,16 @@ Current version `v0.2.1`, package `dsh-mutipleagent`. Compatible with DeepSeek H
|
|
|
34
54
|
|
|
35
55
|
## Install
|
|
36
56
|
|
|
57
|
+
### From npm
|
|
58
|
+
|
|
59
|
+
The package is published to [npm](https://www.npmjs.com/package/dsh-mutipleagent); the npm version matches the plugin version:
|
|
60
|
+
|
|
61
|
+
dsh plugin --profile desktop add 'dsh-mutipleagent@0.2.1' --ignore-scripts
|
|
62
|
+
|
|
63
|
+
Bump the version number when upgrading.
|
|
64
|
+
|
|
65
|
+
### From a GitHub tag
|
|
66
|
+
|
|
37
67
|
Quit Harness Desktop completely, then:
|
|
38
68
|
|
|
39
69
|
dsh plugin --profile desktop add 'dsh-mutipleagent@https://codeload.github.com/lkuliuying/dsh-mutipleagent/legacy.tar.gz/refs/tags/v0.2.1' --ignore-scripts
|
|
@@ -42,7 +72,7 @@ If `dsh` is not on `PATH`, use the bundled CLI:
|
|
|
42
72
|
|
|
43
73
|
& 'C:\ProgramSoftware\Deepseek\resources\runtime\cli\bin\dsh.cmd' plugin --profile desktop add 'dsh-mutipleagent@https://codeload.github.com/lkuliuying/dsh-mutipleagent/legacy.tar.gz/refs/tags/v0.2.1' --ignore-scripts
|
|
44
74
|
|
|
45
|
-
Restart Harness and
|
|
75
|
+
Restart Harness and open **Multi-Agent Office** from any conversation's tab row (or from the sidebar for the cross-project view). No API key, no separate server, nothing else to configure.
|
|
46
76
|
|
|
47
77
|
To build from source instead:
|
|
48
78
|
|
|
@@ -55,7 +85,7 @@ To build from source instead:
|
|
|
55
85
|
|
|
56
86
|
- **Desks, not dashboards.** Agents sit down, face their monitor and type; screens show current status and the latest event. Task hand-offs and reports travel as light trails from screen to screen.
|
|
57
87
|
- **Screen detail.** Click any monitor for the real agent, status, task, model/tool and recent events. Esc, overlay and a close button all work; narrow screens switch to a readable text layout.
|
|
58
|
-
- **Session picker.**
|
|
88
|
+
- **Session picker, when you want it.** The sidebar view asks for a workspace directory, then a session inside it. Search by path, title, time or short ID; identical titles stay distinguishable. Your last choice is remembered.
|
|
59
89
|
- **Notification centre.** Unread dot clears against a read anchor, with "mark all read", category icons and a compact three-line summary.
|
|
60
90
|
- **Graceful degradation.** Without WebGL the team tree, activity log and reports still work, and "reduce motion" is one checkbox away.
|
|
61
91
|
- **Bilingual UI.** Chinese and English, switchable in the header and remembered locally.
|
package/README.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# dsh-mutipleagent
|
|
2
2
|
|
|
3
3
|
[](https://github.com/lkuliuying/dsh-mutipleagent/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/dsh-mutipleagent)
|
|
4
5
|
[](LICENSE)
|
|
5
6
|
|
|
6
7
|
[English](README.en.md) · 简体中文
|
|
7
8
|
|
|
8
|
-
DeepSeek Harness 多 Agent 3D
|
|
9
|
+
DeepSeek Harness 多 Agent 3D 协作办公室插件。安装后,任意会话页顶部标签行会多出「多 Agent 办公室」,点开即用——直接显示当前会话的办公室,不必先选工作目录、再选会话;侧栏入口保留,用于跨项目浏览。观察真实主会话、子 Agent、工具活动和完整汇报,沿用本项目已有的 3D 场景与交互。
|
|
9
10
|
|
|
10
11
|
**只读**:不启动任务、不调用模型、不审批、不修改会话,也不把任何内容发到本机以外。
|
|
11
12
|
|
|
@@ -17,10 +18,29 @@ DeepSeek Harness 多 Agent 3D 协作办公室插件。安装后从 Harness 侧
|
|
|
17
18
|
|
|
18
19
|
以上为 `npm run screenshots` 生成的演示画面(演示数据,非真实会话)。
|
|
19
20
|
|
|
20
|
-
当前版本为 `v0.
|
|
21
|
+
当前版本为 `v0.4.0`,包名为 `dsh-mutipleagent`,仓库地址为 [lkuliuying/dsh-mutipleagent](https://github.com/lkuliuying/dsh-mutipleagent)。兼容 DeepSeek Harness `^0.2.2-rc.1`,需要 Node.js `^22.19.0 || >=24.0.0`。
|
|
21
22
|
|
|
22
23
|
各版本变化见 [CHANGELOG.md](CHANGELOG.md),发布说明见 [GitHub Releases](https://github.com/lkuliuying/dsh-mutipleagent/releases)。
|
|
23
24
|
|
|
25
|
+
## v0.4.0 更新
|
|
26
|
+
|
|
27
|
+
- 办公室场景铺满一整层:4 列 × 3 行「对坐工位」共 24 座,主 Agent 与第一位子 Agent 隔桌面对面;共享长桌中间是背靠背的双屏,两侧各配键鼠与副屏。房间同时补上玻璃会议室、设备柜、书架、茶水吧与休息角。
|
|
28
|
+
- 「隐藏面板」:点场景工具条上的按钮(或按 `H`)只留 3D 画面,顶栏、统计卡、Agent 详情与任务协作流程一起收起;右下角「显示面板」或 `Esc` 恢复,偏好存在本地,刷新后保持。
|
|
29
|
+
- 镜头整体拉远并居中,整层办公室完整入画;室外地坪与背景同色,不再出现悬空平台的硬边,底部工位也不会被协作流程挡住。
|
|
30
|
+
|
|
31
|
+
## v0.3.1 更新
|
|
32
|
+
|
|
33
|
+
- 打开「多 Agent 办公室」标签时,会话页底部的输入框自动收起,切回「对话」立刻恢复——标签页内本来就不需要打字,画面因此多出一整块高度。
|
|
34
|
+
- 嵌入模式下办公室自己的整条顶栏(搜索、会话下拉、计时、铃铛)也一并收起,协作统计与「Agent 详情」上移到标签页顶部;搜索和通知改用宿主自带的。
|
|
35
|
+
- 「Agent 详情」面板的「查看任务详情」与「查看屏幕 / 查看任务原文」上移到标题下方,不必滚到底部;面板底部的小字说明、场景左下角的操作提示一并删除,任务协作流程标题行的说明改为单行省略。
|
|
36
|
+
|
|
37
|
+
## v0.3.0 更新
|
|
38
|
+
|
|
39
|
+
- 办公室进入会话页顶部标签行:注册 `conversation.view`,与「对话 / 轨迹 / 上下文」并列,标签行放不下时收进右侧「更多会话视图」。不再需要先在侧栏打开面板、再挑工作目录和会话。
|
|
40
|
+
- 打开即锁定当前会话:页面用 `?mode=live&source=harness&rootId=<会话 ID>&embed=1` 打开,目录选择器自动隐藏,标题显示当前会话名;切换会话、切换标签时 iframe 会按新的会话 ID 重载。会话不在默认根列表(例如子 Agent 会话)时自动回退到全量列表,不再报「未找到该会话」。
|
|
41
|
+
- 嵌入模式收掉与宿主重复的外壳:品牌、模式切换、语言切换、账号区隐藏,只留搜索、会话、通知。
|
|
42
|
+
- 侧栏入口与全局面板保留,行为不变;`main` / `sidebar.panellist` 注册未改动。
|
|
43
|
+
|
|
24
44
|
## v0.2.1 更新
|
|
25
45
|
|
|
26
46
|
- 读取只展开真正变化的会话:`sessionQuery.observeSession()` 的观察租约只取 header 与日志游标,事件按需物化;游标未变的会话不再克隆整份日志、也不再重新折叠,复用数见 `/api/health` 的 `reused`。
|
|
@@ -39,7 +59,15 @@ DeepSeek Harness 多 Agent 3D 协作办公室插件。安装后从 Harness 侧
|
|
|
39
59
|
|
|
40
60
|
## 安装到 Harness
|
|
41
61
|
|
|
42
|
-
###
|
|
62
|
+
### 从 npm 安装
|
|
63
|
+
|
|
64
|
+
包已发布到 [npm](https://www.npmjs.com/package/dsh-mutipleagent),版本号与插件版本一致:
|
|
65
|
+
|
|
66
|
+
dsh plugin --profile desktop add 'dsh-mutipleagent@0.2.1' --ignore-scripts
|
|
67
|
+
|
|
68
|
+
升级时把 `0.2.1` 换成目标版本号。首次安装建议完全退出 Harness Desktop 后再执行,装完重新启动。
|
|
69
|
+
|
|
70
|
+
### 从 GitHub 标签安装
|
|
43
71
|
|
|
44
72
|
完全退出 Harness Desktop,在 PowerShell 运行以下命令,再重新启动 Harness:
|
|
45
73
|
|
|
@@ -68,7 +96,7 @@ DeepSeek Harness 多 Agent 3D 协作办公室插件。安装后从 Harness 侧
|
|
|
68
96
|
|
|
69
97
|
& 'C:\ProgramSoftware\Deepseek\resources\runtime\cli\bin\dsh.cmd' plugin --profile desktop add 'F:\Program\Multiple-Agent\.artifacts\dsh-mutipleagent-0.2.1.tgz'
|
|
70
98
|
|
|
71
|
-
重启 Harness
|
|
99
|
+
重启 Harness,在任意会话页顶部标签行点「多 Agent 办公室」,或从侧栏「多 Agent 办公室」打开。不需要单独运行办公室服务,也不需要设置 API Key。插件从 `sessionQuery` 公开接口读取会话,不读取 `.codex`、凭据或私有推理。
|
|
72
100
|
|
|
73
101
|
也可以从 Git 地址安装最新主分支:
|
|
74
102
|
|
|
@@ -78,7 +106,8 @@ DeepSeek Harness 多 Agent 3D 协作办公室插件。安装后从 Harness 侧
|
|
|
78
106
|
|
|
79
107
|
- 页面位于宿主的 `/dsh-mutipleagent/` 路由,Web 和 Desktop 共用同源入口;会话接口沿用 Harness 登录校验,仅允许 owner 访问。
|
|
80
108
|
- 主会话与真实子 Agent 的关系、状态和正文来自已记录事件。没有百分比时显示「未记录」;子 Agent 结束不代表主任务完成。
|
|
81
|
-
-
|
|
109
|
+
- 会话内标签直接绑定当前会话:`conversation.view` 的 `inject(sessionId)` 拿到会话 ID 后拼进页面地址,嵌入模式隐藏目录选择器,标题显示当前会话;标签行空间不足时收进「更多会话视图」,可在其中固定或取消固定。
|
|
110
|
+
- 侧栏入口仍是跨项目视图:先选择工作目录,再从该目录选择具体会话。展开下拉后可按完整路径或会话标题、时间、ID 筛选;目录显示完整路径和会话数量,会话分行显示标题、创建时间与短 ID,同名会话也能区分。未记录目录的会话单独归组。
|
|
82
111
|
- 记住上次选择的目录和会话;地址中明确指定的会话优先。「跟随新会话」仅跟随当前目录,其他项目的新任务不会抢走显示。窄窗口通过「会话 / 团队」打开同样的目录筛选。
|
|
83
112
|
- 可以选择成员、搜索任务、查看任务与汇报全文,或切换到明确标记的模拟演示。后台会话标题最多每 30 秒刷新一次,正在观察的团队随原有轮询更新。
|
|
84
113
|
- 搜索与下拉支持方向键、Home / End 和 Enter;下拉中 Escape 取消筛选并返回触发器,Tab 继续切换焦点。任务搜索按 `/` 聚焦,Escape 先收起结果,再按一次清空;清空按钮保留输入框焦点。中文输入法组合期间 Enter 不会提前选择。演示模式使用同样的控件。
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Mini Characters (1.0)
|
|
4
|
-
|
|
5
|
-
Created/distributed by Kenney (www.kenney.nl)
|
|
6
|
-
Creation date: 17-07-2024 11:31
|
|
7
|
-
|
|
8
|
-
------------------------------
|
|
9
|
-
|
|
10
|
-
License: (Creative Commons Zero, CC0)
|
|
11
|
-
http://creativecommons.org/publicdomain/zero/1.0/
|
|
12
|
-
|
|
13
|
-
You can use this content for personal, educational, and commercial purposes.
|
|
14
|
-
|
|
15
|
-
Support by crediting 'Kenney' or 'www.kenney.nl' (this is not a requirement)
|
|
16
|
-
|
|
17
|
-
------------------------------
|
|
18
|
-
|
|
19
|
-
• Website : www.kenney.nl
|
|
20
|
-
• Donate : www.kenney.nl/donate
|
|
21
|
-
|
|
22
|
-
• Patreon : patreon.com/kenney
|
|
23
|
-
|
|
24
|
-
Follow on social media for updates:
|
|
25
|
-
|
|
26
|
-
• Twitter: twitter.com/KenneyNL
|
|
27
|
-
• Instagram: instagram.com/kenney_nl
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
Mini Characters (1.0)
|
|
4
|
+
|
|
5
|
+
Created/distributed by Kenney (www.kenney.nl)
|
|
6
|
+
Creation date: 17-07-2024 11:31
|
|
7
|
+
|
|
8
|
+
------------------------------
|
|
9
|
+
|
|
10
|
+
License: (Creative Commons Zero, CC0)
|
|
11
|
+
http://creativecommons.org/publicdomain/zero/1.0/
|
|
12
|
+
|
|
13
|
+
You can use this content for personal, educational, and commercial purposes.
|
|
14
|
+
|
|
15
|
+
Support by crediting 'Kenney' or 'www.kenney.nl' (this is not a requirement)
|
|
16
|
+
|
|
17
|
+
------------------------------
|
|
18
|
+
|
|
19
|
+
• Website : www.kenney.nl
|
|
20
|
+
• Donate : www.kenney.nl/donate
|
|
21
|
+
|
|
22
|
+
• Patreon : patreon.com/kenney
|
|
23
|
+
|
|
24
|
+
Follow on social media for updates:
|
|
25
|
+
|
|
26
|
+
• Twitter: twitter.com/KenneyNL
|
|
27
|
+
• Instagram: instagram.com/kenney_nl
|
|
28
28
|
• Mastodon: mastodon.gamedev.place/@kenney
|
package/client.js
CHANGED
|
@@ -5,6 +5,21 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
// 面板页面由本插件自己的静态服务提供(不是用户内容),需要同源才能读写本地偏好与同源 API。
|
|
6
6
|
// 其余权限(弹窗、顶层跳转、表单、下载、插件、指针锁定、模态框)保持沙箱默认拒绝。
|
|
7
7
|
const FRAME_SANDBOX = 'allow-scripts allow-same-origin';
|
|
8
|
+
const OFFICE_BASE = '/dsh-mutipleagent/';
|
|
9
|
+
const VIEW_ID = 'dsh-mutipleagent';
|
|
10
|
+
const VIEW_LABEL = '多 Agent 办公室';
|
|
11
|
+
// 会话内视图直接带上当前会话 id:办公室据此锁定 rootId,不再要求用户先选目录和会话。
|
|
12
|
+
// rev 取健康检查里的插件版本:升级后地址随之变化,停留在标签页里的旧 iframe 会自动重载,
|
|
13
|
+
// 不必等用户手动刷新整个 Harness 页面(CSS/脚本更新也能立刻生效)。
|
|
14
|
+
function officeUrl(sessionId, rev) {
|
|
15
|
+
const params = new URLSearchParams({ mode: 'live', source: 'harness' });
|
|
16
|
+
if (sessionId) {
|
|
17
|
+
params.set('rootId', sessionId);
|
|
18
|
+
params.set('embed', '1');
|
|
19
|
+
}
|
|
20
|
+
if (rev) params.set('rev', rev);
|
|
21
|
+
return OFFICE_BASE + '?' + params.toString();
|
|
22
|
+
}
|
|
8
23
|
function OfficeIcon({ size = 18 }) {
|
|
9
24
|
return h(
|
|
10
25
|
'svg',
|
|
@@ -12,23 +27,27 @@ window.__ModuleLoader__.load({
|
|
|
12
27
|
h('path', { d: 'M3 10V5h6v5H3Zm12 0V5h6v5h-6ZM9 21v-5h6v5H9ZM6 10v3h12v-3m-6 3v3' }),
|
|
13
28
|
);
|
|
14
29
|
}
|
|
15
|
-
|
|
30
|
+
// 面板与会话视图共用同一套连接状态机:先探活,再挂 iframe。
|
|
31
|
+
function OfficeFrame({ sessionId, className }) {
|
|
16
32
|
const [version, setVersion] = useState(0);
|
|
17
33
|
const [error, setError] = useState('');
|
|
18
34
|
const [ready, setReady] = useState(false);
|
|
19
|
-
const
|
|
35
|
+
const [rev, setRev] = useState('');
|
|
36
|
+
const url = officeUrl(sessionId, rev);
|
|
20
37
|
useEffect(() => {
|
|
21
38
|
let active = true;
|
|
22
39
|
const controller = new AbortController();
|
|
23
40
|
setError('');
|
|
24
41
|
setReady(false);
|
|
25
|
-
fetch('
|
|
42
|
+
fetch(OFFICE_BASE + 'api/health', { signal: controller.signal })
|
|
26
43
|
.then(response => {
|
|
27
44
|
if (!response.ok) throw Error('办公室暂不可用(HTTP ' + response.status + '),请确认插件已启用后重试。');
|
|
28
45
|
return response.json();
|
|
29
46
|
})
|
|
30
|
-
.then(
|
|
31
|
-
if (active)
|
|
47
|
+
.then(data => {
|
|
48
|
+
if (!active) return;
|
|
49
|
+
setRev(String(data?.version ?? ''));
|
|
50
|
+
setReady(true);
|
|
32
51
|
})
|
|
33
52
|
.catch(error => {
|
|
34
53
|
if (active && error.name !== 'AbortError') setError(error.message);
|
|
@@ -37,7 +56,7 @@ window.__ModuleLoader__.load({
|
|
|
37
56
|
active = false;
|
|
38
57
|
controller.abort();
|
|
39
58
|
};
|
|
40
|
-
}, [version]);
|
|
59
|
+
}, [version, sessionId]);
|
|
41
60
|
if (error)
|
|
42
61
|
return h(
|
|
43
62
|
'section',
|
|
@@ -47,7 +66,8 @@ window.__ModuleLoader__.load({
|
|
|
47
66
|
);
|
|
48
67
|
if (!ready) return h('p', { className: 'dsh-office-status', role: 'status' }, '正在连接多 Agent 办公室…');
|
|
49
68
|
return h('iframe', {
|
|
50
|
-
|
|
69
|
+
key: url,
|
|
70
|
+
className,
|
|
51
71
|
src: url,
|
|
52
72
|
title: 'DeepSeek Harness 多 Agent 协作办公室',
|
|
53
73
|
sandbox: FRAME_SANDBOX,
|
|
@@ -55,13 +75,30 @@ window.__ModuleLoader__.load({
|
|
|
55
75
|
allow: 'clipboard-write',
|
|
56
76
|
});
|
|
57
77
|
}
|
|
78
|
+
function OfficePage() {
|
|
79
|
+
return h(OfficeFrame, { className: 'dsh-office-frame' });
|
|
80
|
+
}
|
|
81
|
+
// 会话内标签:sessionId 由 conversation.view 的 inject(sessionId) 注入,切换会话即换 iframe。
|
|
82
|
+
// 标签页在屏时给 body 打标记,宿主据此收起底部输入框,把整块高度让给办公室画面;离开标签即恢复。
|
|
83
|
+
let officeViewCount = 0;
|
|
84
|
+
function OfficeView({ sessionId }) {
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
officeViewCount += 1;
|
|
87
|
+
document.body.dataset.officeView = 'active';
|
|
88
|
+
return () => {
|
|
89
|
+
officeViewCount = Math.max(0, officeViewCount - 1);
|
|
90
|
+
if (officeViewCount === 0) delete document.body.dataset.officeView;
|
|
91
|
+
};
|
|
92
|
+
}, []);
|
|
93
|
+
return h('div', { className: 'dsh-office-view' }, h(OfficeFrame, { sessionId, className: 'dsh-office-frame' }));
|
|
94
|
+
}
|
|
58
95
|
return {
|
|
59
96
|
inject: ['slots'],
|
|
60
97
|
apply(ctx) {
|
|
61
98
|
ctx.effect(() => {
|
|
62
99
|
const style = document.createElement('style');
|
|
63
100
|
style.textContent =
|
|
64
|
-
'.dsh-office-frame{display:block;width:100%;height:100%;min-height:0;border:0;background:#edf3fc}.dsh-office-status{padding:24px;color:var(--dsw-alias-label-primary);font:inherit}.dsh-office-status button{font:inherit;color:inherit;background:var(--dsw-alias-button-elevated-fill);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 16px;cursor:pointer}.dsh-office-status button:focus-visible{outline:2px solid var(--dsw-focus-ring-color);outline-offset:2px}';
|
|
101
|
+
'.dsh-office-frame{display:block;width:100%;height:100%;min-height:0;border:0;background:#edf3fc}.dsh-office-view{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;min-width:0;height:100%;width:100%;background:#edf3fc}.dsh-office-view>.dsh-office-frame{flex:1 1 auto;height:auto}body[data-office-view=active] [data-conversation-region=composer]{display:none!important}.dsh-office-status{padding:24px;color:var(--dsw-alias-label-primary);font:inherit}.dsh-office-status button{font:inherit;color:inherit;background:var(--dsw-alias-button-elevated-fill);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 16px;cursor:pointer}.dsh-office-status button:focus-visible{outline:2px solid var(--dsw-focus-ring-color);outline-offset:2px}';
|
|
65
102
|
document.head.appendChild(style);
|
|
66
103
|
return () => style.remove();
|
|
67
104
|
}, 'dsh-mutipleagent: panel styles');
|
|
@@ -69,6 +106,23 @@ window.__ModuleLoader__.load({
|
|
|
69
106
|
() => ctx.slots.inject('main', () => ctx.slots.register({ name: 'main', key: 'dsh-mutipleagent' }, OfficePage)),
|
|
70
107
|
'dsh-mutipleagent: main panel',
|
|
71
108
|
);
|
|
109
|
+
// 会话页顶部标签行(对话 / 轨迹 / 上下文 / …):在具体会话里直接可用的入口。
|
|
110
|
+
ctx.effect(
|
|
111
|
+
() =>
|
|
112
|
+
ctx.slots.inject('conversation.view', () =>
|
|
113
|
+
ctx.slots.register(
|
|
114
|
+
{
|
|
115
|
+
name: 'conversation.view',
|
|
116
|
+
id: VIEW_ID,
|
|
117
|
+
order: 30,
|
|
118
|
+
label: () => VIEW_LABEL,
|
|
119
|
+
inject: sessionId => ({ sessionId }),
|
|
120
|
+
},
|
|
121
|
+
OfficeView,
|
|
122
|
+
),
|
|
123
|
+
),
|
|
124
|
+
'dsh-mutipleagent: conversation view',
|
|
125
|
+
);
|
|
72
126
|
ctx.effect(
|
|
73
127
|
() =>
|
|
74
128
|
ctx.slots.inject('sidebar.panellist', () =>
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mutipleagent",
|
|
3
3
|
"displayName": "DSH 多 Agent 办公室",
|
|
4
|
-
"version": "0.
|
|
5
|
-
"description": "DeepSeek Harness 多 Agent 3D
|
|
4
|
+
"version": "0.4.0",
|
|
5
|
+
"description": "DeepSeek Harness 多 Agent 3D 协作办公室插件:在会话页顶部标签行直接打开当前会话的 3D 办公室,观察真实主会话、子 Agent、工具活动与汇报,支持目录/会话切换和跨目录搜索。",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"icon": "./assets/agents/dsh.svg",
|
|
8
8
|
"engines": {
|
|
@@ -56,6 +56,8 @@
|
|
|
56
56
|
"platform": "web",
|
|
57
57
|
"inject": [
|
|
58
58
|
"@deepseek-ai/dsh-client-ui-layout",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-chat",
|
|
60
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
59
61
|
"@deepseek-ai/dsh-client-ui-sidebar"
|
|
60
62
|
]
|
|
61
63
|
}
|
package/public/app.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{s as J,u as a,v as F,w as S,x as v,y as P,z as U}from"./chunks/chunk-RQUUGNU7.js";var e=t=>document.getElementById(t),O={assign:"\u2197",message:"\u2194",wait:"\u25F7",report:"\u2199",complete:"\u2713",error:"!",interrupted:"\u2161",close:"\xD7",approval:"?"},st=new Set(["thinking","tool","typing"]),ct=new Set(["done","error","interrupted","closed"]),r=null,g=null,I=null,u=null,A=0,W=new Set,k=!0,B=null,q="activity",w="",j=!1,K=Date.now(),E="",C="",L="",b="",_=!1,$=null,N=new Map,z=t=>{try{return localStorage.getItem(t)}catch{return null}},G=(t,n)=>{try{localStorage.setItem(t,n)}catch{}},M=t=>t?new Date(t).toLocaleTimeString(F(),{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit"}):"\u2014",c=(t,n,i)=>{let p=document.createElement(t);return n&&(p.className=n),i!=null&&(p.textContent=i),p},h=t=>c("div","empty",t),y=()=>r?.agents.find(t=>t.id===g),dt=t=>c("span","state-dot "+t),x=t=>r?.agents.find(n=>n.id===t)?.name||a("common.agent");function Q(t){e("notice").textContent=t,e("notice").hidden=!t}function T(t){e("connection").classList.toggle("connected",t),e("connection").textContent=t?a("topbar.connected"):a("topbar.reconnecting")}function V(t,n=!1){return c("span","agent-avatar"+(t.id===r.rootId?" lead":""),t.id===r.rootId?"\u25C8":n?"\u25C7":String(r.agents.indexOf(t)).padStart(2,"0"))}function D(t,n=!1){g=t,b="",C="",w="",u&&(u.selectedId=t,n&&u.focus(t)),X(),Z()}function pt(){let t=JSON.stringify(r.roots);if(t===E)return;E=t;let n=e("task-select");n.replaceChildren();for(let i of r.roots){let p=c("option","",i.title.slice(0,80));p.value=i.id,n.append(p)}r.roots.length||n.append(c("option","",a("sidebar.noSessions"))),n.value=r.rootId||""}function X(){if(!r)return;let t=JSON.stringify([g,r.agents.map(o=>[o.id,o.name,o.status,o.parentId,o.relationship])]);if(C===t)return;C=t;let n=e("team-tree");n.replaceChildren(),e("team-count").textContent=r.agents.length;let i=[],p=(o,s)=>{let d=r.agents.find(l=>l.id===o);if(!(!d||i.some(l=>l.s.id===o))){i.push({s:d,depth:s});for(let l of r.agents.filter(f=>f.parentId===o))p(l.id,s+1)}};p(r.rootId,0);for(let o of r.agents)i.some(s=>s.s.id===o.id)||i.push({s:o,depth:0});for(let{s:o,depth:s}of i){let d=c("button","agent-card"+(s?" child":"")+(o.id===g?" selected":""));d.type="button",d.setAttribute("aria-pressed",o.id===g),d.setAttribute("aria-label",a("app.ariaAgentStatus",{name:o.name,status:S(o.status)}));let l=c("div","agent-card-body"),f=c("div","agent-card-name");f.append(c("span","",o.name),dt(o.status)),l.append(f,c("div","agent-card-role",(s>1?"\u21B3 ".repeat(Math.min(3,s-1)):"")+(o.role||(s?a("app.roleSubAgent"):a("app.roleLead")))+" \xB7 "+S(o.status))),d.append(V(o),l),d.onclick=()=>D(o.id,!0),n.append(d)}i.length||n.append(h(a("app.teamEmpty")))}function lt(){let t=r.events.filter(s=>O[s.type]).slice(-40),n=JSON.stringify(t);if(n===L)return;L=n;let i=e("timeline"),p=i.scrollLeft,o=i.scrollWidth-i.clientWidth-p<30;i.replaceChildren(),e("event-count").textContent=t.length?a("main.recentEvents",{count:t.length}):a("main.noEvents");for(let s of t){let d=c("button","event-card "+s.type),l=c("div","event-card-top"),f=c("span","event-type");f.append(c("i","",O[s.type]),document.createTextNode(v(s.type))),l.append(f,c("time","",M(s.at)));let m=s.type==="assign"||s.type==="message"?x(s.sessionId)+" \u2192 "+x(s.targetId):s.type==="wait"?a("app.waitingResult",{name:x(s.sessionId)}):s.preview||x(s.sessionId)+" \xB7 "+v(s.type);d.append(l,c("p","",m)),d.onclick=()=>{D(s.sessionId,!0),s.hasText&&R(s)},d.title=m,i.append(d)}t.length||i.append(h(a("main.timelineEmpty"))),i.scrollLeft=o||k?i.scrollWidth:p}function Y(t){if(!t?.turnStartedAt)return"\u2014";let n=Math.max(0,Math.floor(((ct.has(t.status)?t.endedAt:Date.now())-t.turnStartedAt)/1e3));return n>=3600?a("app.durationHours",{hours:Math.floor(n/3600),minutes:Math.floor(n%3600/60)}):a("app.durationMinutes",{minutes:Math.floor(n/60),seconds:n%60})}function Z(){let t=y();if(!t){e("agent-header").replaceChildren(h(a("details.selectAgent"))),e("agent-metadata").replaceChildren(),e("agent-task").textContent=a("details.noTask"),e("agent-activity").replaceChildren(),e("open-task").disabled=!0,e("report-content").replaceChildren(h(a("details.noReport")));return}let n=JSON.stringify([t.id,t.status,t.model,t.role,t.relationship,t.task,t.events,t.reports,t.reportEventId]);if(n===b)return;b=n;let i=c("div");i.append(c("h3","",t.name),c("p","",t.role||(t.parentId?a("app.roleSplit"):a("app.roleLeadTask")))),e("agent-header").replaceChildren(V(t,!0),i);let p=[["status-pill",a("app.metadataStatus"),S(t.status)],["elapsed",a("app.metadataElapsed"),Y(t)],["",a("app.metadataModel"),t.model||a("common.notRecorded")],["",a("app.metadataRelation"),t.relationship==="conflict"?a("app.relationConflict"):t.parentId?x(t.parentId):a("app.relationMain")]];e("agent-metadata").replaceChildren(...p.map(([d,l,f])=>{let m=c("div","metadata-row");return m.append(c("span","",l),c("span",d,f)),m})),e("agent-task").textContent=t.task||a("app.noTaskLogged"),e("open-task").disabled=!t.taskEventId;let o=e("agent-activity");o.replaceChildren();let s=t.events.filter(d=>d.type!=="task").slice(-16).reverse();for(let d of s){let l=c("div","activity-item");if(l.append(c("time","",M(d.at)+" \xB7 "+v(d.type)),c("p","",d.preview||v(d.type))),d.hasText){let f=c("button","",a("details.viewRecord"));f.onclick=()=>R(d),l.append(f)}o.append(l)}s.length||o.append(h(a("details.noActivity"))),q==="report"&&et(t)}async function tt(t,n){let i=t+":"+n;if(N.has(i))return N.get(i);let p=await fetch("./api/report?"+new URLSearchParams({sessionId:t,eventId:n})),o=await p.json();if(!p.ok)throw new Error(o.error||a("app.readFailed"));return N.set(i,o.text),o.text}async function R(t){e("dialog-title").textContent=(O[t.type]?v(t.type):a("dialog.title"))+" \xB7 "+x(t.sessionId),e("dialog-body").textContent=a("dialog.loading"),e("copy-text").textContent=a("dialog.copy"),e("text-dialog").open||e("text-dialog").showModal();let n=t.id;e("text-dialog").dataset.eventId=n;try{let i=await tt(t.sessionId,t.id);e("text-dialog").dataset.eventId===n&&(e("dialog-body").textContent=i||a("dialog.emptyBody"))}catch(i){e("text-dialog").dataset.eventId===n&&(e("dialog-body").textContent=i.message)}}async function et(t){let n=t.reports||[],i=t.id+":"+n.join(",");if(i===w)return;w=i;let p=e("report-content");if(p.replaceChildren(),!n.length){p.append(h(a("app.reportEmpty")));return}p.append(h(a("app.reportLoading")));let o=await Promise.all(n.slice(-8).reverse().map(async s=>{try{return{id:s,text:await tt(t.id,s)}}catch(d){return{id:s,error:d.message}}}));if(!(y()?.id!==t.id||i!==w)){p.replaceChildren();for(let s of o){let d=c("section","report-block"),l=t.events.find(rt=>rt.id===s.id),f=c("header");f.append(c("span","",(s.id===t.reportEventId?a("app.reportCurrent"):a("app.reportHistory"))+(t.parentId?a("app.reportSub"):a("app.reportLead"))),c("time","",M(l?.at))),d.append(f,c("pre",s.error?"error-text":"",s.error||s.text));let m=c("button","report-open",a("app.reportOpen"));m.onclick=()=>R({id:s.id,sessionId:t.id,type:"report"}),d.append(m),p.append(d)}n.length>8&&p.append(c("p","muted",a("app.reportLimit")))}}function nt(t){q=t;for(let n of["activity","report"]){let i=t===n;e(n+"-tab").classList.toggle("active",i),e(n+"-tab").setAttribute("aria-selected",i),e(n+"-panel").hidden=!i}t==="report"&&y()&&et(y())}async function ut(){if(!u||r.agents.length>A){u?.destroy(),A=Math.max(7,Math.ceil(r.agents.length/2)*2+1),e("scene-status").hidden=!1;try{let t=new J(e("scene"),{count:A,reduced:e("reduced-motion").checked,onSelect:i=>D(i)});u=t,t.setTeam(r.agents,r.rootId),t.selectedId=g;let n=await t.ready;if(u!==t)return;e("scene-status").hidden=!!n,n||(e("scene-status").textContent=a("main.sceneUnavailable"),t.visible=!1)}catch{e("scene-status").hidden=!1,e("scene-status").textContent=a("main.sceneFailed")}}else u.setTeam(r.agents,r.rootId),u.selectedId=g}function ot(t){let n=t.rootId!==B;r=t,n&&(B=r.rootId,g=r.rootId,k=!0,C="",L="",b="",w=""),r.agents.some(o=>o.id===g)||(g=r.rootId),e("mode").textContent=r.mode==="demo"?a("app.demoMode"):a("topbar.readonly"),e("mode").classList.toggle("demo",r.mode==="demo");let i=r.agents.find(o=>o.id===r.rootId);e("project-path").textContent=i?.cwd||"",e("project-path").title=i?.cwd||"",e("task-title").textContent=i?.task||a("main.subtitle"),e("task-title").title=i?.task||"",e("working-count").textContent=r.agents.filter(o=>st.has(o.status)).length,e("done-count").textContent=r.agents.filter(o=>o.status==="done").length,e("waiting-count").textContent=r.agents.filter(o=>o.status==="waiting"||o.status==="waiting_agents").length,Q(r.mode==="demo"?a("app.demoNotice"):r.warnings.join(a("common.listSeparator"))||(r.rootId?"":a("app.noRecentSessions"))),pt(),X(),lt(),Z(),ut();let p=r.agents.some(o=>o.loading);if(!k&&!j&&!p&&u)for(let o of r.events)!W.has(o.id)&&o.at>=K&&u.animateEvent(o);j=p,W=new Set(r.events.map(o=>o.id)),k=!1,G("codex-office-root",r.rootId||"")}function at(t){I?.close(),k=!0,j=!0,K=Date.now();let n=new EventSource("./events"+(t?"?"+new URLSearchParams({rootId:t}):""));I=n,n.onopen=()=>{k=!0,T(!0)},n.onmessage=i=>{I===n&&(T(!0),ot(JSON.parse(i.data)))},n.onerror=()=>{I===n&&T(!1)}}async function it(){try{let t=await fetch("./api/sessions");if(!t.ok)throw new Error(a("app.editFailed"));let n=await t.json(),i=z("codex-office-root"),p=n.roots.find(o=>o.id===i)?.id||n.rootId;at(p)}catch(t){T(!1),Q(a("app.retrying",{message:t.message})),_||($=setTimeout(it,2500))}}e("task-select").onchange=t=>{E="",at(t.target.value)};e("reset-camera").onclick=()=>u?.resetCamera();e("activity-tab").onclick=()=>nt("activity");e("report-tab").onclick=()=>nt("report");e("open-task").onclick=()=>{let t=y();t?.taskEventId&&R({id:t.taskEventId,sessionId:t.taskSessionId||t.id,type:"task"})};e("close-dialog").onclick=()=>e("text-dialog").close();e("copy-text").onclick=async()=>{try{await navigator.clipboard.writeText(e("dialog-body").textContent),e("copy-text").textContent=a("common.copied")}catch{e("copy-text").textContent=a("common.copyFailed")}};var H=z("codex-office-reduced");e("reduced-motion").checked=H==null?matchMedia("(prefers-reduced-motion: reduce)").matches:H==="true";e("reduced-motion").onchange=t=>{G("codex-office-reduced",t.target.checked),u?.setReduced(t.target.checked)};var ft=setInterval(()=>{e("clock").textContent=M(Date.now());let t=document.querySelector(".elapsed");t&&y()&&(t.textContent=Y(y()))},1e3);document.addEventListener("visibilitychange",()=>{u&&(u.visible=!document.hidden&&!u.loadError)});P(()=>{E="",C="",L="",b="",w="",r&&ot(r)});U(document);window.addEventListener("beforeunload",()=>{_=!0,clearInterval(ft),clearTimeout($),I?.close(),u?.destroy()});it();
|
|
1
|
+
import{s as J,u as a,v as F,w as S,x as v,y as P,z as U}from"./chunks/chunk-MU3LEDFF.js";var e=t=>document.getElementById(t),O={assign:"\u2197",message:"\u2194",wait:"\u25F7",report:"\u2199",complete:"\u2713",error:"!",interrupted:"\u2161",close:"\xD7",approval:"?"},st=new Set(["thinking","tool","typing"]),ct=new Set(["done","error","interrupted","closed"]),r=null,g=null,I=null,u=null,A=0,W=new Set,k=!0,B=null,q="activity",w="",j=!1,K=Date.now(),E="",C="",L="",b="",_=!1,$=null,N=new Map,z=t=>{try{return localStorage.getItem(t)}catch{return null}},G=(t,n)=>{try{localStorage.setItem(t,n)}catch{}},M=t=>t?new Date(t).toLocaleTimeString(F(),{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit"}):"\u2014",c=(t,n,i)=>{let p=document.createElement(t);return n&&(p.className=n),i!=null&&(p.textContent=i),p},h=t=>c("div","empty",t),y=()=>r?.agents.find(t=>t.id===g),dt=t=>c("span","state-dot "+t),x=t=>r?.agents.find(n=>n.id===t)?.name||a("common.agent");function Q(t){e("notice").textContent=t,e("notice").hidden=!t}function T(t){e("connection").classList.toggle("connected",t),e("connection").textContent=t?a("topbar.connected"):a("topbar.reconnecting")}function V(t,n=!1){return c("span","agent-avatar"+(t.id===r.rootId?" lead":""),t.id===r.rootId?"\u25C8":n?"\u25C7":String(r.agents.indexOf(t)).padStart(2,"0"))}function D(t,n=!1){g=t,b="",C="",w="",u&&(u.selectedId=t,n&&u.focus(t)),X(),Z()}function pt(){let t=JSON.stringify(r.roots);if(t===E)return;E=t;let n=e("task-select");n.replaceChildren();for(let i of r.roots){let p=c("option","",i.title.slice(0,80));p.value=i.id,n.append(p)}r.roots.length||n.append(c("option","",a("sidebar.noSessions"))),n.value=r.rootId||""}function X(){if(!r)return;let t=JSON.stringify([g,r.agents.map(o=>[o.id,o.name,o.status,o.parentId,o.relationship])]);if(C===t)return;C=t;let n=e("team-tree");n.replaceChildren(),e("team-count").textContent=r.agents.length;let i=[],p=(o,s)=>{let d=r.agents.find(l=>l.id===o);if(!(!d||i.some(l=>l.s.id===o))){i.push({s:d,depth:s});for(let l of r.agents.filter(f=>f.parentId===o))p(l.id,s+1)}};p(r.rootId,0);for(let o of r.agents)i.some(s=>s.s.id===o.id)||i.push({s:o,depth:0});for(let{s:o,depth:s}of i){let d=c("button","agent-card"+(s?" child":"")+(o.id===g?" selected":""));d.type="button",d.setAttribute("aria-pressed",o.id===g),d.setAttribute("aria-label",a("app.ariaAgentStatus",{name:o.name,status:S(o.status)}));let l=c("div","agent-card-body"),f=c("div","agent-card-name");f.append(c("span","",o.name),dt(o.status)),l.append(f,c("div","agent-card-role",(s>1?"\u21B3 ".repeat(Math.min(3,s-1)):"")+(o.role||(s?a("app.roleSubAgent"):a("app.roleLead")))+" \xB7 "+S(o.status))),d.append(V(o),l),d.onclick=()=>D(o.id,!0),n.append(d)}i.length||n.append(h(a("app.teamEmpty")))}function lt(){let t=r.events.filter(s=>O[s.type]).slice(-40),n=JSON.stringify(t);if(n===L)return;L=n;let i=e("timeline"),p=i.scrollLeft,o=i.scrollWidth-i.clientWidth-p<30;i.replaceChildren(),e("event-count").textContent=t.length?a("main.recentEvents",{count:t.length}):a("main.noEvents");for(let s of t){let d=c("button","event-card "+s.type),l=c("div","event-card-top"),f=c("span","event-type");f.append(c("i","",O[s.type]),document.createTextNode(v(s.type))),l.append(f,c("time","",M(s.at)));let m=s.type==="assign"||s.type==="message"?x(s.sessionId)+" \u2192 "+x(s.targetId):s.type==="wait"?a("app.waitingResult",{name:x(s.sessionId)}):s.preview||x(s.sessionId)+" \xB7 "+v(s.type);d.append(l,c("p","",m)),d.onclick=()=>{D(s.sessionId,!0),s.hasText&&R(s)},d.title=m,i.append(d)}t.length||i.append(h(a("main.timelineEmpty"))),i.scrollLeft=o||k?i.scrollWidth:p}function Y(t){if(!t?.turnStartedAt)return"\u2014";let n=Math.max(0,Math.floor(((ct.has(t.status)?t.endedAt:Date.now())-t.turnStartedAt)/1e3));return n>=3600?a("app.durationHours",{hours:Math.floor(n/3600),minutes:Math.floor(n%3600/60)}):a("app.durationMinutes",{minutes:Math.floor(n/60),seconds:n%60})}function Z(){let t=y();if(!t){e("agent-header").replaceChildren(h(a("details.selectAgent"))),e("agent-metadata").replaceChildren(),e("agent-task").textContent=a("details.noTask"),e("agent-activity").replaceChildren(),e("open-task").disabled=!0,e("report-content").replaceChildren(h(a("details.noReport")));return}let n=JSON.stringify([t.id,t.status,t.model,t.role,t.relationship,t.task,t.events,t.reports,t.reportEventId]);if(n===b)return;b=n;let i=c("div");i.append(c("h3","",t.name),c("p","",t.role||(t.parentId?a("app.roleSplit"):a("app.roleLeadTask")))),e("agent-header").replaceChildren(V(t,!0),i);let p=[["status-pill",a("app.metadataStatus"),S(t.status)],["elapsed",a("app.metadataElapsed"),Y(t)],["",a("app.metadataModel"),t.model||a("common.notRecorded")],["",a("app.metadataRelation"),t.relationship==="conflict"?a("app.relationConflict"):t.parentId?x(t.parentId):a("app.relationMain")]];e("agent-metadata").replaceChildren(...p.map(([d,l,f])=>{let m=c("div","metadata-row");return m.append(c("span","",l),c("span",d,f)),m})),e("agent-task").textContent=t.task||a("app.noTaskLogged"),e("open-task").disabled=!t.taskEventId;let o=e("agent-activity");o.replaceChildren();let s=t.events.filter(d=>d.type!=="task").slice(-16).reverse();for(let d of s){let l=c("div","activity-item");if(l.append(c("time","",M(d.at)+" \xB7 "+v(d.type)),c("p","",d.preview||v(d.type))),d.hasText){let f=c("button","",a("details.viewRecord"));f.onclick=()=>R(d),l.append(f)}o.append(l)}s.length||o.append(h(a("details.noActivity"))),q==="report"&&et(t)}async function tt(t,n){let i=t+":"+n;if(N.has(i))return N.get(i);let p=await fetch("./api/report?"+new URLSearchParams({sessionId:t,eventId:n})),o=await p.json();if(!p.ok)throw new Error(o.error||a("app.readFailed"));return N.set(i,o.text),o.text}async function R(t){e("dialog-title").textContent=(O[t.type]?v(t.type):a("dialog.title"))+" \xB7 "+x(t.sessionId),e("dialog-body").textContent=a("dialog.loading"),e("copy-text").textContent=a("dialog.copy"),e("text-dialog").open||e("text-dialog").showModal();let n=t.id;e("text-dialog").dataset.eventId=n;try{let i=await tt(t.sessionId,t.id);e("text-dialog").dataset.eventId===n&&(e("dialog-body").textContent=i||a("dialog.emptyBody"))}catch(i){e("text-dialog").dataset.eventId===n&&(e("dialog-body").textContent=i.message)}}async function et(t){let n=t.reports||[],i=t.id+":"+n.join(",");if(i===w)return;w=i;let p=e("report-content");if(p.replaceChildren(),!n.length){p.append(h(a("app.reportEmpty")));return}p.append(h(a("app.reportLoading")));let o=await Promise.all(n.slice(-8).reverse().map(async s=>{try{return{id:s,text:await tt(t.id,s)}}catch(d){return{id:s,error:d.message}}}));if(!(y()?.id!==t.id||i!==w)){p.replaceChildren();for(let s of o){let d=c("section","report-block"),l=t.events.find(rt=>rt.id===s.id),f=c("header");f.append(c("span","",(s.id===t.reportEventId?a("app.reportCurrent"):a("app.reportHistory"))+(t.parentId?a("app.reportSub"):a("app.reportLead"))),c("time","",M(l?.at))),d.append(f,c("pre",s.error?"error-text":"",s.error||s.text));let m=c("button","report-open",a("app.reportOpen"));m.onclick=()=>R({id:s.id,sessionId:t.id,type:"report"}),d.append(m),p.append(d)}n.length>8&&p.append(c("p","muted",a("app.reportLimit")))}}function nt(t){q=t;for(let n of["activity","report"]){let i=t===n;e(n+"-tab").classList.toggle("active",i),e(n+"-tab").setAttribute("aria-selected",i),e(n+"-panel").hidden=!i}t==="report"&&y()&&et(y())}async function ut(){if(!u||r.agents.length>A){u?.destroy(),A=Math.max(7,Math.ceil(r.agents.length/2)*2+1),e("scene-status").hidden=!1;try{let t=new J(e("scene"),{count:A,reduced:e("reduced-motion").checked,onSelect:i=>D(i)});u=t,t.setTeam(r.agents,r.rootId),t.selectedId=g;let n=await t.ready;if(u!==t)return;e("scene-status").hidden=!!n,n||(e("scene-status").textContent=a("main.sceneUnavailable"),t.visible=!1)}catch{e("scene-status").hidden=!1,e("scene-status").textContent=a("main.sceneFailed")}}else u.setTeam(r.agents,r.rootId),u.selectedId=g}function ot(t){let n=t.rootId!==B;r=t,n&&(B=r.rootId,g=r.rootId,k=!0,C="",L="",b="",w=""),r.agents.some(o=>o.id===g)||(g=r.rootId),e("mode").textContent=r.mode==="demo"?a("app.demoMode"):a("topbar.readonly"),e("mode").classList.toggle("demo",r.mode==="demo");let i=r.agents.find(o=>o.id===r.rootId);e("project-path").textContent=i?.cwd||"",e("project-path").title=i?.cwd||"",e("task-title").textContent=i?.task||a("main.subtitle"),e("task-title").title=i?.task||"",e("working-count").textContent=r.agents.filter(o=>st.has(o.status)).length,e("done-count").textContent=r.agents.filter(o=>o.status==="done").length,e("waiting-count").textContent=r.agents.filter(o=>o.status==="waiting"||o.status==="waiting_agents").length,Q(r.mode==="demo"?a("app.demoNotice"):r.warnings.join(a("common.listSeparator"))||(r.rootId?"":a("app.noRecentSessions"))),pt(),X(),lt(),Z(),ut();let p=r.agents.some(o=>o.loading);if(!k&&!j&&!p&&u)for(let o of r.events)!W.has(o.id)&&o.at>=K&&u.animateEvent(o);j=p,W=new Set(r.events.map(o=>o.id)),k=!1,G("codex-office-root",r.rootId||"")}function at(t){I?.close(),k=!0,j=!0,K=Date.now();let n=new EventSource("./events"+(t?"?"+new URLSearchParams({rootId:t}):""));I=n,n.onopen=()=>{k=!0,T(!0)},n.onmessage=i=>{I===n&&(T(!0),ot(JSON.parse(i.data)))},n.onerror=()=>{I===n&&T(!1)}}async function it(){try{let t=await fetch("./api/sessions");if(!t.ok)throw new Error(a("app.editFailed"));let n=await t.json(),i=z("codex-office-root"),p=n.roots.find(o=>o.id===i)?.id||n.rootId;at(p)}catch(t){T(!1),Q(a("app.retrying",{message:t.message})),_||($=setTimeout(it,2500))}}e("task-select").onchange=t=>{E="",at(t.target.value)};e("reset-camera").onclick=()=>u?.resetCamera();e("activity-tab").onclick=()=>nt("activity");e("report-tab").onclick=()=>nt("report");e("open-task").onclick=()=>{let t=y();t?.taskEventId&&R({id:t.taskEventId,sessionId:t.taskSessionId||t.id,type:"task"})};e("close-dialog").onclick=()=>e("text-dialog").close();e("copy-text").onclick=async()=>{try{await navigator.clipboard.writeText(e("dialog-body").textContent),e("copy-text").textContent=a("common.copied")}catch{e("copy-text").textContent=a("common.copyFailed")}};var H=z("codex-office-reduced");e("reduced-motion").checked=H==null?matchMedia("(prefers-reduced-motion: reduce)").matches:H==="true";e("reduced-motion").onchange=t=>{G("codex-office-reduced",t.target.checked),u?.setReduced(t.target.checked)};var ft=setInterval(()=>{e("clock").textContent=M(Date.now());let t=document.querySelector(".elapsed");t&&y()&&(t.textContent=Y(y()))},1e3);document.addEventListener("visibilitychange",()=>{u&&(u.visible=!document.hidden&&!u.loadError)});P(()=>{E="",C="",L="",b="",w="",r&&ot(r)});U(document);window.addEventListener("beforeunload",()=>{_=!0,clearInterval(ft),clearTimeout($),I?.close(),u?.destroy()});it();
|