dsh-super-code 0.0.9

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 (71) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +162 -0
  3. package/client/index.js +920 -0
  4. package/cordis.patch.yml +21 -0
  5. package/lib/core/delegation.d.ts +59 -0
  6. package/lib/core/delegation.d.ts.map +1 -0
  7. package/lib/core/delegation.js +41 -0
  8. package/lib/core/delegation.js.map +1 -0
  9. package/lib/core/evaluation.d.ts +123 -0
  10. package/lib/core/evaluation.d.ts.map +1 -0
  11. package/lib/core/evaluation.js +180 -0
  12. package/lib/core/evaluation.js.map +1 -0
  13. package/lib/core/experiment-recipes.d.ts +5 -0
  14. package/lib/core/experiment-recipes.d.ts.map +1 -0
  15. package/lib/core/experiment-recipes.js +67 -0
  16. package/lib/core/experiment-recipes.js.map +1 -0
  17. package/lib/core/guidance.d.ts +10 -0
  18. package/lib/core/guidance.d.ts.map +1 -0
  19. package/lib/core/guidance.js +16 -0
  20. package/lib/core/guidance.js.map +1 -0
  21. package/lib/core/session-scan.d.ts +8 -0
  22. package/lib/core/session-scan.d.ts.map +1 -0
  23. package/lib/core/session-scan.js +21 -0
  24. package/lib/core/session-scan.js.map +1 -0
  25. package/lib/core/task-memory.d.ts +81 -0
  26. package/lib/core/task-memory.d.ts.map +1 -0
  27. package/lib/core/task-memory.js +158 -0
  28. package/lib/core/task-memory.js.map +1 -0
  29. package/lib/core/teams.d.ts +19 -0
  30. package/lib/core/teams.d.ts.map +1 -0
  31. package/lib/core/teams.js +40 -0
  32. package/lib/core/teams.js.map +1 -0
  33. package/lib/core/tool-evidence.d.ts +57 -0
  34. package/lib/core/tool-evidence.d.ts.map +1 -0
  35. package/lib/core/tool-evidence.js +97 -0
  36. package/lib/core/tool-evidence.js.map +1 -0
  37. package/lib/dsh/index.d.ts +6 -0
  38. package/lib/dsh/index.d.ts.map +1 -0
  39. package/lib/dsh/index.js +11 -0
  40. package/lib/dsh/index.js.map +1 -0
  41. package/lib/dsh/super-code.d.ts +22 -0
  42. package/lib/dsh/super-code.d.ts.map +1 -0
  43. package/lib/dsh/super-code.js +247 -0
  44. package/lib/dsh/super-code.js.map +1 -0
  45. package/lib/evaluate.d.ts +3 -0
  46. package/lib/evaluate.d.ts.map +1 -0
  47. package/lib/evaluate.js +40 -0
  48. package/lib/evaluate.js.map +1 -0
  49. package/lib/experiments.d.ts +77 -0
  50. package/lib/experiments.d.ts.map +1 -0
  51. package/lib/experiments.js +196 -0
  52. package/lib/experiments.js.map +1 -0
  53. package/lib/index.d.ts +13 -0
  54. package/lib/index.d.ts.map +1 -0
  55. package/lib/index.js +14 -0
  56. package/lib/index.js.map +1 -0
  57. package/lib/super-agent-usage.d.ts +44 -0
  58. package/lib/super-agent-usage.d.ts.map +1 -0
  59. package/lib/super-agent-usage.js +203 -0
  60. package/lib/super-agent-usage.js.map +1 -0
  61. package/lib/task-memory-projection.d.ts +43 -0
  62. package/lib/task-memory-projection.d.ts.map +1 -0
  63. package/lib/task-memory-projection.js +48 -0
  64. package/lib/task-memory-projection.js.map +1 -0
  65. package/lib/task-memory-store.d.ts +35 -0
  66. package/lib/task-memory-store.d.ts.map +1 -0
  67. package/lib/task-memory-store.js +69 -0
  68. package/lib/task-memory-store.js.map +1 -0
  69. package/package.json +123 -0
  70. package/presets/super-code/agent.cordis.yml +50 -0
  71. package/presets/super-code/preset.yml +3 -0
