dsh-mutipleagent 0.0.0-stage → 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.
Files changed (138) hide show
  1. package/CHANGELOG.md +214 -0
  2. package/CONTRIBUTING.md +49 -0
  3. package/LICENSE +21 -0
  4. package/README.en.md +125 -0
  5. package/README.md +302 -2
  6. package/SECURITY.md +27 -0
  7. package/THIRD_PARTY_NOTICES.md +29 -0
  8. package/assets/agents/MAGPIE-LICENSE +21 -0
  9. package/assets/agents/README.md +47 -0
  10. package/assets/agents/antigravity.png +0 -0
  11. package/assets/agents/claude.png +0 -0
  12. package/assets/agents/codex.png +0 -0
  13. package/assets/agents/copilot.svg +1 -0
  14. package/assets/agents/dsh.svg +1 -0
  15. package/assets/agents/gemini.png +0 -0
  16. package/assets/agents/grok.svg +1 -0
  17. package/assets/agents/hermes.svg +1 -0
  18. package/assets/agents/kimi.svg +1 -0
  19. package/assets/agents/omp.svg +7 -0
  20. package/assets/agents/opencode.svg +1 -0
  21. package/assets/agents/pi.svg +1 -0
  22. package/assets/kenney/LICENSE-furniture-kit.txt +23 -0
  23. package/assets/kenney/LICENSE-mini-characters.txt +28 -0
  24. package/assets/kenney/characters/Textures/colormap.png +0 -0
  25. package/assets/kenney/characters/character-female-a.glb +0 -0
  26. package/assets/kenney/characters/character-female-b.glb +0 -0
  27. package/assets/kenney/characters/character-female-c.glb +0 -0
  28. package/assets/kenney/characters/character-female-d.glb +0 -0
  29. package/assets/kenney/characters/character-female-e.glb +0 -0
  30. package/assets/kenney/characters/character-female-f.glb +0 -0
  31. package/assets/kenney/characters/character-male-a.glb +0 -0
  32. package/assets/kenney/characters/character-male-b.glb +0 -0
  33. package/assets/kenney/characters/character-male-c.glb +0 -0
  34. package/assets/kenney/characters/character-male-d.glb +0 -0
  35. package/assets/kenney/characters/character-male-e.glb +0 -0
  36. package/assets/kenney/characters/character-male-f.glb +0 -0
  37. package/assets/kenney/furniture/benchCushion.glb +0 -0
  38. package/assets/kenney/furniture/bookcaseOpen.glb +0 -0
  39. package/assets/kenney/furniture/books.glb +0 -0
  40. package/assets/kenney/furniture/cabinetTelevision.glb +0 -0
  41. package/assets/kenney/furniture/cardboardBoxClosed.glb +0 -0
  42. package/assets/kenney/furniture/chairCushion.glb +0 -0
  43. package/assets/kenney/furniture/chairDesk.glb +0 -0
  44. package/assets/kenney/furniture/chairModernCushion.glb +0 -0
  45. package/assets/kenney/furniture/coatRackStanding.glb +0 -0
  46. package/assets/kenney/furniture/computerKeyboard.glb +0 -0
  47. package/assets/kenney/furniture/computerMouse.glb +0 -0
  48. package/assets/kenney/furniture/computerScreen.glb +0 -0
  49. package/assets/kenney/furniture/desk.glb +0 -0
  50. package/assets/kenney/furniture/doorway.glb +0 -0
  51. package/assets/kenney/furniture/kitchenBar.glb +0 -0
  52. package/assets/kenney/furniture/kitchenCabinet.glb +0 -0
  53. package/assets/kenney/furniture/kitchenCoffeeMachine.glb +0 -0
  54. package/assets/kenney/furniture/kitchenFridgeLarge.glb +0 -0
  55. package/assets/kenney/furniture/kitchenMicrowave.glb +0 -0
  56. package/assets/kenney/furniture/kitchenSink.glb +0 -0
  57. package/assets/kenney/furniture/lampRoundFloor.glb +0 -0
  58. package/assets/kenney/furniture/lampSquareTable.glb +0 -0
  59. package/assets/kenney/furniture/laptop.glb +0 -0
  60. package/assets/kenney/furniture/loungeChairRelax.glb +0 -0
  61. package/assets/kenney/furniture/loungeSofa.glb +0 -0
  62. package/assets/kenney/furniture/plantSmall1.glb +0 -0
  63. package/assets/kenney/furniture/plantSmall2.glb +0 -0
  64. package/assets/kenney/furniture/plantSmall3.glb +0 -0
  65. package/assets/kenney/furniture/pottedPlant.glb +0 -0
  66. package/assets/kenney/furniture/radio.glb +0 -0
  67. package/assets/kenney/furniture/rugRectangle.glb +0 -0
  68. package/assets/kenney/furniture/rugRound.glb +0 -0
  69. package/assets/kenney/furniture/sideTable.glb +0 -0
  70. package/assets/kenney/furniture/speaker.glb +0 -0
  71. package/assets/kenney/furniture/stoolBar.glb +0 -0
  72. package/assets/kenney/furniture/table.glb +0 -0
  73. package/assets/kenney/furniture/tableCoffee.glb +0 -0
  74. package/assets/kenney/furniture/tableRound.glb +0 -0
  75. package/assets/kenney/furniture/televisionModern.glb +0 -0
  76. package/assets/kenney/furniture/toaster.glb +0 -0
  77. package/assets/kenney/furniture/trashcan.glb +0 -0
  78. package/assets/kenney/furniture/wallWindow.glb +0 -0
  79. package/assets/logo.svg +12 -0
  80. package/assets/scene-icons/arcade.png +0 -0
  81. package/assets/scene-icons/cafe.png +0 -0
  82. package/assets/scene-icons/castle.png +0 -0
  83. package/assets/scene-icons/christmas.png +0 -0
  84. package/assets/scene-icons/graveyard.png +0 -0
  85. package/assets/scene-icons/map.png +0 -0
  86. package/assets/scene-icons/office.png +0 -0
  87. package/assets/scene-icons/pirate.png +0 -0
  88. package/assets/scene-icons/station.png +0 -0
  89. package/assets/scenes/arcade.jpg +0 -0
  90. package/assets/scenes/cafe.jpg +0 -0
  91. package/assets/scenes/castle.jpg +0 -0
  92. package/assets/scenes/christmas.jpg +0 -0
  93. package/assets/scenes/graveyard.jpg +0 -0
  94. package/assets/scenes/office.jpg +0 -0
  95. package/assets/scenes/pirate.jpg +0 -0
  96. package/assets/scenes/station.jpg +0 -0
  97. package/client.js +144 -0
  98. package/cordis.patch.yml +3 -0
  99. package/harness.cjs +49 -0
  100. package/package.json +89 -4
  101. package/public/app.js +1 -0
  102. package/public/chunks/chunk-MU3LEDFF.js +4305 -0
  103. package/public/demo-icons/arrow-counterclockwise.svg +4 -0
  104. package/public/demo-icons/arrow-right.svg +3 -0
  105. package/public/demo-icons/bell.svg +3 -0
  106. package/public/demo-icons/box-seam-fill.svg +3 -0
  107. package/public/demo-icons/chat-left-text-fill.svg +3 -0
  108. package/public/demo-icons/check-lg.svg +3 -0
  109. package/public/demo-icons/chevron-down.svg +3 -0
  110. package/public/demo-icons/cpu-fill.svg +4 -0
  111. package/public/demo-icons/crosshair.svg +3 -0
  112. package/public/demo-icons/diagram-3-fill.svg +3 -0
  113. package/public/demo-icons/file-earmark-code-fill.svg +3 -0
  114. package/public/demo-icons/fullscreen.svg +3 -0
  115. package/public/demo-icons/pause-fill.svg +3 -0
  116. package/public/demo-icons/people-fill.svg +3 -0
  117. package/public/demo-icons/pie-chart-fill.svg +3 -0
  118. package/public/demo-icons/play-fill.svg +3 -0
  119. package/public/demo-icons/search.svg +3 -0
  120. package/public/demo-icons/shield-check.svg +4 -0
  121. package/public/demo-icons/stack.svg +4 -0
  122. package/public/demo-icons/x-lg.svg +3 -0
  123. package/public/demo.css +225 -0
  124. package/public/demo.html +67 -0
  125. package/public/demo.js +7 -0
  126. package/public/index.html +59 -0
  127. package/public/style.css +15 -0
  128. package/public/vendor/office.js +1631 -0
  129. package/src/codex.js +674 -0
  130. package/src/demo.js +103 -0
  131. package/src/harness-watcher.js +584 -0
  132. package/src/server.js +239 -0
  133. package/vendor/licenses/BOOTSTRAP-ICONS-LICENSE.txt +21 -0
  134. package/vendor/licenses/ESBUILD-LICENSE.txt +21 -0
  135. package/vendor/licenses/MESHOPTIMIZER-LICENSE.txt +22 -0
  136. package/vendor/licenses/PLAYWRIGHT-LICENSE.txt +202 -0
  137. package/vendor/licenses/PLAYWRIGHT-NOTICE.txt +5 -0
  138. package/vendor/licenses/THREE-LICENSE.txt +21 -0