@@ -0,0 +1,920 @@
1
+ /* Browser entry for dsh-super-code. It deliberately uses the Harness public
2
+ * slot and Remote faces; no DOM selectors or private React internals are used.
3
+ *
4
+ * The Harness client module table serves classic scripts and expects every
5
+ * plugin artifact to register a closure factory. Keeping the small bundle
6
+ * self-contained here also makes the published package usable outside the
7
+ * Harness monorepo's tsdown workspace.
8
+ */
9
+ ;(globalThis.window || globalThis).__ModuleLoader__.load({ id: 'dsh-super-code', factory: (require) => {
10
+ const React = require('react')
11
+ const { useEffect, useLayoutEffect, useRef, useMemo, useState, useSyncExternalStore } = React
12
+ const { IconRefreshOutline16 } = require('@deepseek-ai/dsh-client-ui-primitives')
13
+ const { SessionEventStream } = require('@deepseek-ai/dsh-api-session-controller')
14
+
15
+ function TaskMemoryPanel({ state }) {
16
+ const tasks = state?.tasks || []
17
+ if (!tasks.length) return null
18
+ const current = state.current?.kind === 'task' ? state.current : undefined
19
+ const labels = { active: '进行中', paused: '已暂停', completed: '已完成', cancelled: '已取消' }
20
+ return React.createElement('section', { className: 'dsh-super-code-task-memory', 'aria-label': '任务档案' },
21
+ current && React.createElement(React.Fragment, null,
22
+ React.createElement('div', { className: 'dsh-super-code-task-heading' },
23
+ React.createElement('strong', null, current.title),
24
+ React.createElement('span', null, labels[current.status] || current.status)),
25
+ current.goal && React.createElement('p', null, current.goal),
26
+ current.next && React.createElement('p', null, '下一步:', current.next)),
27
+ tasks.some(task => task.id !== current?.id) && React.createElement('details', null,
28
+ React.createElement('summary', null, current ? '其他任务' : '任务记录'),
29
+ React.createElement('ul', null, tasks.filter(task => task.id !== current?.id).map(task => React.createElement('li', { key: task.id }, `${task.title} · ${labels[task.status] || task.status}`)))),
30
+ )
31
+ }
32
+
33
+ /** Join host catalogs without leaking agents from unrelated conversations. */
34
+ function buildAgentView(state) {
35
+ const nodes = new Map(Object.values(state.byId || {}).map(node => [node.id, { ...node }]))
36
+ for (const [parentId, catalog] of Object.entries(state.subagentsByParent || {})) {
37
+ // Navigation rows omit timestamps; the parent's public projection retains them.
38
+ const created = new Map((nodes.get(parentId)?.projectionValues?.subagentCatalog || []).map(entry => [entry.id, entry.createdAt]))
39
+ for (const entry of catalog?.entries || []) {
40
+ const summary = nodes.get(entry.id)
41
+ nodes.set(entry.id, { ...summary, id: entry.id, parentId,
42
+ title: entry.label || summary?.title || summary?.displayTitle || entry.id,
43
+ mode: entry.mode, hasChildren: entry.hasChildren, createdAt: created.get(entry.id),
44
+ running: summary?.running ?? entry.activity === 'running',
45
+ unavailable: entry.kind === 'diagnostic',
46
+ })
47
+ }
48
+ }
49
+ if (!state.current) return { root: '', nodes: new Map(), children: new Map(), ancestors: new Set() }
50
+ if (state.currentAddress && !nodes.get(state.current)?.parentId) {
51
+ nodes.set(state.current, { ...nodes.get(state.current), id: state.current,
52
+ parentId: state.currentAddress.parentSessionId, mode: state.currentAddress.mode })
53
+ }
54
+ let root = state.current
55
+ const ancestors = new Set()
56
+ while (!ancestors.has(root)) {
57
+ ancestors.add(root)
58
+ if (!nodes.has(root)) nodes.set(root, { id: root, title: root })
59
+ const parent = nodes.get(root).parentId
60
+ if (!parent || ancestors.has(parent)) break
61
+ root = parent
62
+ }
63
+ const children = new Map()
64
+ for (const node of nodes.values()) {
65
+ if (!children.has(node.parentId)) children.set(node.parentId, [])
66
+ children.get(node.parentId).push(node.id)
67
+ }
68
+ // The public catalog is ordered by creation time; summary insertion order is not.
69
+ for (const [parent, catalog] of Object.entries(state.subagentsByParent || {})) {
70
+ const rank = new Map((catalog.entries || []).map((entry, index) => [entry.id, index]))
71
+ children.get(parent)?.sort((a, b) => (rank.get(a) ?? -1) - (rank.get(b) ?? -1))
72
+ }
73
+ const reachable = new Map(), pending = [root]
74
+ while (pending.length) {
75
+ const id = pending.pop()
76
+ if (reachable.has(id)) continue
77
+ reachable.set(id, nodes.get(id))
78
+ for (const child of children.get(id) || []) pending.push(child)
79
+ }
80
+ return { root, nodes: reachable, children, ancestors }
81
+ }
82
+
83
+ /** Iterative traversal bounds rendered rows even for long-running sessions. */
84
+ function visibleAgentRows(view, expanded, limit) {
85
+ const rows = [], seen = new Set(), stack = view.root ? [{ id: view.root, level: 1 }] : []
86
+ while (stack.length && rows.length < limit) {
87
+ const row = stack.pop()
88
+ if (seen.has(row.id)) continue
89
+ seen.add(row.id)
90
+ rows.push(row)
91
+ if (expanded(row.id)) {
92
+ const children = view.children.get(row.id) || []
93
+ for (let i = children.length - 1; i >= 0; i--) stack.push({ id: children[i], level: row.level + 1 })
94
+ }
95
+ }
96
+ return { rows, more: stack.length > 0 }
97
+ }
98
+
99
+ /** Presentation only: never archive sessions or discard usage/history. */
100
+ function agentTreeDisplay(view, catalogs = {}, options = {}) {
101
+ const expanded = options.expanded || new Set(), collapsed = options.collapsed || new Set()
102
+ const recent = options.compact ? 2 : 4
103
+ const all = visibleAgentRows(view, () => true, view.nodes.size).rows
104
+ const levels = new Map(all.map(row => [row.id, row.level])), required = new Set(view.ancestors)
105
+ if (options.focused) required.add(options.focused)
106
+ const children = new Map()
107
+ for (let index = all.length - 1; index >= 0; index--) {
108
+ const { id, level } = all[index], node = view.nodes.get(id)
109
+ const original = (view.children.get(id) || []).filter(child => levels.get(child) === level + 1)
110
+ const unknownChildren = node.hasChildren && catalogs[id]?.state !== 'ready'
111
+ if (node.running !== false || node.unavailable || unknownChildren || original.some(child => required.has(child))) required.add(id)
112
+ const deep = level >= 2 && !expanded.has(id)
113
+ const keep = collapsed.has(id) || deep ? new Set() : new Set(original.slice(-recent))
114
+ const visible = original.filter(child => required.has(child) || (!collapsed.has(id) && (expanded.has(id) || keep.has(child))))
115
+ children.set(id, visible)
116
+ }
117
+ const display = { ...view, children }
118
+ const shown = new Set(visibleAgentRows(display, () => true, view.nodes.size).rows.map(row => row.id))
119
+ return { view: display, hidden: all.filter(row => !shown.has(row.id)).map(row => row.id) }
120
+ }
121
+
122
+ function agentTreeTotals(view, catalogs = {}) {
123
+ const totals = new Map(), order = [], parents = new Map(), seen = new Set(), pending = view.root ? [view.root] : []
124
+ while (pending.length) {
125
+ const id = pending.pop()
126
+ if (seen.has(id)) continue
127
+ seen.add(id); order.push(id)
128
+ for (const child of view.children.get(id) || []) {
129
+ if (!seen.has(child) && !parents.has(child)) { parents.set(child, id); pending.push(child) }
130
+ }
131
+ }
132
+ for (let i = order.length - 1; i >= 0; i--) {
133
+ const id = order[i], node = view.nodes.get(id), projections = node.projectionValues || {}
134
+ const usage = projections.superAgentUsage?.totals || projections.tokenUsage
135
+ const input = usage ? (usage.uncachedInputTokens || 0) + (usage.cacheReadTokens || 0) + (usage.cacheWriteTokens || 0) : 0
136
+ const total = totals.get(id) || { agents: 0, tokens: 0, cache: 0, input: 0, known: 0, partial: false }
137
+ total.agents++; total.tokens += input + (usage?.outputTokens || 0); total.cache += usage?.cacheReadTokens || 0; total.input += input
138
+ if (usage) total.known++
139
+ if (node.unavailable || ((id === view.root || node.hasChildren) && catalogs[id]?.state !== 'ready')) total.partial = true
140
+ totals.set(id, total)
141
+ const parent = parents.get(id)
142
+ if (parent && parent !== id) {
143
+ const sum = totals.get(parent) || { agents: 0, tokens: 0, cache: 0, input: 0, known: 0, partial: false }
144
+ for (const key of ['agents', 'tokens', 'cache', 'input', 'known']) sum[key] += total[key]
145
+ sum.partial ||= total.partial
146
+ totals.set(parent, sum)
147
+ }
148
+ }
149
+ return totals
150
+ }
151
+
152
+ const historyRowHeight = 56
153
+ const historyCollator = new Intl.Collator('zh-CN', { numeric: true, sensitivity: 'base' })
154
+ const historyDateFormat = new Intl.DateTimeFormat('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })
155
+
156
+ function agentHistoryRange(range, from = '', to = '', now = Date.now()) {
157
+ const today = new Date(now)
158
+ today.setHours(0, 0, 0, 0)
159
+ const parse = value => {
160
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return NaN
161
+ const [year, month, day] = value.split('-').map(Number), date = new Date(year, month - 1, day)
162
+ return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day ? date.getTime() : NaN
163
+ }
164
+ let start = -Infinity, end = Infinity
165
+ if (range === 'custom') {
166
+ start = from ? parse(from) : -Infinity
167
+ if (to) { const date = new Date(parse(to)); date.setDate(date.getDate() + 1); end = date.getTime() }
168
+ } else if (range !== 'all') {
169
+ const tomorrow = new Date(today)
170
+ tomorrow.setDate(tomorrow.getDate() + 1)
171
+ today.setDate(today.getDate() - ({ today: 0, week: 6, month: 29 }[range] ?? 0))
172
+ start = today.getTime(); end = tomorrow.getTime()
173
+ }
174
+ return { start, end, valid: !Number.isNaN(start) && !Number.isNaN(end) && start < end }
175
+ }
176
+
177
+ function agentHistoryRows(view, hidden, totals, options = {}) {
178
+ const query = (options.query || '').trim().toLocaleLowerCase()
179
+ const range = agentHistoryRange(options.range || 'all', options.from, options.to, options.now)
180
+ if (!range.valid) return []
181
+ const rows = []
182
+ for (const id of hidden) {
183
+ const node = view.nodes.get(id), title = node.title || node.displayTitle || id
184
+ const parent = view.nodes.get(node.parentId)?.title || '主 Agent', usage = totals.get(id)
185
+ const time = Number.isFinite(node.createdAt) && node.createdAt >= 0 && node.createdAt <= 8640000000000000 ? node.createdAt : -1
186
+ if (query && !`${title} ${parent}`.toLocaleLowerCase().includes(query)) continue
187
+ if (options.range && options.range !== 'all' && (time < 0 || time < range.start || time >= range.end)) continue
188
+ rows.push({ id, node, title, parent, time, tokens: usage?.known ? usage.tokens : -1, partial: !!usage && (usage.partial || usage.known < usage.agents) })
189
+ }
190
+ const [field, direction] = (options.sort || 'time-desc').split('-'), sign = direction === 'asc' ? 1 : -1
191
+ return rows.sort((a, b) => {
192
+ let comparison
193
+ if (field === 'title') comparison = historyCollator.compare(a.title, b.title)
194
+ else {
195
+ const left = field === 'tokens' ? a.tokens : a.time, right = field === 'tokens' ? b.tokens : b.time
196
+ // Missing metadata stays last in either direction, never masquerades as zero.
197
+ if ((left < 0) !== (right < 0)) return left < 0 ? 1 : -1
198
+ comparison = left - right
199
+ }
200
+ return comparison * sign || historyCollator.compare(a.title, b.title) || a.id.localeCompare(b.id)
201
+ })
202
+ }
203
+
204
+ function agentHistoryWindow(count, scrollTop, height) {
205
+ const top = Math.max(0, Math.min(scrollTop, Math.max(0, count * historyRowHeight - height)))
206
+ return { top, start: Math.max(0, Math.floor(top / historyRowHeight) - 3),
207
+ end: Math.min(count, Math.ceil((top + height) / historyRowHeight) + 3), height: count * historyRowHeight }
208
+ }
209
+
210
+ /** Local scroll state keeps the graph out of the scrolling render path. */
211
+ function AgentHistory({ view, hidden, totals, open }) {
212
+ const [filters, setFilters] = useState({ query: '', range: 'all', from: '', to: '', sort: 'time-desc' })
213
+ const [scroll, setScroll] = useState({ top: 0, height: 168 })
214
+ const viewport = useRef(null), focusTarget = useRef('')
215
+ const records = useMemo(() => agentHistoryRows(view, hidden, totals, filters), [view, hidden, totals, filters])
216
+ const window = agentHistoryWindow(records.length, scroll.top, scroll.height)
217
+ const range = agentHistoryRange(filters.range, filters.from, filters.to)
218
+ const change = (key, value) => setFilters(previous => ({ ...previous, [key]: value }))
219
+ useLayoutEffect(() => {
220
+ viewport.current.scrollTop = 0
221
+ setScroll(previous => ({ ...previous, top: 0 }))
222
+ }, [filters])
223
+ useLayoutEffect(() => {
224
+ const element = viewport.current
225
+ const measure = () => setScroll(previous => previous.top === element.scrollTop && previous.height === element.clientHeight
226
+ ? previous : { top: element.scrollTop, height: element.clientHeight })
227
+ measure()
228
+ const observer = new ResizeObserver(measure)
229
+ observer.observe(element)
230
+ return () => observer.disconnect()
231
+ }, [])
232
+ useLayoutEffect(() => {
233
+ viewport.current.scrollTop = window.top
234
+ if (focusTarget.current) {
235
+ const target = Array.from(viewport.current.querySelectorAll('button')).find(button => button.dataset.historyId === focusTarget.current)
236
+ if (target) { target.focus({ preventScroll: true }); focusTarget.current = '' }
237
+ }
238
+ }, [window.top, window.start, window.end, records, scroll])
239
+ const keyDown = (event, index) => {
240
+ const next = { ArrowDown: index + 1, ArrowUp: index - 1, Home: 0, End: records.length - 1 }[event.key]
241
+ if (next === undefined) return
242
+ event.preventDefault()
243
+ const target = Math.max(0, Math.min(records.length - 1, next)), element = viewport.current
244
+ focusTarget.current = records[target].id
245
+ if (target * historyRowHeight < element.scrollTop) element.scrollTop = target * historyRowHeight
246
+ else if ((target + 1) * historyRowHeight > element.scrollTop + element.clientHeight) element.scrollTop = (target + 1) * historyRowHeight - element.clientHeight
247
+ setScroll({ top: element.scrollTop, height: element.clientHeight })
248
+ }
249
+ const select = (key, label, choices) => React.createElement('label', null, label,
250
+ React.createElement('select', { value: filters[key], 'aria-label': label, onChange: event => change(key, event.target.value) },
251
+ choices.map(([value, text]) => React.createElement('option', { key: value, value }, text))))
252
+ return React.createElement('section', { className: 'dsh-super-code-history', 'aria-label': '已收起的 Agent' },
253
+ React.createElement('input', { type: 'search', value: filters.query, 'aria-label': '查找收起的 Agent', placeholder: '搜索 Agent 或上级任务', onChange: event => change('query', event.target.value) }),
254
+ React.createElement('div', { className: 'dsh-super-code-history-filters' },
255
+ select('range', '创建时间', [['all', '全部时间'], ['today', '今天'], ['week', '近 7 天'], ['month', '近 30 天'], ['custom', '自定义']]),
256
+ select('sort', '排序', [['time-desc', '最新创建'], ['time-asc', '最早创建'], ['tokens-desc', 'Token 从多到少'], ['tokens-asc', 'Token 从少到多'], ['title-asc', '标题正序'], ['title-desc', '标题倒序']])),
257
+ filters.range === 'custom' && React.createElement('div', { className: 'dsh-super-code-history-filters' },
258
+ ['from', 'to'].map((key, index) => React.createElement('label', { key }, index ? '结束日期' : '开始日期',
259
+ React.createElement('input', { type: 'date', value: filters[key], 'aria-label': index ? '结束日期' : '开始日期', onChange: event => change(key, event.target.value) })))),
260
+ React.createElement('p', { className: 'dsh-super-code-history-summary', role: 'status' }, range.valid ? `${records.length} / ${hidden.length} 个已收起节点 · Token 含下级` : '请选择有效的起止日期'),
261
+ React.createElement('div', { ref: viewport, className: 'dsh-super-code-history-list', role: 'list', 'aria-label': '收起节点列表',
262
+ onScroll: event => { const top = event.currentTarget.scrollTop; setScroll(previous => previous.top === top ? previous : { ...previous, top }) } },
263
+ React.createElement('div', { style: { height: window.height, position: 'relative' } }, records.slice(window.start, window.end).map((record, offset) => {
264
+ const index = window.start + offset, status = agentExecutionStatus(record.node, false)
265
+ const date = record.time < 0 ? '时间未知' : historyDateFormat.format(record.time)
266
+ return React.createElement('div', { key: record.id, role: 'listitem', 'aria-posinset': index + 1, 'aria-setsize': records.length,
267
+ style: { position: 'absolute', top: index * historyRowHeight, height: historyRowHeight, width: '100%' } },
268
+ React.createElement('button', { type: 'button', 'data-history-id': record.id, onClick: () => open(record.node), onKeyDown: event => keyDown(event, index),
269
+ title: `${record.title} · ${record.parent} · ${record.time < 0 ? date : new Date(record.time).toLocaleString('zh-CN')} · ${record.tokens < 0 ? '用量未知' : record.tokens + ' Token' + (record.partial ? '(部分)' : '')}`,
270
+ 'aria-label': `查看 ${record.title} 的详情` },
271
+ React.createElement('i', { className: 'dsh-super-code-circle ' + status.tone, 'aria-hidden': true }),
272
+ React.createElement('span', null, React.createElement('strong', null, record.title), React.createElement('small', null, `${status.label} · ${date} · ${record.parent}`)),
273
+ React.createElement('small', null, record.tokens < 0 ? '—' : `${record.partial ? '≈' : ''}${record.tokens.toLocaleString('zh-CN', { notation: 'compact', maximumFractionDigits: 1 })} tok`)))
274
+ })),
275
+ !records.length && React.createElement('p', { className: 'dsh-super-code-history-empty' }, '没有匹配的节点')))
276
+ }
277
+
278
+ function AgentTree({ useSessions, useTabInfo, treeStates, openDetail, refresh, watchCatalog }) {
279
+ const state = useSessions(s => s)
280
+ const { tab } = useTabInfo()
281
+ const view = useMemo(() => buildAgentView(state), [state.byId, state.subagentsByParent, state.current, state.currentAddress])
282
+ const saved = useMemo(() => {
283
+ let value = treeStates.get(tab.signal)
284
+ if (!value || value.root !== view.root) {
285
+ value = { root: view.root, limit: 200, focused: state.current, expanded: new Set(), collapsed: new Set() }
286
+ treeStates.set(tab.signal, value)
287
+ }
288
+ return value
289
+ }, [treeStates, tab.signal, view.root])
290
+ return React.createElement(AgentTreeBody, { key: view.root, state, view, saved, openDetail, refresh, watchCatalog })
291
+ }
292
+
293
+ function AgentCatalogWatch({ id, watchCatalog }) {
294
+ useEffect(() => {
295
+ watchCatalog(id, true)
296
+ return () => watchCatalog(id, false)
297
+ }, [id, watchCatalog])
298
+ return null
299
+ }
300
+
301
+ function agentTreeLayout(view, limit) {
302
+ const { rows, more } = visibleAgentRows(view, () => true, limit)
303
+ const positions = new Map(), edges = []
304
+ let leaves = 0
305
+ for (let i = rows.length - 1; i >= 0; i--) {
306
+ const row = rows[i]
307
+ const children = (view.children.get(row.id) || []).filter(id => positions.has(id) && positions.get(id).level === row.level + 1)
308
+ const x = children.length ? children.reduce((sum, id) => sum + positions.get(id).x, 0) / children.length : 70 + leaves++ * 140
309
+ positions.set(row.id, { ...row, x, y: 18 + (row.level - 1) * 128 })
310
+ for (const child of children) edges.push({ parent: row.id, child })
311
+ }
312
+ const width = Math.max(140, leaves * 140)
313
+ // Reverse the postorder leaf allocation to preserve catalog order.
314
+ for (const position of positions.values()) position.x = width - position.x
315
+ return { rows: rows.map(row => positions.get(row.id)), edges, positions, more, width,
316
+ height: Math.max(140, ...rows.map(row => row.level * 128)) }
317
+ }
318
+
319
+ function agentExecutionStatus(node, isRoot) {
320
+ if (!node || node.unavailable || typeof node.running !== 'boolean') return { tone: 'unavailable', label: '记录不可用' }
321
+ if (node.running) return { tone: 'active', label: '运行中' }
322
+ return isRoot || node.mode === 'continuable' ? { tone: 'idle', label: '待命' } : { tone: 'stopped', label: '已停止' }
323
+ }
324
+
325
+ function AgentTreeBody({ state, view, saved, openDetail, refresh, watchCatalog }) {
326
+ const [pan, setPan] = useState(saved.pan || { x: 0, y: 0 })
327
+ const [zoom, setZoom] = useState(saved.zoom || 1)
328
+ const viewport = useRef(null), graph = useRef(null)
329
+ const autoFit = useRef(!saved.pan)
330
+ const camera = useRef({ pan, zoom })
331
+ camera.current = { pan, zoom }
332
+ const changeCamera = (nextPan, nextZoom) => {
333
+ saved.pan = nextPan; saved.zoom = nextZoom
334
+ camera.current = { pan: nextPan, zoom: nextZoom }
335
+ setPan(nextPan); setZoom(nextZoom)
336
+ }
337
+ const zoomAt = (factor, x, y) => {
338
+ autoFit.current = false
339
+ const current = camera.current, rect = graph.current.getBoundingClientRect()
340
+ const next = Math.max(.4, Math.min(2, current.zoom * factor))
341
+ const dx = (x - rect.left) / current.zoom, dy = (y - rect.top) / current.zoom
342
+ changeCamera({ x: current.pan.x - dx * (next - current.zoom), y: current.pan.y - dy * (next - current.zoom) }, next)
343
+ }
344
+ useEffect(() => {
345
+ const element = viewport.current
346
+ const wheel = event => { event.preventDefault(); zoomAt(Math.exp(-event.deltaY * (event.deltaMode === 1 ? .04 : .002)), event.clientX, event.clientY) }
347
+ element.addEventListener('wheel', wheel, { passive: false })
348
+ return () => element.removeEventListener('wheel', wheel)
349
+ }, [])
350
+ const gesture = useRef({ moved: false })
351
+ const finishDrag = event => {
352
+ const drag = gesture.current
353
+ if (drag.id !== event.pointerId) return
354
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId)
355
+ drag.id = undefined
356
+ event.currentTarget.classList.remove('dragging')
357
+ }
358
+ const [limit, setLimit] = useState(saved.limit)
359
+ const [error, setError] = useState('')
360
+ const [busy, setBusy] = useState(false)
361
+ const [focused, setFocused] = useState(saved.focused)
362
+ const [expanded, setExpanded] = useState(saved.expanded || new Set())
363
+ const [collapsed, setCollapsed] = useState(saved.collapsed || new Set())
364
+ const [historyOpen, setHistoryOpen] = useState(false)
365
+ const [compact, setCompact] = useState(false)
366
+ useEffect(() => { Object.assign(saved, { limit, focused, expanded, collapsed }) }, [saved, limit, focused, expanded, collapsed])
367
+ const catalogs = state.subagentsByParent || {}
368
+ const display = useMemo(() => agentTreeDisplay(view, catalogs, { expanded, collapsed, focused, compact }), [view, catalogs, expanded, collapsed, focused, compact])
369
+ const { rows, more, edges, positions, width, height } = useMemo(() => agentTreeLayout(display.view, limit), [display, limit])
370
+ const toggleBranch = id => {
371
+ autoFit.current = true
372
+ const show = collapsed.has(id) || !(display.view.children.get(id) || []).length
373
+ const nextExpanded = new Set(expanded), nextCollapsed = new Set(collapsed)
374
+ nextExpanded.delete(id); nextCollapsed.delete(id)
375
+ if (show) nextExpanded.add(id); else nextCollapsed.add(id)
376
+ setExpanded(nextExpanded); setCollapsed(nextCollapsed)
377
+ }
378
+ const fit = () => {
379
+ autoFit.current = true
380
+ const element = viewport.current
381
+ const next = Math.max(.4, Math.min(1.25, (element.clientWidth - 24) / width, (element.clientHeight - 24) / height))
382
+ changeCamera({ x: (element.clientWidth - 24 - width * next) / 2, y: 8 }, next)
383
+ element.scrollLeft = 0; element.scrollTop = 0
384
+ }
385
+ useLayoutEffect(() => {
386
+ const resize = () => {
387
+ setCompact(viewport.current.clientWidth < 400)
388
+ if (autoFit.current) fit()
389
+ }
390
+ resize()
391
+ const observer = new ResizeObserver(resize)
392
+ observer.observe(viewport.current)
393
+ return () => observer.disconnect()
394
+ }, [width, height])
395
+ const zoomButton = factor => {
396
+ const rect = viewport.current.getBoundingClientRect()
397
+ zoomAt(factor, rect.left + rect.width / 2, rect.top + rect.height / 2)
398
+ }
399
+ const totals = useMemo(() => agentTreeTotals(view, catalogs), [view, catalogs])
400
+ // Retain already-loaded branch subscriptions so a hidden worker can become visible.
401
+ const watched = [...new Set([...rows.filter(({ id }) => id === view.root || view.nodes.get(id)?.hasChildren).map(row => row.id),
402
+ ...Object.keys(catalogs).filter(id => view.nodes.has(id))])]
403
+ const open = node => {
404
+ setError('')
405
+ Promise.resolve().then(() => openDetail(node)).catch(() => setError('无法打开详情,请重试'))
406
+ }
407
+ const reload = async () => {
408
+ setBusy(true); setError('')
409
+ try { await Promise.all([...new Set([view.root, ...watched])].filter(Boolean).map(id => refresh(id))) }
410
+ catch { setError('执行记录刷新失败') }
411
+ finally { setBusy(false) }
412
+ }
413
+ const keyDown = (event, row) => {
414
+ const index = rows.findIndex(item => item.id === row.id)
415
+ let target
416
+ if (event.key === 'ArrowDown') target = rows[Math.min(rows.length - 1, index + 1)]?.id
417
+ else if (event.key === 'ArrowUp') target = rows[Math.max(0, index - 1)]?.id
418
+ else if (event.key === 'Home') target = rows[0]?.id
419
+ else if (event.key === 'End') target = rows.at(-1)?.id
420
+ else if (event.key === 'ArrowRight') {
421
+ target = rows[index + 1]?.level > row.level ? rows[index + 1].id : row.id
422
+ } else if (event.key === 'ArrowLeft') {
423
+ target = view.nodes.get(row.id).parentId
424
+ } else if (event.key === 'Enter' || event.key === ' ') {
425
+ if (!view.nodes.get(row.id).unavailable) open(view.nodes.get(row.id))
426
+ } else return
427
+ event.preventDefault()
428
+ if (target) {
429
+ setFocused(target)
430
+ const tree = event.currentTarget.closest('[role="tree"]')
431
+ Array.from(tree.querySelectorAll('[role="treeitem"]')).find(item => item.dataset.agentId === target)?.focus()
432
+ }
433
+ }
434
+ const focusId = rows.some(row => row.id === focused) ? focused : view.root
435
+ const running = [...view.nodes.values()].filter(node => node.running).length
436
+ const catalogError = watched.some(id => catalogs[id]?.state === 'error')
437
+ return React.createElement('section', { className: 'dsh-super-code-tree', 'data-super-agent-tree': true },
438
+ watched.map(id => React.createElement(AgentCatalogWatch, { key: id, id, watchCatalog })),
439
+ React.createElement('header', { className: 'dsh-super-code-tree-header' },
440
+ React.createElement('div', null,
441
+ React.createElement('strong', null, '协作执行'),
442
+ React.createElement('span', { className: 'dsh-super-code-tree-count' }, `显示 ${rows.length} / ${view.nodes.size} · 运行中 ${running}`)),
443
+ React.createElement('button', { type: 'button', className: 'dsh-super-code-icon-button', disabled: busy || !view.root, onClick: reload, title: '刷新执行记录', 'aria-label': '刷新执行记录' }, React.createElement(IconRefreshOutline16))),
444
+ (error || catalogError) && React.createElement('p', { role: 'alert', className: 'dsh-super-code-tree-error' }, error || '部分执行记录读取失败,请刷新重试'),
445
+ React.createElement('div', { className: 'dsh-super-code-graph-legend', 'aria-label': '状态颜色' },
446
+ ['运行中', '待命', '已停止', '记录不可用'].map((label, index) => React.createElement('span', { key: label },
447
+ React.createElement('i', { className: 'dsh-super-code-circle ' + ['active', 'idle', 'stopped', 'unavailable'][index], 'aria-hidden': true }), label))),
448
+ !view.root && React.createElement('p', { className: 'dsh-super-code-tree-empty' }, '暂无会话'),
449
+ React.createElement('div', { className: 'dsh-super-code-graph-controls', 'aria-label': '画布操作' },
450
+ React.createElement('button', { type: 'button', 'aria-label': '缩小节点图', disabled: zoom <= .4, onClick: () => zoomButton(1 / 1.2) }, '−'),
451
+ React.createElement('span', { 'aria-live': 'polite' }, `${Math.round(zoom * 100)}%`),
452
+ React.createElement('button', { type: 'button', 'aria-label': '放大节点图', disabled: zoom >= 2, onClick: () => zoomButton(1.2) }, '+'),
453
+ React.createElement('button', { type: 'button', onClick: fit }, '适应窗口')),
454
+ (display.hidden.length > 0 || expanded.size > 0 || collapsed.size > 0) && React.createElement('div', { className: 'dsh-super-code-history-controls' },
455
+ React.createElement('button', { type: 'button', 'aria-expanded': historyOpen, onClick: () => setHistoryOpen(!historyOpen) }, `已收起 ${display.hidden.length}`),
456
+ (expanded.size > 0 || collapsed.size > 0) && React.createElement('button', { type: 'button', onClick: () => { autoFit.current = true; setExpanded(new Set()); setCollapsed(new Set()); setFocused(state.current) } }, '自动整理')),
457
+ historyOpen && React.createElement(AgentHistory, { view, hidden: display.hidden, totals, open }),
458
+ React.createElement('div', { ref: viewport, className: 'dsh-super-code-graph-scroll',
459
+ onPointerDown: event => {
460
+ if (!event.isPrimary || event.button !== 0) return
461
+ gesture.current = { id: event.pointerId, x: event.clientX, y: event.clientY, origin: pan, moved: false }
462
+ },
463
+ onPointerMove: event => {
464
+ const drag = gesture.current
465
+ if (drag.id !== event.pointerId) return
466
+ const dx = event.clientX - drag.x, dy = event.clientY - drag.y
467
+ if (!drag.moved && Math.hypot(dx, dy) < 6) return
468
+ autoFit.current = false
469
+ if (!drag.moved) { drag.moved = true; event.currentTarget.setPointerCapture(event.pointerId) }
470
+ event.currentTarget.classList.add('dragging')
471
+ const next = { x: drag.origin.x + dx, y: drag.origin.y + dy }
472
+ changeCamera(next, camera.current.zoom)
473
+ },
474
+ onPointerUp: finishDrag, onPointerCancel: finishDrag, onLostPointerCapture: finishDrag,
475
+ onClickCapture: event => { if (gesture.current.moved && event.detail !== 0) { event.preventDefault(); event.stopPropagation() } },
476
+ onDoubleClick: event => { if (!event.target.closest('[role="treeitem"]')) fit() },
477
+ title: '拖动平移,滚轮缩放,双击空白适应窗口',
478
+ },
479
+ React.createElement('div', { ref: graph, role: 'tree', 'aria-label': 'Agent 执行树', className: 'dsh-super-code-graph', style: { width, height, transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom})` } },
480
+ React.createElement('svg', { width, height, className: 'dsh-super-code-graph-edges', 'aria-hidden': true }, edges.map(edge => {
481
+ const parent = positions.get(edge.parent), child = positions.get(edge.child)
482
+ return React.createElement('path', { key: edge.child, d: `M ${parent.x} ${parent.y + 105} V ${child.y - 12} H ${child.x} V ${child.y}`, fill: 'none' })
483
+ })),
484
+ rows.map(row => {
485
+ const node = view.nodes.get(row.id)
486
+ const title = node.title || node.displayTitle || '主 Agent'
487
+ const { label: status, tone } = agentExecutionStatus(node, row.id === view.root)
488
+ const usage = totals.get(row.id)
489
+ const partial = usage.partial || usage.known < usage.agents
490
+ const number = value => value.toLocaleString('zh-CN', { notation: 'compact', maximumFractionDigits: 1 })
491
+ const hasChildren = (view.children.get(row.id) || []).length > 0
492
+ const isExpanded = (display.view.children.get(row.id) || []).length > 0
493
+ return React.createElement(React.Fragment, { key: row.id }, React.createElement('button', {
494
+ type: 'button',
495
+ role: 'treeitem', tabIndex: row.id === focusId ? 0 : -1, 'data-agent-id': row.id,
496
+ 'aria-level': row.level, 'aria-selected': row.id === state.current,
497
+ 'aria-expanded': hasChildren ? isExpanded : undefined,
498
+ 'aria-disabled': node.unavailable || undefined, 'aria-label': `${title},${status}`,
499
+ className: `dsh-super-code-graph-node${row.id === state.current ? ' selected' : ''}${node.running ? ' running' : ''}`,
500
+ style: { left: row.x - 64, top: row.y }, title: `${title} · ${status} · 点击查看详情`,
501
+ onFocus: () => setFocused(row.id), onClick: () => { if (!node.unavailable) open(node) },
502
+ onKeyDown: event => { if (event.target === event.currentTarget) keyDown(event, row) },
503
+ },
504
+ React.createElement('span', { className: 'dsh-super-code-circle ' + tone, 'aria-hidden': true }, row.id === view.root ? '主' : String(rows.findIndex(item => item.id === row.id))),
505
+ React.createElement('span', { className: 'dsh-super-code-tree-content' },
506
+ React.createElement('span', { className: 'dsh-super-code-tree-title' }, title),
507
+ React.createElement('span', { className: 'dsh-super-code-tree-usage', title: `本节点及所有已知下级合计,包含本节点。${partial ? '部分记录或用量尚不可用;仅显示已知值。' : ''}Token ${usage.tokens.toLocaleString('zh-CN')};缓存读取 ${usage.cache.toLocaleString('zh-CN')};${usage.agents} 个 Agent` },
508
+ React.createElement('span', null, usage.known ? `${number(usage.tokens)} token` : '用量 —'),
509
+ React.createElement('span', null, usage.known ? `缓存 ${usage.input ? Math.round(usage.cache / usage.input * 100) : 0}%${partial ? ' · 部分' : ''}` : '缓存 —')))),
510
+ hasChildren && React.createElement('button', { type: 'button', className: 'dsh-super-code-branch-toggle',
511
+ style: { left: row.x + 21, top: row.y + 25 }, 'aria-label': `${collapsed.has(row.id) || !isExpanded ? '展开' : '收起'} ${title} 的下级`,
512
+ title: '整理下级节点;运行中的分支始终保留', onClick: () => toggleBranch(row.id) },
513
+ React.createElement('svg', { width: 12, height: 12, viewBox: '0 0 12 12', 'aria-hidden': true, style: { transform: collapsed.has(row.id) || !isExpanded ? 'rotate(-90deg)' : undefined } },
514
+ React.createElement('path', { d: 'M3 4.5 6 7.5 9 4.5', fill: 'none', stroke: 'currentColor', strokeWidth: 1.5 }))))
515
+ }))),
516
+ more && React.createElement('button', { type: 'button', className: 'dsh-super-code-tree-more', onClick: () => setLimit(limit + 200) }, '显示更多'))
517
+ }
518
+
519
+ function agentDetailAddress(node) {
520
+ const address = new URL('dsh-resource://super-agent/' + encodeURIComponent(node.id))
521
+ if (node.parentId && node.mode) {
522
+ address.searchParams.set('parent', node.parentId)
523
+ address.searchParams.set('mode', node.mode)
524
+ }
525
+ return address.href
526
+ }
527
+
528
+ function agentDetailTarget(address) {
529
+ const url = new URL(address)
530
+ if (url.protocol !== 'dsh-resource:' || url.hostname !== 'super-agent') throw new Error('Invalid Agent address')
531
+ const id = decodeURIComponent(url.pathname.slice(1))
532
+ if (!id) throw new Error('Missing Agent identity')
533
+ const parent = url.searchParams.get('parent'), mode = url.searchParams.get('mode')
534
+ if (parent && (mode === 'one-shot' || mode === 'continuable')) return { kind: 'subagent', parentSessionId: parent, childSessionId: id, mode }
535
+ return { kind: 'session', sessionId: id }
536
+ }
537
+
538
+ function agentPath(view, id) {
539
+ const path = [], seen = new Set()
540
+ while (id && !seen.has(id)) {
541
+ seen.add(id)
542
+ const node = view.nodes.get(id)
543
+ path.unshift({ id, title: node?.title || node?.displayTitle || id })
544
+ id = node?.parentId
545
+ }
546
+ return path
547
+ }
548
+
549
+ function contentText(blocks) {
550
+ return (blocks || []).map(block => {
551
+ if (block.type === 'text' && typeof block.text === 'string') return block.text
552
+ if (block.type === 'image') return '[图片]'
553
+ return ''
554
+ }).filter(Boolean).join('\n\n')
555
+ }
556
+
557
+ function detailRecord(entry) {
558
+ const event = entry.event, data = event?.data
559
+ if (!data) return { kind: 'skip' }
560
+ const base = { seq: event.seq, time: event.time }
561
+ if (event.type === 'user/message') {
562
+ if (data.source?.kind !== 'user') return { kind: 'skip' }
563
+ return { ...base, kind: 'message', label: '任务', text: contentText(data.content) }
564
+ }
565
+ if (event.type === 'assistant/message') return { ...base, kind: 'message', label: data.interrupted ? 'Agent · 已中断' : 'Agent', text: contentText(data.message?.content) }
566
+ if (event.type === 'turn/error') return { ...base, kind: 'error', label: '执行错误', text: data.message || '执行未完成,请稍后重试。' }
567
+ return { kind: 'skip' }
568
+ }
569
+
570
+ // The host journal owns decoding, cursor validation and reconnects. This view
571
+ // keeps a bounded display window and never stages or sends to another session.
572
+ function createAgentInspector(createStream, address, readPage, initial) {
573
+ let snapshot = initial || { status: 'loading', items: [], hasMore: false, earlier: false, truncated: false, projections: {}, error: '', firstSeq: 0, cursor: -1 }
574
+ let cursor = snapshot.cursor
575
+ let disposed = false, stream
576
+ const listeners = new Set()
577
+ const publish = patch => {
578
+ if (disposed) return
579
+ if (patch.cursor !== undefined) cursor = patch.cursor
580
+ snapshot = { ...snapshot, cursor, ...patch }
581
+ listeners.forEach(listener => listener())
582
+ }
583
+ const itemsOf = entries => entries.map(detailRecord).filter(item => item.kind !== 'skip' && item.text)
584
+ stream = createStream(agentDetailTarget(address), {
585
+ publish: change => {
586
+ if (disposed) return
587
+ if (change.type === 'replace') {
588
+ if (snapshot.earlier) {
589
+ publish({ status: 'ready', projections: change.page.projections?.values || {}, error: '' })
590
+ return
591
+ }
592
+ const fresh = itemsOf(change.entries)
593
+ const sequences = new Set(fresh.map(item => item.seq))
594
+ const overlaps = snapshot.items.some(item => sequences.has(item.seq))
595
+ // A resumed tail page can start later than the user's reading window.
596
+ // Immutable journal records can be merged without moving that window.
597
+ const items = overlaps ? [...new Map([...snapshot.items, ...fresh].map(item => [item.seq, item])).values()].sort((a, b) => a.seq - b.seq) : fresh
598
+ publish({ status: 'ready', items: items.slice(-200), hasMore: (overlaps ? snapshot.hasMore : change.hasMore) || items.length > 200, earlier: false, truncated: items.length > 200,
599
+ firstSeq: items.length > 200 ? items.at(-200).seq : overlaps ? snapshot.firstSeq : change.entries[0]?.event.seq ?? 0,
600
+ cursor: change.entries.at(-1)?.event.seq ?? -1,
601
+ projections: change.page.projections?.values || {}, error: '' })
602
+ } else if (change.type === 'append' && !snapshot.earlier) {
603
+ const item = detailRecord(change.entry)
604
+ cursor = change.entry.event.seq
605
+ if (item.kind !== 'skip' && item.text) {
606
+ const items = [...snapshot.items, item]
607
+ publish({ items: items.slice(-200), cursor, truncated: snapshot.truncated || items.length > 200,
608
+ firstSeq: items.length > 200 ? items.at(-200).seq : snapshot.firstSeq, hasMore: snapshot.hasMore || items.length > 200 })
609
+ }
610
+ }
611
+ },
612
+ carrierFailed: () => publish({ error: '连接中断,正在重连' }),
613
+ failed: () => publish({ status: 'error', error: '对话详情读取失败' }),
614
+ })
615
+ return {
616
+ getSnapshot: () => snapshot,
617
+ subscribe: listener => { listeners.add(listener); return () => listeners.delete(listener) },
618
+ start: () => { stream.open({ maxMessages: 40 }).catch(() => publish({ status: 'error', error: '对话详情读取失败' })) },
619
+ older: async () => {
620
+ if (snapshot.status === 'paging' || !snapshot.hasMore) return
621
+ publish({ status: 'paging', error: '' })
622
+ const throughSeq = cursor
623
+ try {
624
+ const result = await readPage({ address: agentDetailTarget(address), throughSeq, beforeSeq: snapshot.firstSeq, maxMessages: 40 }, stream.signal)
625
+ if (!result.ok) throw new Error('History unavailable')
626
+ const items = itemsOf(result.value.records), visible = items.slice(-200)
627
+ publish({ status: 'ready', items: visible, earlier: true, cursor: throughSeq, truncated: items.length > 200,
628
+ firstSeq: items.length > 200 ? visible[0].seq : result.value.records[0]?.event.seq ?? 0,
629
+ hasMore: result.value.hasMore || items.length > 200 })
630
+ }
631
+ catch { publish({ status: 'error', error: '历史记录读取失败' }) }
632
+ },
633
+ dispose: () => { disposed = true; listeners.clear(); void stream.dispose() },
634
+ }
635
+ }
636
+
637
+ function DetailText({ text }) {
638
+ const [full, setFull] = useState(false)
639
+ return React.createElement(React.Fragment, null,
640
+ React.createElement('pre', null, full ? text : text.slice(0, 12000)),
641
+ !full && text.length > 12000 && React.createElement('button', { type: 'button', onClick: () => setFull(true) }, '展开全文'))
642
+ }
643
+
644
+ function AgentDetail({ useTabInfo, useSessions, createInspector, detailStates }) {
645
+ const { tab } = useTabInfo()
646
+ const state = useSessions(s => s)
647
+ const saved = useMemo(() => {
648
+ if (!detailStates.has(tab.signal)) detailStates.set(tab.signal, { top: 0, following: true, open: new Set(), snapshot: undefined })
649
+ return detailStates.get(tab.signal)
650
+ }, [detailStates, tab.signal])
651
+ const log = useRef(null)
652
+ const restored = useRef(false)
653
+ const previous = useRef(saved.snapshot)
654
+ const [unread, setUnread] = useState(saved.unread || false)
655
+ const markUnread = value => { saved.unread = value; setUnread(value) }
656
+ const [revision, setRevision] = useState(0)
657
+ const inspector = useMemo(() => createInspector(tab.contentId, saved.snapshot), [createInspector, tab.contentId, tab.visible, revision])
658
+ const snapshot = useSyncExternalStore(inspector.subscribe, inspector.getSnapshot, inspector.getSnapshot)
659
+ const latest = () => {
660
+ saved.snapshot = undefined; saved.following = true; saved.top = 0
661
+ restored.current = false; previous.current = undefined; markUnread(false)
662
+ setRevision(value => value + 1)
663
+ }
664
+ useLayoutEffect(() => {
665
+ const element = log.current
666
+ if (!element || snapshot.status === 'loading') return
667
+ if (!restored.current) {
668
+ element.scrollTop = saved.following && !snapshot.earlier ? element.scrollHeight : saved.top
669
+ restored.current = true
670
+ } else if (snapshot.items !== previous.current?.items) {
671
+ if (snapshot.earlier && snapshot.firstSeq !== previous.current?.firstSeq) {
672
+ element.scrollTop = 0; saved.following = false
673
+ } else if (saved.following && !snapshot.earlier) element.scrollTop = element.scrollHeight
674
+ else if (snapshot.items.at(-1)?.seq > (previous.current?.items.at(-1)?.seq ?? -1)) markUnread(true)
675
+ }
676
+ saved.top = element.scrollTop; saved.snapshot = snapshot; previous.current = snapshot
677
+ const visible = new Set(snapshot.items.map(item => item.seq))
678
+ for (const key of saved.open) if (key !== 'usage' && !visible.has(key)) saved.open.delete(key)
679
+ }, [snapshot, saved])
680
+ useEffect(() => {
681
+ if (tab.visible) inspector.start()
682
+ return () => inspector.dispose()
683
+ }, [inspector, tab.visible])
684
+ const target = agentDetailTarget(tab.contentId), id = target.childSessionId || target.sessionId
685
+ const view = buildAgentView(state), node = view.nodes.get(id)
686
+ const path = agentPath(view, id)
687
+ const projections = state.byId[id]?.projectionValues || snapshot.projections
688
+ const totals = projections.superAgentUsage?.totals || projections.tokenUsage
689
+ const input = totals ? (totals.uncachedInputTokens || 0) + (totals.cacheReadTokens || 0) + (totals.cacheWriteTokens || 0) : 0
690
+ const format = value => value === undefined ? '未提供' : value.toLocaleString('zh-CN')
691
+ const tasks = projections.superCodeTasks
692
+ const task = tasks?.current?.kind === 'task' ? tasks.current : undefined
693
+ const title = node?.title || node?.displayTitle || 'Agent 详情'
694
+ const metrics = [
695
+ ['输入', totals ? input : undefined], ['输出', totals?.outputTokens],
696
+ ['缓存读取', totals?.cacheReadTokens], ['缓存写入', totals?.cacheWriteTokens],
697
+ ]
698
+ return React.createElement('section', { className: 'dsh-super-code-detail', 'data-agent-detail': id },
699
+ React.createElement('header', { className: 'dsh-super-code-detail-header' },
700
+ React.createElement('nav', { 'aria-label': 'Agent 路径' }, path.map((part, index) => React.createElement('span', { key: part.id }, index > 0 ? ' / ' : '', part.title))),
701
+ React.createElement('div', { className: 'dsh-super-code-detail-heading' },
702
+ React.createElement('div', null,
703
+ React.createElement('h2', null, title),
704
+ React.createElement('span', { className: 'dsh-super-code-detail-status' }, agentExecutionStatus(node, id === view.root).label)),
705
+ React.createElement('button', { type: 'button', className: 'dsh-super-code-icon-button', title: '刷新对话详情', 'aria-label': '刷新对话详情', onClick: latest }, React.createElement(IconRefreshOutline16))),
706
+ id !== view.root && task?.goal && task.goal !== title && React.createElement('p', { className: 'dsh-super-code-detail-task' }, task.goal),
707
+ id === view.root && React.createElement(TaskMemoryPanel, { state: tasks }),
708
+ React.createElement('dl', { className: 'dsh-super-code-detail-metrics' },
709
+ React.createElement('div', null, React.createElement('dt', null, 'Token 总量'), React.createElement('dd', null, format(totals ? input + (totals.outputTokens || 0) : undefined))),
710
+ React.createElement('div', null, React.createElement('dt', null, '缓存命中率'), React.createElement('dd', null, totals ? (input ? Math.round((totals.cacheReadTokens || 0) / input * 100) : 0) + '%' : '未提供'))),
711
+ React.createElement('details', { className: 'dsh-super-code-usage-details', open: saved.open.has('usage'), onToggle: event => { if (event.currentTarget.open) saved.open.add('usage'); else saved.open.delete('usage') } },
712
+ React.createElement('summary', null, '用量详情'),
713
+ React.createElement('dl', { className: 'dsh-super-code-detail-metrics' }, metrics.map(([label, value]) => React.createElement('div', { key: label }, React.createElement('dt', null, label), React.createElement('dd', null, format(value))))))),
714
+ React.createElement('div', { ref: log, className: 'dsh-super-code-detail-log', 'aria-label': 'Agent 执行内容', onScroll: event => {
715
+ const element = event.currentTarget
716
+ saved.top = element.scrollTop
717
+ saved.following = !snapshot.earlier && element.scrollHeight - element.clientHeight - element.scrollTop < 32
718
+ if (saved.following) markUnread(false)
719
+ } },
720
+ snapshot.error && React.createElement('p', { role: 'alert' }, snapshot.error),
721
+ snapshot.status === 'loading' && React.createElement('p', { role: 'status' }, '正在读取对话…'),
722
+ snapshot.hasMore && React.createElement('button', { type: 'button', disabled: snapshot.status === 'paging', onClick: inspector.older }, snapshot.status === 'paging' ? '读取中…' : '加载更早记录'),
723
+ (snapshot.earlier || snapshot.truncated) && React.createElement('button', { type: 'button', onClick: latest }, '回到最新'),
724
+ snapshot.status === 'ready' && !snapshot.items.length && React.createElement('p', null, '暂无执行内容'),
725
+ snapshot.items.map(item => React.createElement('article', { key: item.seq, className: 'dsh-super-code-detail-entry ' + item.kind },
726
+ item.collapsed ? React.createElement('details', { open: saved.open.has(item.seq), onToggle: event => { if (event.currentTarget.open) saved.open.add(item.seq); else saved.open.delete(item.seq) } },
727
+ React.createElement('summary', null, item.label),
728
+ React.createElement(DetailText, { text: item.text }))
729
+ : React.createElement(React.Fragment, null,
730
+ React.createElement('div', { className: 'dsh-super-code-detail-entry-label' }, item.label,
731
+ React.createElement('time', null, new Date(item.time).toLocaleTimeString('zh-CN', { hour12: false }))),
732
+ React.createElement(DetailText, { text: item.text })))) ),
733
+ unread && React.createElement('button', { type: 'button', className: 'dsh-super-code-new-content', onClick: () => {
734
+ saved.following = true; log.current.scrollTop = log.current.scrollHeight; markUnread(false)
735
+ } }, '有新内容 · 查看最新'))
736
+ }
737
+
738
+ const inject = [
739
+ 'slots', 'remote', 'remote.session', 'sessions', 'locale',
740
+ 'sidebarRightTabs', 'sidebarRight',
741
+ ]
742
+
743
+ function apply(ctx) {
744
+ ctx.effect(() => {
745
+ const style = document.createElement('style')
746
+ style.dataset.dshSuperCode = 'true'
747
+ style.textContent = `
748
+ .dsh-super-code-tree{--agent-muted:var(--dsw-alias-label-secondary,#70757d);--agent-line:var(--dsw-alias-border-l1,#dedfe3);height:100%;min-width:0;min-height:0;display:flex;flex-direction:column;overflow:auto;font-size:13px;letter-spacing:0;color:inherit}
749
+ .dsh-super-code-tree>header,.dsh-super-code-tree>.dsh-super-code-graph-controls,.dsh-super-code-tree>.dsh-super-code-graph-legend,.dsh-super-code-tree>.dsh-super-code-history-controls{flex-shrink:0}
750
+ .dsh-super-code-tree-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:20px 18px 16px}
751
+ .dsh-super-code-tree-header strong{display:block;font-size:14px;font-weight:600}
752
+ .dsh-super-code-tree-count{display:block;margin-top:5px;color:var(--agent-muted);font-size:12px}
753
+ .dsh-super-code-graph-controls{display:flex;align-items:center;gap:6px;padding:2px 18px 12px;color:var(--agent-muted);font-size:11px}
754
+ .dsh-super-code-graph-controls button{border:1px solid var(--agent-line);border-radius:5px;background:transparent;color:inherit;padding:3px 8px;min-height:28px;cursor:pointer;font:inherit}
755
+ .dsh-super-code-graph-controls button:disabled{opacity:.4;cursor:default}
756
+ .dsh-super-code-graph-controls span{min-width:38px;text-align:center;font-variant-numeric:tabular-nums}
757
+ .dsh-super-code-history-controls{display:flex;gap:8px;padding:0 18px 10px}
758
+ .dsh-super-code-history-controls button{border:1px solid var(--agent-line);border-radius:14px;background:transparent;color:var(--agent-muted);padding:5px 10px;font:inherit;font-size:11px;cursor:pointer}
759
+ .dsh-super-code-history-controls button[aria-expanded=true]{color:var(--dsw-color-primary,#3276dc);background:color-mix(in srgb,currentColor 5%,transparent)}
760
+ .dsh-super-code-history{display:flex;flex-direction:column;flex-shrink:0;box-sizing:border-box;height:46%;min-height:220px;max-height:400px;margin:0 12px 12px;padding:12px;border:1px solid var(--agent-line);border-radius:12px;background:color-mix(in srgb,var(--agent-muted) 3%,transparent)}
761
+ .dsh-super-code-history>p,.dsh-super-code-history>input,.dsh-super-code-history-filters{flex-shrink:0}
762
+ .dsh-super-code-history p{font-size:11px;color:var(--agent-muted);margin:0 0 10px;line-height:1.6}
763
+ .dsh-super-code-history input,.dsh-super-code-history select{box-sizing:border-box;min-width:0;width:100%;border:1px solid var(--agent-line);border-radius:7px;background:var(--dsw-alias-bg-base,#fff);color:var(--dsw-alias-text-primary,inherit);font:inherit;font-size:12px;padding:7px 8px}
764
+ .dsh-super-code-history>input{margin-bottom:8px}
765
+ .dsh-super-code-history-filters{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:8px;margin-bottom:8px}
766
+ .dsh-super-code-history-filters label{display:flex;flex-direction:column;gap:4px;min-width:0;font-size:10px;color:var(--agent-muted)}
767
+ .dsh-super-code-history-filters select{cursor:pointer}
768
+ .dsh-super-code-history input:focus-visible,.dsh-super-code-history select:focus-visible,.dsh-super-code-history-list button:focus-visible{outline:2px solid var(--dsw-color-primary,#3276dc);outline-offset:-2px}
769
+ .dsh-super-code-history p.dsh-super-code-history-summary{font-size:10px;margin-bottom:6px}
770
+ .dsh-super-code-history-list{flex:1;min-height:56px;overflow:auto;overflow-anchor:none;overscroll-behavior:contain;scrollbar-gutter:stable}
771
+ .dsh-super-code-history-list button{display:flex;align-items:center;box-sizing:border-box;gap:9px;width:100%;height:56px;text-align:left;border:0;border-radius:7px;padding:9px 5px;background:transparent;color:inherit;cursor:pointer;font:inherit}
772
+ .dsh-super-code-history-list button:hover{background:color-mix(in srgb,currentColor 5%,transparent)}
773
+ .dsh-super-code-history-list button>span{flex:1;min-width:0}.dsh-super-code-history-list strong{display:block;font-size:12px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
774
+ .dsh-super-code-history-list small{font-size:10px;color:var(--agent-muted);line-height:1.6}.dsh-super-code-history-list button>small{white-space:nowrap;font-variant-numeric:tabular-nums}
775
+ .dsh-super-code-history-list button>span>small{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
776
+ .dsh-super-code-history-list .dsh-super-code-circle{width:7px;height:7px;box-shadow:none}
777
+ .dsh-super-code-history-empty{padding:14px 5px}
778
+ .dsh-super-code-branch-toggle{position:absolute;display:grid;place-items:center;width:22px;height:22px;padding:0;border:1px solid var(--agent-line);border-radius:50%;background:var(--dsw-alias-bg-base,#fff);color:var(--agent-muted);cursor:pointer;box-shadow:0 1px 3px #0000000a}
779
+ .dsh-super-code-branch-toggle:hover{color:var(--dsw-color-primary,#3276dc);border-color:currentColor}
780
+ .dsh-super-code-graph-scroll{overflow:hidden;padding:0 12px;min-height:180px;flex:1;touch-action:none;cursor:grab;user-select:none}
781
+ .dsh-super-code-graph-scroll.dragging,.dsh-super-code-graph-scroll.dragging *{cursor:grabbing!important}
782
+ .dsh-super-code-graph-legend{display:flex;flex-wrap:wrap;gap:6px 12px;padding:0 18px 8px;color:var(--agent-muted);font-size:10px}
783
+ .dsh-super-code-graph-legend>span{display:flex;align-items:center;gap:5px}
784
+ .dsh-super-code-graph-legend .dsh-super-code-circle{width:7px;height:7px;box-shadow:none}
785
+ .dsh-super-code-graph{position:relative;transform-origin:0 0}
786
+ .dsh-super-code-graph-edges{position:absolute;inset:0;pointer-events:none;stroke:var(--agent-line);stroke-width:1.5}
787
+ .dsh-super-code-graph-node{position:absolute;width:128px;min-height:100px;display:flex;flex-direction:column;align-items:center;gap:6px;border:0;padding:0 4px;background:transparent;color:inherit;font:inherit;cursor:pointer;border-radius:8px}
788
+ .dsh-super-code-circle{position:relative;display:flex;align-items:center;justify-content:center;flex:none;width:40px;height:40px;border-radius:50%!important;corner-shape:round!important;font-size:13px;font-weight:600;background:var(--state-color);color:#fff}
789
+ .dsh-super-code-circle.active{--state-color:#008b78}.dsh-super-code-circle.idle{--state-color:#3971de}.dsh-super-code-circle.stopped{--state-color:#777d87}.dsh-super-code-circle.unavailable{--state-color:#b87716}
790
+ .dsh-super-code-graph-node .dsh-super-code-circle{background:linear-gradient(145deg,color-mix(in srgb,var(--state-color) 83%,white),var(--state-color));box-shadow:inset 0 1px 1px #ffffff40,0 2px 6px #00000012}
791
+ .dsh-super-code-graph-node .dsh-super-code-circle:after{content:"";position:absolute;inset:-5px;border:1px solid color-mix(in srgb,var(--state-color) 22%,transparent);border-radius:50%;corner-shape:round;pointer-events:none}
792
+ .dsh-super-code-graph-node .dsh-super-code-circle.idle:after{animation:super-agent-breathe 3s ease-in-out infinite}
793
+ .dsh-super-code-graph-node .dsh-super-code-circle.active:after{border-width:2px;border-top-color:var(--state-color);border-right-color:var(--state-color);animation:super-agent-process 1.6s linear infinite}
794
+ .dsh-super-code-graph-node .dsh-super-code-circle.stopped{background:linear-gradient(145deg,#9297a0,#747a84);box-shadow:inset 0 1px 1px #ffffff30}
795
+ .dsh-super-code-graph-node .dsh-super-code-circle.stopped:after{border-color:color-mix(in srgb,var(--state-color) 14%,transparent)}
796
+ .dsh-super-code-graph-node .dsh-super-code-circle.unavailable:after{border-style:dashed;border-color:var(--state-color)}
797
+ .dsh-super-code-graph-node:hover .dsh-super-code-circle{box-shadow:inset 0 1px 1px #ffffff40,0 3px 10px color-mix(in srgb,var(--state-color) 25%,transparent)}
798
+ @keyframes super-agent-breathe{0%,100%{transform:scale(1);opacity:.35}50%{transform:scale(1.12);opacity:.85}}
799
+ @keyframes super-agent-process{to{transform:rotate(360deg)}}
800
+ .dsh-super-code-graph-node:focus-visible{outline:2px solid var(--dsw-color-primary,#3276dc);outline-offset:4px}
801
+ .dsh-super-code-graph-node[aria-disabled]{cursor:default;opacity:.65}
802
+ .dsh-super-code-graph-node .dsh-super-code-tree-content{width:100%;text-align:center}
803
+ .dsh-super-code-graph-node .dsh-super-code-tree-title{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:18px;height:18px;font-size:12px}
804
+ .dsh-super-code-graph-node .dsh-super-code-tree-usage{justify-content:center;gap:1px 7px;font-size:10px}
805
+ .dsh-super-code-icon-button,.dsh-super-code-tree-toggle{display:inline-flex;align-items:center;justify-content:center;flex:none;width:28px;height:28px;padding:0;border:0;border-radius:4px;background:transparent;color:var(--agent-muted);cursor:pointer}
806
+ .dsh-super-code-icon-button:hover,.dsh-super-code-tree-toggle:hover{background:color-mix(in srgb,currentColor 9%,transparent)}
807
+ .dsh-super-code-icon-button:disabled{opacity:.45;cursor:default}
808
+ .dsh-super-code-view-button{flex:none;border:0;border-radius:4px;padding:6px 8px;background:transparent;color:var(--agent-muted);font:inherit;font-size:12px;cursor:pointer}
809
+ .dsh-super-code-view-button:hover{background:color-mix(in srgb,currentColor 7%,transparent);color:inherit}
810
+ .dsh-super-code-view-button:disabled{opacity:.45;cursor:default}
811
+ .dsh-super-code-tree-rows{padding:0 12px 20px}
812
+ .dsh-super-code-tree-row{position:relative;display:flex;align-items:center;gap:6px;min-width:0;min-height:64px;padding:10px 8px 10px calc(4px + var(--agent-depth)*14px);border-radius:6px;cursor:default;box-sizing:border-box}
813
+ .dsh-super-code-tree-row[aria-expanded]{cursor:pointer}
814
+ .dsh-super-code-tree-row[aria-level]:not([aria-level="1"]):before{content:"";position:absolute;left:15px;width:calc(var(--agent-depth)*14px);height:100%;top:0;background:repeating-linear-gradient(to right,var(--agent-line) 0,var(--agent-line) 1px,transparent 1px,transparent 14px);pointer-events:none}
815
+ .dsh-super-code-tree-row:hover{background:color-mix(in srgb,currentColor 5%,transparent)}
816
+ .dsh-super-code-tree-row.selected{background:color-mix(in srgb,var(--dsw-color-primary,#3276dc) 6%,transparent)}
817
+ .dsh-super-code-tree-row:focus-visible,.dsh-super-code-tree button:focus-visible{outline:2px solid var(--dsw-color-primary,#3276dc);outline-offset:-2px}
818
+ .dsh-super-code-tree-row[aria-disabled]{cursor:default;opacity:.7}
819
+ .dsh-super-code-tree-toggle{width:22px;height:26px;z-index:1}
820
+ .dsh-super-code-tree-toggle svg{transition:transform .12s}.dsh-super-code-tree-toggle.expanded svg{transform:rotate(90deg)}
821
+ .dsh-super-code-tree-connector{width:22px;flex:none}
822
+ .dsh-super-code-tree-content{display:grid;gap:4px;flex:1;min-width:0}
823
+ .dsh-super-code-tree-title{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;overflow-wrap:anywhere;line-height:1.4}
824
+ .dsh-super-code-tree-meta{color:var(--agent-muted);font-size:11px;line-height:1.4;overflow-wrap:anywhere}
825
+ .dsh-super-code-tree-usage{display:flex;flex-wrap:wrap;gap:3px 10px;font-size:10px;line-height:1.6;color:var(--agent-muted);font-variant-numeric:tabular-nums}
826
+ .dsh-super-code-tree-usage>span{white-space:nowrap}
827
+ .dsh-super-code-state{display:inline-flex;align-items:center;gap:5px;padding:2px 6px;border-radius:5px;color:var(--state-color);background:color-mix(in srgb,var(--state-color) 9%,transparent)}
828
+ .dsh-super-code-state:before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor}
829
+ .dsh-super-code-state.active{--state-color:#16877d}
830
+ .dsh-super-code-state.idle{--state-color:#5471bb}
831
+ .dsh-super-code-state.stopped{--state-color:#827299}
832
+ .dsh-super-code-state.unavailable{--state-color:#b77b29}
833
+ @media(prefers-color-scheme:dark){.dsh-super-code-state.active{--state-color:#6bc7b6}.dsh-super-code-state.idle{--state-color:#91b0f4}.dsh-super-code-state.stopped{--state-color:#b9a9cf}.dsh-super-code-state.unavailable{--state-color:#e8b971}}
834
+ .dsh-super-code-tree-status{width:6px;height:6px;border-radius:50%;background:var(--agent-line);flex:none}
835
+ .dsh-super-code-tree-row.running .dsh-super-code-tree-status{background:#168c78}
836
+ .dsh-super-code-tree-status.unavailable{background:#c17e1d}
837
+ .dsh-super-code-tree-note,.dsh-super-code-tree-empty{margin:10px 18px;color:var(--agent-muted);font-size:12px}
838
+ .dsh-super-code-tree-error{margin:12px 14px;color:var(--dsw-color-text-error,#bd423b);font-size:12px;overflow-wrap:anywhere}
839
+ .dsh-super-code-tree-more{display:block;margin:4px auto 14px;padding:6px 12px;border:1px solid var(--agent-line);border-radius:4px;background:transparent;color:inherit;cursor:pointer}
840
+ .dsh-super-code-detail{height:100%;min-width:0;display:flex;flex-direction:column;font-size:13px;letter-spacing:0;--agent-muted:var(--dsw-alias-label-secondary,#70757d);--agent-line:var(--dsw-alias-border-l1,#dedfe3)}
841
+ .dsh-super-code-detail-header{padding:20px 20px 18px;border-bottom:1px solid var(--agent-line);flex:none;max-height:45%;overflow:auto}
842
+ .dsh-super-code-detail-header nav{font-size:11px;line-height:1.5;color:var(--agent-muted);overflow-wrap:anywhere}
843
+ .dsh-super-code-detail-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-top:14px}
844
+ .dsh-super-code-detail-heading>div{min-width:0}
845
+ .dsh-super-code-detail h2{font-size:16px;line-height:1.5;font-weight:600;margin:0;overflow-wrap:anywhere}
846
+ .dsh-super-code-detail-task{font-size:13px;line-height:1.7;margin:12px 0 0;overflow-wrap:anywhere}
847
+ .dsh-super-code-task-memory{margin-top:16px;padding-top:14px;border-top:1px solid var(--agent-line);font-size:12px;line-height:1.7;overflow-wrap:anywhere}
848
+ .dsh-super-code-task-heading{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px}
849
+ .dsh-super-code-task-heading strong{font-weight:500;color:inherit}
850
+ .dsh-super-code-task-heading span{color:var(--agent-muted);font-size:11px}
851
+ .dsh-super-code-task-memory p{margin:6px 0 0}
852
+ .dsh-super-code-task-memory details{margin-top:8px;color:var(--agent-muted)}
853
+ .dsh-super-code-task-memory summary{cursor:pointer}
854
+ .dsh-super-code-detail-metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px 16px;margin:16px 0 0}
855
+ .dsh-super-code-usage-details{margin-top:14px;font-size:12px;color:var(--agent-muted)}
856
+ .dsh-super-code-usage-details summary{cursor:pointer}
857
+ .dsh-super-code-new-content{align-self:center;flex:none;margin:8px 16px 12px;padding:7px 12px;border:1px solid var(--agent-line);border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}
858
+ .dsh-super-code-detail-metrics dt{font-size:11px;color:var(--agent-muted)}
859
+ .dsh-super-code-detail-metrics dd{margin:5px 0 0;font-variant-numeric:tabular-nums;font-size:14px;line-height:1.4;overflow-wrap:anywhere}
860
+ .dsh-super-code-detail-metrics>div:first-child dd{font-weight:600}
861
+ .dsh-super-code-detail-status{display:block;margin-top:5px;font-size:11px;color:var(--agent-muted)}
862
+ .dsh-super-code-detail-log{flex:1;min-height:0;overflow:auto;padding:4px 20px 24px}
863
+ .dsh-super-code-detail-log>button,.dsh-super-code-detail-entry button{border:1px solid var(--agent-line);border-radius:4px;background:transparent;color:inherit;padding:5px 8px;margin:10px 8px 4px 0;font-size:12px;cursor:pointer}
864
+ .dsh-super-code-detail-entry{padding:20px 0;min-width:0}
865
+ .dsh-super-code-detail-entry:has(>details){padding:10px 0;color:var(--agent-muted)}
866
+ .dsh-super-code-detail-entry details[open]{color:inherit;padding-bottom:8px}
867
+ .dsh-super-code-detail-entry-label{display:flex;justify-content:space-between;gap:10px;font-size:12px;font-weight:600}
868
+ .dsh-super-code-detail-entry time{color:var(--agent-muted);font-size:11px;font-weight:400;flex:none}
869
+ .dsh-super-code-detail-entry pre{font:inherit;line-height:1.7;white-space:pre-wrap;overflow-wrap:anywhere;margin:8px 0 0}
870
+ .dsh-super-code-detail-entry summary{cursor:pointer;font-size:12px;overflow-wrap:anywhere}
871
+ .dsh-super-code-detail-entry.context{color:var(--agent-muted)}
872
+ .dsh-super-code-detail-entry.error,.dsh-super-code-detail [role=alert]{color:var(--dsw-color-text-error,#bd423b)}
873
+ .dsh-super-code-detail button:focus-visible,.dsh-super-code-detail summary:focus-visible{outline:2px solid var(--dsw-color-primary,#3276dc);outline-offset:3px}
874
+ .dsh-super-code-detail-entry summary:hover{color:var(--dsw-alias-label-primary,inherit)}
875
+ @media(prefers-reduced-motion:reduce){.dsh-super-code-tree-toggle svg{transition:none}.dsh-super-code-graph-node .dsh-super-code-circle:after{animation:none!important}}
876
+ `
877
+ document.head.appendChild(style)
878
+ return () => { style.remove() }
879
+ }, 'super-code: browser styles')
880
+ // Both the tree and read-only details stay in the current conversation's tabs.
881
+ const treeId = 'dsh-super-code'
882
+ const treeKind = 'super-agent-agents'
883
+ const disposeTreeType = ctx.sidebarRightTabs.register({
884
+ id: treeId, kind: treeKind,
885
+ title: () => 'Agent 执行树',
886
+ guide: [{ order: 40, title: () => 'Agent 执行树', description: () => '查看 Agent 层级和执行对话' }],
887
+ })
888
+ const treeActions = {
889
+ // The host unmounts hidden bodies; tab signals live until close/unload.
890
+ treeStates: new WeakMap(),
891
+ refresh: id => ctx.sessions.refreshSubagents(id),
892
+ watchCatalog: (id, open) => ctx.sessions.setSubagentCatalogOpen(id, open),
893
+ openDetail: child => ctx.sidebarRight.openResource(agentDetailAddress(child), { kind: 'super-agent-detail' }),
894
+ }
895
+ const disposeDetailType = ctx.sidebarRightTabs.register({
896
+ id: 'dsh-super-code-detail', kind: 'super-agent-detail', patterns: ['dsh-resource://super-agent/**'],
897
+ title: address => {
898
+ const target = agentDetailTarget(address), state = ctx.sessions.list.getSnapshot()
899
+ const node = buildAgentView(state).nodes.get(target.childSessionId || target.sessionId)
900
+ return node?.title || node?.displayTitle || 'Agent 详情'
901
+ },
902
+ })
903
+ const detailActions = { detailStates: new WeakMap(), createInspector: (address, initial) => createAgentInspector(
904
+ (target, options) => new SessionEventStream(ctx.remote, target, options), address,
905
+ (request, signal) => ctx.remote.session.page(request, signal), initial) }
906
+ ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({
907
+ name: 'sidebar.right.pane.tab', key: 'dsh-super-code-detail', inject: () => detailActions,
908
+ }, AgentDetail))
909
+ ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({
910
+ // Keyed tab bodies dispatch by the definition id, while `kind` is the
911
+ // page/content discriminator used by the Sidebar controller.
912
+ name: 'sidebar.right.pane.tab', key: treeId,
913
+ inject: () => treeActions,
914
+ }, AgentTree))
915
+ ctx.effect(() => disposeTreeType, 'super-code: Agent tree tab')
916
+ ctx.effect(() => disposeDetailType, 'super-code: Agent detail tab')
917
+ }
918
+
919
+ return { inject, apply, buildAgentView, visibleAgentRows, agentTreeDisplay, agentTreeTotals, agentTreeLayout, agentExecutionStatus, agentDetailAddress, agentDetailTarget, agentPath, detailRecord, createAgentInspector, agentHistoryRange, agentHistoryRows, agentHistoryWindow }
920
+ } })