@@ -0,0 +1,12 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="256" height="256">
2
+ <title>whatsmyagentdoing</title>
3
+ <rect width="32" height="32" rx="9" fill="#ff6a4d"/>
4
+ <rect x="5.5" y="7.5" width="21" height="15" rx="4" fill="#fff"/>
5
+ <path d="M13 22h6l1 3h-8z" fill="#fff"/>
6
+ <rect x="10" y="24.6" width="12" height="2.2" rx="1.1" fill="#fff"/>
7
+ <ellipse cx="12.6" cy="15" rx="2.3" ry="3" fill="#1a1a1a"/>
8
+ <ellipse cx="19.4" cy="15" rx="2.3" ry="3" fill="#1a1a1a"/>
9
+ <circle cx="13.3" cy="13.9" r=".8" fill="#fff"/>
10
+ <circle cx="20.1" cy="13.9" r=".8" fill="#fff"/>
11
+ <circle cx="25.2" cy="7.6" r="3.6" fill="#22a874" stroke="#ff6a4d" stroke-width="1.6"/>
12
+ </svg>
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/client.js ADDED
@@ -0,0 +1,144 @@
1
+ window.__ModuleLoader__.load({
2
+ id: 'dsh-mutipleagent',
3
+ factory: require => {
4
+ const { createElement: h, useEffect, useState } = require('react');
5
+ // 面板页面由本插件自己的静态服务提供(不是用户内容),需要同源才能读写本地偏好与同源 API。
6
+ // 其余权限(弹窗、顶层跳转、表单、下载、插件、指针锁定、模态框)保持沙箱默认拒绝。
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
+ }
23
+ function OfficeIcon({ size = 18 }) {
24
+ return h(
25
+ 'svg',
26
+ { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.6, 'aria-hidden': true },
27
+ h('path', { d: 'M3 10V5h6v5H3Zm12 0V5h6v5h-6ZM9 21v-5h6v5H9ZM6 10v3h12v-3m-6 3v3' }),
28
+ );
29
+ }
30
+ // 面板与会话视图共用同一套连接状态机:先探活,再挂 iframe。
31
+ function OfficeFrame({ sessionId, className }) {
32
+ const [version, setVersion] = useState(0);
33
+ const [error, setError] = useState('');
34
+ const [ready, setReady] = useState(false);
35
+ const [rev, setRev] = useState('');
36
+ const url = officeUrl(sessionId, rev);
37
+ useEffect(() => {
38
+ let active = true;
39
+ const controller = new AbortController();
40
+ setError('');
41
+ setReady(false);
42
+ fetch(OFFICE_BASE + 'api/health', { signal: controller.signal })
43
+ .then(response => {
44
+ if (!response.ok) throw Error('办公室暂不可用(HTTP ' + response.status + '),请确认插件已启用后重试。');
45
+ return response.json();
46
+ })
47
+ .then(data => {
48
+ if (!active) return;
49
+ setRev(String(data?.version ?? ''));
50
+ setReady(true);
51
+ })
52
+ .catch(error => {
53
+ if (active && error.name !== 'AbortError') setError(error.message);
54
+ });
55
+ return () => {
56
+ active = false;
57
+ controller.abort();
58
+ };
59
+ }, [version, sessionId]);
60
+ if (error)
61
+ return h(
62
+ 'section',
63
+ { className: 'dsh-office-status', role: 'alert' },
64
+ h('p', null, error),
65
+ h('button', { type: 'button', onClick: () => setVersion(value => value + 1) }, '重新连接'),
66
+ );
67
+ if (!ready) return h('p', { className: 'dsh-office-status', role: 'status' }, '正在连接多 Agent 办公室…');
68
+ return h('iframe', {
69
+ key: url,
70
+ className,
71
+ src: url,
72
+ title: 'DeepSeek Harness 多 Agent 协作办公室',
73
+ sandbox: FRAME_SANDBOX,
74
+ referrerPolicy: 'no-referrer',
75
+ allow: 'clipboard-write',
76
+ });
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
+ }
95
+ return {
96
+ inject: ['slots'],
97
+ apply(ctx) {
98
+ ctx.effect(() => {
99
+ const style = document.createElement('style');
100
+ style.textContent =
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}';
102
+ document.head.appendChild(style);
103
+ return () => style.remove();
104
+ }, 'dsh-mutipleagent: panel styles');
105
+ ctx.effect(
106
+ () => ctx.slots.inject('main', () => ctx.slots.register({ name: 'main', key: 'dsh-mutipleagent' }, OfficePage)),
107
+ 'dsh-mutipleagent: main panel',
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
+ );
126
+ ctx.effect(
127
+ () =>
128
+ ctx.slots.inject('sidebar.panellist', () =>
129
+ ctx.slots.register(
130
+ {
131
+ name: 'sidebar.panellist',
132
+ id: 'dsh-mutipleagent',
133
+ order: 30,
134
+ label: '多 Agent 办公室',
135
+ },
136
+ OfficeIcon,
137
+ ),
138
+ ),
139
+ 'dsh-mutipleagent: sidebar entry',
140
+ );
141
+ },
142
+ };
143
+ },
144
+ });
@@ -0,0 +1,3 @@
1
+ - insert:
2
+ - id: dsh-mutipleagent
3
+ name: dsh-mutipleagent
package/harness.cjs ADDED
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+ const { createOfficeServer } = require('./src/server');
3
+ const { HarnessWatcher } = require('./src/harness-watcher');
4
+
5
+ exports.name = 'dsh-mutipleagent';
6
+ exports.inject = ['webServer', 'connection', 'sessionQuery'];
7
+
8
+ exports.apply = function apply(ctx) {
9
+ ctx.effect(() => {
10
+ const watcher = new HarnessWatcher({ query: ctx.sessionQuery });
11
+ const server = createOfficeServer({
12
+ watcher,
13
+ basePath: '/dsh-mutipleagent',
14
+ defaultDocument: 'demo.html',
15
+ authorize(req) {
16
+ const admission = ctx.connection.admit(req);
17
+ if ('rejection' in admission) return admission.rejection;
18
+ // 此页面展示本机完整团队,只向宿主的 owner 开放。
19
+ return admission.peer.principal.kind === 'owner' ? undefined : 403;
20
+ },
21
+ });
22
+ let unregister;
23
+ try {
24
+ unregister = ctx.webServer.register({
25
+ kind: 'prefix',
26
+ path: '/dsh-mutipleagent',
27
+ handler(req, res) {
28
+ // 保留目录尾斜线,保证相对资源在 Web 和 Desktop 代理中一致。
29
+ const url = new URL(req.url, 'http://localhost');
30
+ if (url.pathname === '/dsh-mutipleagent') {
31
+ res.writeHead(302, { location: '/dsh-mutipleagent/' + url.search });
32
+ res.end();
33
+ return;
34
+ }
35
+ server.emit('request', req, res);
36
+ },
37
+ });
38
+ watcher.start();
39
+ } catch (error) {
40
+ unregister?.();
41
+ server.closeOffice();
42
+ throw error;
43
+ }
44
+ return () => {
45
+ unregister();
46
+ server.closeOffice();
47
+ };
48
+ }, 'dsh-mutipleagent: office route and session observer');
49
+ };
package/package.json CHANGED
@@ -1,6 +1,91 @@
1
1
  {
2
2
  "name": "dsh-mutipleagent",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "displayName": "DSH 多 Agent 办公室",
4
+ "version": "0.4.0",
5
+ "description": "DeepSeek Harness 多 Agent 3D 协作办公室插件:在会话页顶部标签行直接打开当前会话的 3D 办公室,观察真实主会话、子 Agent、工具活动与汇报,支持目录/会话切换和跨目录搜索。",
6
+ "license": "MIT",
7
+ "icon": "./assets/agents/dsh.svg",
8
+ "engines": {
9
+ "node": "^22.19.0 || >=24.0.0",
10
+ "dsh": "^0.2.2-rc.1"
11
+ },
12
+ "main": "harness.cjs",
13
+ "exports": {
14
+ ".": "./harness.cjs",
15
+ "./client": "./client.js",
16
+ "./cordis.patch.yml": "./cordis.patch.yml",
17
+ "./package.json": "./package.json",
18
+ "./icon": "./assets/agents/dsh.svg"
19
+ },
20
+ "files": [
21
+ "harness.cjs",
22
+ "client.js",
23
+ "cordis.patch.yml",
24
+ "src",
25
+ "public",
26
+ "assets",
27
+ "!assets/screenshots",
28
+ "vendor/licenses",
29
+ "LICENSE",
30
+ "README.md",
31
+ "README.en.md",
32
+ "SECURITY.md",
33
+ "CONTRIBUTING.md",
34
+ "CHANGELOG.md",
35
+ "THIRD_PARTY_NOTICES.md"
36
+ ],
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/lkuliuying/dsh-mutipleagent.git"
40
+ },
41
+ "homepage": "https://github.com/lkuliuying/dsh-mutipleagent",
42
+ "bugs": {
43
+ "url": "https://github.com/lkuliuying/dsh-mutipleagent/issues"
44
+ },
45
+ "keywords": [
46
+ "dsh-plugin",
47
+ "deepseek-harness",
48
+ "multi-agent",
49
+ "3d-office"
50
+ ],
51
+ "dsh": {
52
+ "bundle": {
53
+ "patch": "./cordis.patch.yml"
54
+ },
55
+ "client": {
56
+ "platform": "web",
57
+ "inject": [
58
+ "@deepseek-ai/dsh-client-ui-layout",
59
+ "@deepseek-ai/dsh-client-ui-chat",
60
+ "@deepseek-ai/dsh-client-ui-conversation",
61
+ "@deepseek-ai/dsh-client-ui-sidebar"
62
+ ]
63
+ }
64
+ },
65
+ "scripts": {
66
+ "start": "node src/server.js --open",
67
+ "demo": "node src/server.js --demo --open",
68
+ "build": "node scripts/build.mjs",
69
+ "lint": "eslint .",
70
+ "format": "prettier --write \"**/*.{js,cjs,mjs,yml}\"",
71
+ "format:check": "prettier --check \"**/*.{js,cjs,mjs,yml}\"",
72
+ "check:compat": "node scripts/check-compat.mjs",
73
+ "test": "node --test tests/*.test.js",
74
+ "test:ui": "node tests/browser.cjs",
75
+ "test:plugin:ui": "node tests/plugin-browser.cjs",
76
+ "smoke:host": "node scripts/smoke-host.cjs",
77
+ "release": "node scripts/release.mjs",
78
+ "screenshots": "node scripts/screenshots.mjs",
79
+ "test:coverage": "node --test --experimental-test-coverage --test-coverage-lines=85 --test-coverage-functions=80 --test-coverage-branches=75 tests/*.test.js"
80
+ },
81
+ "devDependencies": {
82
+ "@eslint/js": "^9.39.5",
83
+ "esbuild": "^0.25.12",
84
+ "eslint": "^9.39.4",
85
+ "globals": "^15.15.0",
86
+ "playwright": "^1.62.1",
87
+ "prettier": "^3.9.10",
88
+ "semver": "^7.8.6",
89
+ "three": "^0.186.1"
90
+ }
91
+ }
package/public/app.js ADDED
@@ -0,0 +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-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();