dsh-better-sidebar 0.10.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 (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +139 -0
  3. package/README_EN.md +139 -0
  4. package/lib/client-docx.js +7649 -0
  5. package/lib/client-editor.js +32677 -0
  6. package/lib/client-pptx.js +63438 -0
  7. package/lib/client-registry.js +8021 -0
  8. package/lib/client-terminal.js +8060 -0
  9. package/lib/client-xlsx.js +359755 -0
  10. package/lib/client.js +8021 -0
  11. package/lib/index.js +2604 -0
  12. package/lib/invariant.js +22 -0
  13. package/lib/types/agent-pty.d.ts +220 -0
  14. package/lib/types/browser-probe.d.ts +15 -0
  15. package/lib/types/bundle-route.d.ts +13 -0
  16. package/lib/types/client/BrowserView.d.ts +22 -0
  17. package/lib/types/client/DiffTab.d.ts +6 -0
  18. package/lib/types/client/DiffView.d.ts +51 -0
  19. package/lib/types/client/EditorHost.d.ts +10 -0
  20. package/lib/types/client/ExplorerView.d.ts +9 -0
  21. package/lib/types/client/GitView.d.ts +8 -0
  22. package/lib/types/client/OrphanedTab.d.ts +11 -0
  23. package/lib/types/client/PdfView.d.ts +6 -0
  24. package/lib/types/client/PptxView.d.ts +6 -0
  25. package/lib/types/client/SandboxStatusBar.d.ts +10 -0
  26. package/lib/types/client/SideCardSection.d.ts +27 -0
  27. package/lib/types/client/Sidebar.d.ts +6 -0
  28. package/lib/types/client/SubagentView.d.ts +16 -0
  29. package/lib/types/client/TabBar.d.ts +38 -0
  30. package/lib/types/client/TerminalView.d.ts +8 -0
  31. package/lib/types/client/TextEditor.d.ts +10 -0
  32. package/lib/types/client/api.d.ts +170 -0
  33. package/lib/types/client/binary-download.d.ts +12 -0
  34. package/lib/types/client/breakpoints.d.ts +12 -0
  35. package/lib/types/client/browser.d.ts +50 -0
  36. package/lib/types/client/builtins/index.d.ts +17 -0
  37. package/lib/types/client/builtins/tabs.d.ts +6 -0
  38. package/lib/types/client/builtins/viewers.d.ts +3 -0
  39. package/lib/types/client/chunk-loader.d.ts +74 -0
  40. package/lib/types/client/chunks/docx.d.ts +8 -0
  41. package/lib/types/client/chunks/editor.d.ts +9 -0
  42. package/lib/types/client/chunks/pptx.d.ts +8 -0
  43. package/lib/types/client/chunks/terminal.d.ts +9 -0
  44. package/lib/types/client/chunks/xlsx.d.ts +9 -0
  45. package/lib/types/client/cm-themes.d.ts +24 -0
  46. package/lib/types/client/conversation-draft.d.ts +14 -0
  47. package/lib/types/client/docx-view.d.ts +8 -0
  48. package/lib/types/client/editor-load.d.ts +66 -0
  49. package/lib/types/client/icons.d.ts +47 -0
  50. package/lib/types/client/image-types.d.ts +3 -0
  51. package/lib/types/client/ime-guard.d.ts +36 -0
  52. package/lib/types/client/index.d.ts +10 -0
  53. package/lib/types/client/intercept.d.ts +20 -0
  54. package/lib/types/client/lang.d.ts +12 -0
  55. package/lib/types/client/lazy-chunk.d.ts +25 -0
  56. package/lib/types/client/link-intercept.d.ts +35 -0
  57. package/lib/types/client/locales.d.ts +237 -0
  58. package/lib/types/client/office-shared.d.ts +33 -0
  59. package/lib/types/client/office-types.d.ts +36 -0
  60. package/lib/types/client/office-view.d.ts +12 -0
  61. package/lib/types/client/openpath-intercept.d.ts +41 -0
  62. package/lib/types/client/paths.d.ts +19 -0
  63. package/lib/types/client/pdf-types.d.ts +2 -0
  64. package/lib/types/client/prefs.d.ts +30 -0
  65. package/lib/types/client/produced-files.d.ts +27 -0
  66. package/lib/types/client/selection-payload.d.ts +27 -0
  67. package/lib/types/client/service.d.ts +214 -0
  68. package/lib/types/client/split-pane.d.ts +29 -0
  69. package/lib/types/client/state.d.ts +322 -0
  70. package/lib/types/client/subagent-activity.d.ts +35 -0
  71. package/lib/types/client/subagent-detect.d.ts +47 -0
  72. package/lib/types/client/subagent-jobs.d.ts +63 -0
  73. package/lib/types/client/theme.d.ts +28 -0
  74. package/lib/types/client/xlsx-to-univer.d.ts +33 -0
  75. package/lib/types/client/xlsx-view.d.ts +10 -0
  76. package/lib/types/config.d.ts +41 -0
  77. package/lib/types/context-types.d.ts +431 -0
  78. package/lib/types/fs-tree.d.ts +41 -0
  79. package/lib/types/git.d.ts +75 -0
  80. package/lib/types/html-route.d.ts +48 -0
  81. package/lib/types/index.d.ts +39 -0
  82. package/lib/types/invariant.d.ts +15 -0
  83. package/lib/types/jobs-routes.d.ts +45 -0
  84. package/lib/types/prefs-shared.d.ts +106 -0
  85. package/lib/types/pty-manager.d.ts +76 -0
  86. package/lib/types/tools.d.ts +28 -0
  87. package/lib/types/trust-fence.d.ts +24 -0
  88. package/lib/types/wire.d.ts +38 -0
  89. package/package.json +168 -0
  90. package/src/agent-pty.ts +519 -0
  91. package/src/browser-probe.ts +26 -0
  92. package/src/bundle-route.ts +130 -0
  93. package/src/client/BrowserView.tsx +253 -0
  94. package/src/client/DiffTab.tsx +110 -0
  95. package/src/client/DiffView.tsx +266 -0
  96. package/src/client/EditorHost.tsx +101 -0
  97. package/src/client/ExplorerView.tsx +283 -0
  98. package/src/client/GitView.tsx +540 -0
  99. package/src/client/OrphanedTab.tsx +27 -0
  100. package/src/client/PdfView.tsx +110 -0
  101. package/src/client/PptxView.tsx +131 -0
  102. package/src/client/SandboxStatusBar.tsx +60 -0
  103. package/src/client/SideCardSection.module.css +271 -0
  104. package/src/client/SideCardSection.tsx +426 -0
  105. package/src/client/Sidebar.tsx +861 -0
  106. package/src/client/SubagentView.module.css +547 -0
  107. package/src/client/SubagentView.tsx +866 -0
  108. package/src/client/TabBar.tsx +188 -0
  109. package/src/client/TerminalView.tsx +233 -0
  110. package/src/client/TextEditor.tsx +382 -0
  111. package/src/client/api.ts +215 -0
  112. package/src/client/binary-download.tsx +23 -0
  113. package/src/client/breakpoints.ts +51 -0
  114. package/src/client/browser.ts +119 -0
  115. package/src/client/builtins/index.ts +33 -0
  116. package/src/client/builtins/tabs.tsx +211 -0
  117. package/src/client/builtins/viewers.tsx +151 -0
  118. package/src/client/chunk-loader.ts +196 -0
  119. package/src/client/chunks/docx.tsx +8 -0
  120. package/src/client/chunks/editor.tsx +9 -0
  121. package/src/client/chunks/pptx.tsx +8 -0
  122. package/src/client/chunks/terminal.tsx +9 -0
  123. package/src/client/chunks/xlsx.tsx +9 -0
  124. package/src/client/cm-themes.ts +129 -0
  125. package/src/client/conversation-draft.ts +29 -0
  126. package/src/client/css-modules.d.ts +5 -0
  127. package/src/client/docx-view.tsx +113 -0
  128. package/src/client/editor-load.ts +92 -0
  129. package/src/client/icons.tsx +137 -0
  130. package/src/client/image-types.ts +8 -0
  131. package/src/client/ime-guard.ts +47 -0
  132. package/src/client/index.tsx +238 -0
  133. package/src/client/intercept.tsx +94 -0
  134. package/src/client/lang.ts +95 -0
  135. package/src/client/layout.css +54 -0
  136. package/src/client/lazy-chunk.tsx +89 -0
  137. package/src/client/link-intercept.ts +66 -0
  138. package/src/client/locales.ts +476 -0
  139. package/src/client/office-shared.tsx +40 -0
  140. package/src/client/office-types.ts +48 -0
  141. package/src/client/office-view.tsx +12 -0
  142. package/src/client/openpath-intercept.ts +62 -0
  143. package/src/client/paths.ts +28 -0
  144. package/src/client/pdf-types.ts +4 -0
  145. package/src/client/prefs.ts +100 -0
  146. package/src/client/produced-files.ts +87 -0
  147. package/src/client/selection-payload.ts +86 -0
  148. package/src/client/service.ts +434 -0
  149. package/src/client/sidebar.module.css +1917 -0
  150. package/src/client/split-pane.tsx +292 -0
  151. package/src/client/state.ts +1048 -0
  152. package/src/client/subagent-activity.ts +67 -0
  153. package/src/client/subagent-detect.ts +125 -0
  154. package/src/client/subagent-jobs.ts +161 -0
  155. package/src/client/theme.ts +44 -0
  156. package/src/client/xlsx-to-univer.ts +260 -0
  157. package/src/client/xlsx-view.tsx +116 -0
  158. package/src/config.ts +95 -0
  159. package/src/context-types.ts +445 -0
  160. package/src/fs-tree.ts +115 -0
  161. package/src/git.ts +236 -0
  162. package/src/html-route.ts +70 -0
  163. package/src/index.ts +897 -0
  164. package/src/invariant.ts +32 -0
  165. package/src/jobs-routes.ts +257 -0
  166. package/src/prefs-shared.ts +127 -0
  167. package/src/pty-manager.ts +199 -0
  168. package/src/tools.ts +477 -0
  169. package/src/trust-fence.ts +77 -0
  170. package/src/wire.ts +94 -0
@@ -0,0 +1,116 @@
1
+ /**
2
+ * The .xlsx preview component: renders via the Univer sheets preset (data +
3
+ * formulas + formatting). Lives in its own module (split out of the former
4
+ * office-view.tsx) so it builds as a standalone lazy chunk
5
+ * (`lib/client-xlsx.js`) — the Univer family and SheetJS pull in several MB
6
+ * but are loaded only when a .xlsx is first opened. The docx stack never
7
+ * shares this script.
8
+ *
9
+ * Errors degrade to the shared download-button affordance, so a corrupted /
10
+ * encrypted / oversized file always leaves the user with a way to get it.
11
+ */
12
+ import { useEffect, useRef, useState } from 'react'
13
+ import { mediaUrl } from './api.ts'
14
+ import { isZh, t } from './locales.ts'
15
+ import { BinaryFallback, type LoadState, type OfficeViewProps } from './office-shared.tsx'
16
+ import { xlsxWorkbookToUniver } from './xlsx-to-univer.ts'
17
+ import css from './sidebar.module.css'
18
+ // Univer's stylesheets ride the same dsh-css-inline pipeline as xterm's CSS
19
+ // (one <style data-plugin-css> tag, idempotent). Static import so the styles
20
+ // are present before the first .xlsx opens.
21
+ import '@univerjs/preset-sheets-core/lib/index.css'
22
+
23
+ /**
24
+ * Render a .xlsx file via Univer. The sheets preset creates a canvas-based
25
+ * spreadsheet (formula bar, sheet tabs, formula engine) sized to its
26
+ * container, so the host fills the pane. Unmounting calls `univer.dispose()`
27
+ * — without it the canvas, workers, and DOM listeners leak (mirrors the
28
+ * xterm dispose discipline in TerminalView).
29
+ */
30
+ export function XlsxView(props: OfficeViewProps): JSX.Element {
31
+ const { scope, path, title } = props
32
+ const hostRef = useRef<HTMLDivElement>(null)
33
+ const univerRef = useRef<{ dispose: () => void } | null>(null)
34
+ const [load, setLoad] = useState<LoadState>({ status: 'loading' })
35
+
36
+ useEffect(() => {
37
+ let cancelled = false
38
+ const host = hostRef.current
39
+ if (host === null) return
40
+ void (async () => {
41
+ try {
42
+ const response = await fetch(mediaUrl(scope, path))
43
+ if (cancelled) return
44
+ if (!response.ok) {
45
+ throw new Error(`HTTP ${response.status}`)
46
+ }
47
+ const buf = await response.arrayBuffer()
48
+ if (cancelled) return
49
+
50
+ // Dynamic imports — collapsed into the chunk by codeSplitting:false.
51
+ // The dynamic form keeps the source readable: each lib is only pulled
52
+ // in when an .xlsx is actually opened (semantically; the chunk still
53
+ // contains all of them — the chunk itself only loads on first .xlsx).
54
+ const XLSX = await import('xlsx')
55
+ const { createUniver, LocaleType, mergeLocales } = await import('@univerjs/presets')
56
+ const { UniverSheetsCorePreset } = await import('@univerjs/preset-sheets-core')
57
+ // Locales follow the DSH i18n system (the active locale); falls
58
+ // back to the browser language, then en-US.
59
+ const localePack = await (isZh()
60
+ ? import('@univerjs/preset-sheets-core/locales/zh-CN').then(m => m.default).catch(() => null)
61
+ : import('@univerjs/preset-sheets-core/locales/en-US').then(m => m.default).catch(() => null))
62
+
63
+ const wb = XLSX.read(buf, { type: 'array' })
64
+ const locale = isZh() ? LocaleType.ZH_CN : LocaleType.EN_US
65
+ const workbookData = xlsxWorkbookToUniver(wb, '0.25.1', locale)
66
+
67
+ if (cancelled) return
68
+ const { univer, univerAPI } = createUniver({
69
+ locale,
70
+ locales: localePack !== null ? { [locale]: mergeLocales(localePack) } : {},
71
+ presets: [UniverSheetsCorePreset({ container: host })],
72
+ })
73
+ univerRef.current = univer
74
+ univerAPI.createWorkbook(workbookData)
75
+ if (!cancelled) setLoad({ status: 'ready' })
76
+ } catch (error) {
77
+ if (!cancelled) {
78
+ try {
79
+ univerRef.current?.dispose()
80
+ } catch {
81
+ // Partially initialized instance — ignore disposal errors.
82
+ }
83
+ univerRef.current = null
84
+ host.innerHTML = ''
85
+ setLoad({ status: 'error', message: error instanceof Error ? error.message : String(error) })
86
+ }
87
+ }
88
+ })()
89
+ return () => {
90
+ cancelled = true
91
+ // Critical: dispose the Univer instance (canvas + workers + listeners).
92
+ try {
93
+ univerRef.current?.dispose()
94
+ } catch {
95
+ // Already torn down — ignore.
96
+ }
97
+ univerRef.current = null
98
+ // Clear the host in case dispose left DOM behind.
99
+ if (host !== null) host.innerHTML = ''
100
+ }
101
+ }, [scope.sessionId, scope.cwd, path])
102
+
103
+ return (
104
+ <div className={css.editorXlsx} aria-label={title}>
105
+ {/* Univer exclusively owns this element's descendants. React only owns
106
+ the sibling overlay, so Univer cannot remove a React child. */}
107
+ <div className={css.editorUniverHost} ref={hostRef} />
108
+ {load.status !== 'ready' && (
109
+ <div className={css.editorOfficeOverlay}>
110
+ {load.status === 'loading' && <div className={css.editorPlaceholder}>{t('loading')}</div>}
111
+ {load.status === 'error' && <BinaryFallback scope={scope} path={path} message={load.message} />}
112
+ </div>
113
+ )}
114
+ </div>
115
+ )
116
+ }
package/src/config.ts ADDED
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Serializable configuration and defaults for the sidebar host half. Loader
3
+ * schema validation normally fills defaults; {@link resolveSidebarConfig}
4
+ * applies the same defaults for direct callers that bypass the Loader.
5
+ * @module dsh-better-sidebar/config
6
+ */
7
+
8
+ import z from 'schemastery'
9
+ import {
10
+ SIDEBAR_PREFS_DEFAULTS,
11
+ SIDEBAR_PREFS_NS,
12
+ WIDTH_PERCENT_DEFAULT,
13
+ WIDTH_PERCENT_MAX,
14
+ WIDTH_PERCENT_MIN,
15
+ type SidebarPrefs,
16
+ } from './prefs-shared.ts'
17
+
18
+ export {
19
+ SIDEBAR_PREFS_DEFAULTS,
20
+ SIDEBAR_PREFS_NS,
21
+ WIDTH_PERCENT_DEFAULT,
22
+ WIDTH_PERCENT_MAX,
23
+ WIDTH_PERCENT_MIN,
24
+ type SidebarPrefs,
25
+ } from './prefs-shared.ts'
26
+
27
+ /** Tunable sidebar host limits (every field optional; defaults fill in). */
28
+ export interface SidebarConfig {
29
+ /** Read cap of one text file (bytes); larger files return truncated. */
30
+ readLimit?: number
31
+ /** Media route cap (bytes); larger binaries are refused. */
32
+ mediaLimit?: number
33
+ /** Explorer row bound of one level. */
34
+ listLimit?: number
35
+ /** Terminals per session. */
36
+ terminalsPerSession?: number
37
+ /** How long a disconnected terminal process survives awaiting a reconnect. */
38
+ reconnectGraceMs?: number
39
+ }
40
+
41
+ /** Schemastery schema for the plugin configuration. */
42
+ export const Config: z<SidebarConfig> = z.object({
43
+ readLimit: z.number().step(1).min(1).default(512 * 1024),
44
+ mediaLimit: z.number().step(1).min(1).default(20 * 1024 * 1024),
45
+ listLimit: z.number().step(1).min(1).default(1000),
46
+ terminalsPerSession: z.number().step(1).min(1).default(3),
47
+ reconnectGraceMs: z.number().step(1).min(0).default(30_000),
48
+ })
49
+
50
+ /** Fully defaulted sidebar host settings. */
51
+ export interface ResolvedSidebarConfig {
52
+ readLimit: number
53
+ mediaLimit: number
54
+ listLimit: number
55
+ terminalsPerSession: number
56
+ reconnectGraceMs: number
57
+ }
58
+
59
+ /**
60
+ * Apply direct-call defaults after Loader schema validation has normally run.
61
+ *
62
+ * @param config - Deployment-provided sidebar host settings.
63
+ * @returns Complete settings consumed by the host half.
64
+ */
65
+ export function resolveSidebarConfig(config: SidebarConfig | undefined): ResolvedSidebarConfig {
66
+ return {
67
+ readLimit: config?.readLimit ?? 512 * 1024,
68
+ mediaLimit: config?.mediaLimit ?? 20 * 1024 * 1024,
69
+ listLimit: config?.listLimit ?? 1000,
70
+ terminalsPerSession: config?.terminalsPerSession ?? 3,
71
+ reconnectGraceMs: config?.reconnectGraceMs ?? 30_000,
72
+ }
73
+ }
74
+
75
+ // ── User-facing "Side card" preferences ─────────────────────────────────────
76
+
77
+ /** Schemastery schema for the user-facing preferences (validated by the settings service). */
78
+ export const PrefsSchema: z<SidebarPrefs> = z.object({
79
+ openByDefault: z.boolean().default(true),
80
+ defaultWidthPercent: z.number().step(1).min(WIDTH_PERCENT_MIN).max(WIDTH_PERCENT_MAX).default(WIDTH_PERCENT_DEFAULT),
81
+ autoOpenSubagent: z.boolean().default(true),
82
+ autoOpenJobs: z.boolean().default(true),
83
+ agentTerminalTools: z.boolean().default(false),
84
+ bottomPanelAutoTerminal: z.boolean().default(true),
85
+ interceptOpenPath: z.boolean().default(true),
86
+ htmlViewerNoSandbox: z.boolean().default(false),
87
+ htmlViewerDefaultUnsafe: z.boolean().default(false),
88
+ browserNoSandbox: z.boolean().default(false),
89
+ browserInterceptLinks: z.boolean().default(true),
90
+ // Per-feature enable switches are OPEN maps (any tab/viewer id, built-in or
91
+ // external): an absent key means enabled, so old documents resolve to {}
92
+ // (everything on) with no migration. Non-boolean values fail validation.
93
+ tabsEnabled: z.dict(z.boolean()).default({}),
94
+ viewersEnabled: z.dict(z.boolean()).default({}),
95
+ })
@@ -0,0 +1,445 @@
1
+ /**
2
+ * Structural types for the cordis services this plugin consumes, plus the
3
+ * Context augmentation both halves share. A third-party plugin resolves
4
+ * outside the DSH monorepo's single cordis instance, so the upstream
5
+ * `declare module 'cordis'` augmentations do not reach this Context — and
6
+ * the npm cordis package does not declare the DSH-vendored runtime members
7
+ * (`ctx.effect`, service properties). The members below mirror the actual
8
+ * runtime shapes this plugin touches:
9
+ * - webServer: @deepseek-ai/dsh-host-webserver (the WebServer)
10
+ * - sessions: host side @deepseek-ai/dsh-session (SessionStore), client
11
+ * side the runtime ISessions list feed
12
+ * - conversation: client side ui-conversation's IConversation (composer
13
+ * draft), read lazily through `ctx.get` — cross-plugin service reads need
14
+ * an inject declaration, so the direct property is never typed here
15
+ * - loader: @cordisjs/plugin-loader (entry options)
16
+ * - slots: the client runtime SlotRegistry
17
+ * - effect: the DSH-vendored cordis lifecycle helper
18
+ * Drift from upstream is contained to this file.
19
+ */
20
+ import type { IncomingMessage, ServerResponse } from 'node:http'
21
+ import type { Duplex } from 'node:stream'
22
+ import type { Context } from 'cordis'
23
+ import type { BetterSidebarService } from './client/service.ts'
24
+
25
+ /** One named webserver route (mirror of the host-webserver WebRoute). */
26
+ export interface SidebarWebRoute {
27
+ kind: 'exact' | 'prefix'
28
+ path: string
29
+ handler: (req: IncomingMessage, res: ServerResponse) => void | Promise<void>
30
+ }
31
+
32
+ /** One exact-path HTTP upgrade registration (mirror of WebUpgradeRoute). */
33
+ export interface SidebarWebUpgradeRoute {
34
+ path: string
35
+ handler: (req: IncomingMessage, socket: Duplex, head: Buffer) => void | Promise<void>
36
+ }
37
+
38
+ /** The webServer service face this plugin uses. */
39
+ export interface SidebarWebServer {
40
+ register(route: SidebarWebRoute): () => void
41
+ registerUpgrade(route: SidebarWebUpgradeRoute): () => void
42
+ }
43
+
44
+ /** A published session's header slice the sidebar reads (authoritative cwd). */
45
+ export interface SidebarSessionHeader {
46
+ cwd?: string
47
+ }
48
+
49
+ /** The host session store face (`ctx.sessions.get(id)` returns the live session). */
50
+ export interface SidebarSessionStore {
51
+ get(id: string): {
52
+ header: SidebarSessionHeader
53
+ /**
54
+ * The live session's append-only event log (immutable snapshot; absent
55
+ * on sessions the runtime has not hydrated). Read-only access — the
56
+ * jobs.output route replays `job_output` tool/result rows from it.
57
+ */
58
+ events?: readonly SidebarSessionEvent[]
59
+ } | undefined
60
+ }
61
+
62
+ /** One loader entry's options slice (the connection row's resolved config). */
63
+ export interface SidebarLoaderEntry {
64
+ options: { name: string; config?: unknown }
65
+ }
66
+
67
+ /** The loader face used to read the connection row's trustedHosts config. */
68
+ export interface SidebarLoader {
69
+ entries(): Iterable<SidebarLoaderEntry>
70
+ }
71
+
72
+ /** Registration options the sidebar passes to `ctx.slots.register` (subset of the real options). */
73
+ export interface SidebarSlotRegisterOptions {
74
+ name: string
75
+ key?: string
76
+ id?: string
77
+ order?: number
78
+ label?: string | (() => string)
79
+ /** Chain routing selector (returns the matched value, or null to pass on). */
80
+ select?: (owner: unknown) => unknown
81
+ priority?: number
82
+ locale?: string
83
+ registrant?: string
84
+ /** Business-face factory; args depend on the slot scope. */
85
+ inject?: (...args: any[]) => Record<string, unknown>
86
+ children?: Record<string, unknown>
87
+ }
88
+
89
+ /** The client slots service face (register returns the disposer). */
90
+ export interface SidebarSlotsService {
91
+ register(options: SidebarSlotRegisterOptions, component: unknown): () => void
92
+ /**
93
+ * Run a callback for each declaration lifetime of a slot (the runtime
94
+ * SlotRegistry.inject): a no-op while the slot is undeclared, so the
95
+ * settings section registration waits for the settings shell.
96
+ */
97
+ inject(key: string, callback: () => () => void): () => void
98
+ }
99
+
100
+ /** The client session list row the sidebar reads (cwd for the explorer). */
101
+ export interface SidebarSessionSummary {
102
+ id: string
103
+ cwd?: string
104
+ displayTitle: string
105
+ /** Coarse durable origin for navigation filtering (subagent children). */
106
+ origin?: 'subagent'
107
+ /** Durable direct parent session id (present on subagent children). */
108
+ parentId?: string
109
+ /** Whether the session's agent is currently running. */
110
+ running?: boolean
111
+ }
112
+
113
+ /** One healthy subagent catalog child row (structural mirror of the runtime). */
114
+ export interface SidebarSubagentChildEntry {
115
+ kind: 'child'
116
+ id: string
117
+ /** Whether the child Agent driver is running at the Host sampling boundary. */
118
+ activity: 'running' | 'inactive'
119
+ /** Whether a direct descendant has durable `origin: 'subagent'`. */
120
+ hasChildren: boolean
121
+ mode: 'one-shot' | 'continuable'
122
+ label?: string
123
+ }
124
+
125
+ /** One unreadable catalog row (corrupt / unsupported / unavailable). */
126
+ export interface SidebarSubagentDiagnosticEntry {
127
+ kind: 'diagnostic'
128
+ id: string
129
+ reason: 'corrupt' | 'unsupported' | 'unavailable'
130
+ }
131
+
132
+ /** The per-parent lazy catalog delivered through the sessions list feed. */
133
+ export interface SidebarSubagentCatalog {
134
+ entries: Array<SidebarSubagentChildEntry | SidebarSubagentDiagnosticEntry>
135
+ parentAvailable: boolean
136
+ state: 'loading' | 'ready' | 'error'
137
+ error: { code?: string; message?: string } | null
138
+ }
139
+
140
+ /** Durable parent/child address that selects subagent transport in the client. */
141
+ export interface SidebarSubagentAddress {
142
+ parentSessionId: string
143
+ childSessionId: string
144
+ mode: 'one-shot' | 'continuable'
145
+ }
146
+
147
+ /** Minimal structural mirror of one session event (the subagent history tail). */
148
+ export interface SidebarSessionEvent {
149
+ type: string
150
+ seq: number
151
+ time: number
152
+ data: Record<string, unknown>
153
+ }
154
+
155
+ /** One history row: the durable event plus an optional tool presentation view. */
156
+ export interface SidebarHistoryEntry {
157
+ event: SidebarSessionEvent
158
+ view?: unknown
159
+ }
160
+
161
+ /** Lifecycle status set of one background job (closed wire union). */
162
+ export type SidebarJobStatus = 'running' | 'stopping' | 'completed' | 'killed' | 'failed'
163
+
164
+ /**
165
+ * One background job as the client mirror sees it (wire `JobView` shape:
166
+ * id/kind/label/status/detail?/startedAt/finishedAt?).
167
+ */
168
+ export interface SidebarJobView {
169
+ /** Registry-issued `<kind>-N` identity, stable for the job's whole life. */
170
+ id: string
171
+ /** Producer kind (`bash`, `pwsh`, `subagent`, …; open string by design). */
172
+ kind: string
173
+ /** Producer-supplied one-line label: the command, or the delegation description. */
174
+ label: string
175
+ /** Current lifecycle state. */
176
+ status: SidebarJobStatus
177
+ /** Kind-specific status detail ('exit code: 3'), present once supplied. */
178
+ detail?: string
179
+ /** Epoch ms when the job was registered. */
180
+ startedAt: number
181
+ /** Epoch ms when the job settled; absent while live. */
182
+ finishedAt?: number
183
+ }
184
+
185
+ /** The host jobs registry face the sidebar routes touch (structural mirror of `JobRegistry`). */
186
+ export interface SidebarJobsService {
187
+ /** Request cancellation; throws for an unknown or foreign job. */
188
+ kill(id: string, caller?: SidebarAgent, reason?: string): 'requested' | 'already-finished'
189
+ }
190
+
191
+ /** The host agent registry face (structural mirror of the runtime `ctx.agents`). */
192
+ export interface SidebarAgentsService {
193
+ /** The live agent registered under a session id, or undefined when not live. */
194
+ get(id: string): SidebarAgent | undefined
195
+ }
196
+
197
+ /** RPC result slot mirror (`RpcResult<T>` on the wire). */
198
+ export type SidebarRpcResult<T> = { ok: true; value: T } | { ok: false; error: { code: string; message: string } }
199
+
200
+ /** Unary response mirror (`RpcResponse<T>` on the wire). */
201
+ export interface SidebarRpcResponse<T> {
202
+ rpcId: unknown
203
+ result: SidebarRpcResult<T>
204
+ }
205
+
206
+ /** The wire face the Subagent activity summary needs (subset of `ctx.connection`). */
207
+ export interface SidebarConnectionHandle {
208
+ api: {
209
+ subagents: {
210
+ history(
211
+ payload: SidebarSubagentAddress & { beforeSeq?: number; maxMessages?: number },
212
+ signal?: AbortSignal,
213
+ ): Promise<SidebarRpcResponse<{ events: SidebarHistoryEntry[]; hasMore: boolean }>>
214
+ }
215
+ }
216
+ }
217
+
218
+ /** The client session list snapshot the sidebar subscribes to. */
219
+ export interface SidebarSessionList {
220
+ current: string | undefined
221
+ byId: Record<string, SidebarSessionSummary>
222
+ /** Direct durable catalogs keyed by their selected parent address. */
223
+ subagentsByParent?: Readonly<Record<string, SidebarSubagentCatalog>>
224
+ /**
225
+ * Background jobs per session, last-wins from the harness's `session/jobs`
226
+ * push (a missing key is an empty set). Absent on runtime snapshots older
227
+ * than the jobs mirror — the sidebar simply shows no job rows.
228
+ */
229
+ jobsBySession?: Readonly<Record<string, readonly SidebarJobView[]>>
230
+ }
231
+
232
+ /** The client sessions service face (only the list feed is needed). */
233
+ export interface SidebarSessionsService {
234
+ list: {
235
+ getSnapshot(): SidebarSessionList
236
+ subscribe(fn: () => void): () => void
237
+ }
238
+ /**
239
+ * Select a listed session as current (mirror of the runtime ISessions.open)
240
+ * — used to jump back to the main agent from the topology root node.
241
+ */
242
+ open?(id: string): void
243
+ /**
244
+ * Resolve an Agent-scoped context view for one session (mirror of the
245
+ * runtime ISessions.scope) — the ticket `ctx.conversation.input.for`
246
+ * requires to reach that session's composer.
247
+ */
248
+ scope(id: string): Context | undefined
249
+ /**
250
+ * Open a healthy catalog child through its exact direct-parent address
251
+ * (mirror of the runtime ISessions.openSubagent).
252
+ */
253
+ openSubagent?(address: SidebarSubagentAddress): void
254
+ /**
255
+ * Resolve an already discovered direct-parent address without opening it.
256
+ */
257
+ subagentAddress?(id: string): SidebarSubagentAddress | undefined
258
+ /**
259
+ * Mark whether a catalog surface is consuming live membership updates.
260
+ */
261
+ setSubagentCatalogOpen?(parentSessionId: string, open: boolean): void
262
+ /**
263
+ * Refresh one direct-child catalog.
264
+ */
265
+ refreshSubagents?(parentSessionId: string): Promise<void>
266
+ }
267
+
268
+ /**
269
+ * The client locale service face (mirror of @deepseek-ai/dsh-client-locale's
270
+ * LocaleRuntime — only the slices the sidebar touches). The sidebar follows
271
+ * the DSH i18n system: the active locale is the Host-backed preference
272
+ * (`locale.preference` in settings.yaml) rather than the raw browser
273
+ * language, and the sidebar's zh/en dictionaries register into the service's
274
+ * namespace registry under `betterSidebar`.
275
+ */
276
+ export interface SidebarLocaleService {
277
+ /** Current immutable locale snapshot (uSES-safe; `active` is 'zh' | 'en' today). */
278
+ getSnapshot(): { active: string }
279
+ /** Subscribe to snapshot changes (locale switch or dictionary registration). */
280
+ subscribe(fn: () => void): () => void
281
+ /** Register one locale's dictionary for a namespace; returns the disposer. */
282
+ register(ns: string, locale: string, dict: Record<string, string>): () => void
283
+ }
284
+
285
+ /** The composer draft face the sidebar reaches through `ctx.conversation.input`. */
286
+ export interface SidebarSessionInput {
287
+ /** The live input store (draft read for append). */
288
+ state: {
289
+ getSnapshot(): { draft: string }
290
+ }
291
+ /** Replace the draft text (the input machine's single public write path). */
292
+ setDraft(text: string): void
293
+ }
294
+
295
+ /** The composer draft face the sidebar reaches through `ctx.get('conversation')`. */
296
+ export interface SidebarConversation {
297
+ input: {
298
+ for(actx: Context): SidebarSessionInput
299
+ }
300
+ }
301
+
302
+ /**
303
+ * The client workspaces service face (mirror of the runtime IWorkspaces). Only
304
+ * the chat's file-open funnel is touched: `openPath` hands an absolute path
305
+ * to the Host OS's default application, and every chat-side file open
306
+ * (tool rows, produced-files, prose mentions) funnels through it.
307
+ */
308
+ export interface SidebarWorkspacesService {
309
+ /** Open a filesystem path with the Host operating system's default application. */
310
+ openPath(path: string): Promise<void>
311
+ }
312
+
313
+ /**
314
+ * The invariant service face (mirror of @deepseek-ai/dsh-invariants'
315
+ * InvariantRegistry). The upstream augmentation does not reach this Context
316
+ * (dual-cordis-instance resolution), so the register signature is restated
317
+ * structurally, exactly like the other service faces above.
318
+ */
319
+ export interface SidebarInvariantsService {
320
+ /** Reserve one package's checks and install them in the service's child fiber. */
321
+ register(
322
+ packageName: string,
323
+ installer: (ctx: Context, fail: (message: string) => never) => void | Promise<void>,
324
+ ): () => void
325
+ }
326
+
327
+ /** The settings service face (mirror of @deepseek-ai/dsh-settings' SettingsProvider). */
328
+ export interface SidebarSettingsService {
329
+ /**
330
+ * Register one namespace schema (the resolved value layers schema defaults,
331
+ * then the composition base, then the user document).
332
+ */
333
+ register<T>(
334
+ ns: string,
335
+ schema: unknown,
336
+ options?: { base?: Partial<T>; applies?: 'live' | 'restart' },
337
+ ): {
338
+ get(): T
339
+ watch(callback: (next: T, prev: T) => void | Promise<void>): () => void
340
+ update(patch: object): Promise<void>
341
+ replace(section: object): Promise<void>
342
+ }
343
+ /** Redacted descriptors of every registered namespace (secrets stripped). */
344
+ describe(options?: { redactSecrets?: boolean }): Array<{
345
+ ns: string
346
+ value?: unknown
347
+ base?: unknown
348
+ user?: unknown
349
+ applies: 'live' | 'restart'
350
+ revision: number
351
+ }>
352
+ /** Service-level merge write with the revision guard (a stale writer is refused). */
353
+ update(ns: string, patch: object, expectedRevision?: number): Promise<void>
354
+ }
355
+
356
+ /**
357
+ * The tools service face (mirror of @deepseek-ai/dsh-tools' ToolRuntime).
358
+ * The host half registers model-facing tools here; the registry attaches the
359
+ * returned disposer to the contributing fiber so unloading unregisters them.
360
+ */
361
+ export interface SidebarToolsService {
362
+ /** Register one tool definition (raw JSON-Schema or defineTool-sugar form). */
363
+ register(tool: unknown): () => void
364
+ }
365
+
366
+ /**
367
+ * The agent face a tool sees on `exec.agent` (mirror of @deepseek-ai/dsh-agent's
368
+ * Agent). Only the slices the terminal tools touch are restated: the live
369
+ * session identity and its header cwd, both readonly.
370
+ */
371
+ export interface SidebarAgent {
372
+ /** The live session identity shared with the session log. */
373
+ readonly id: string
374
+ /** The live session this agent drives. */
375
+ readonly session: {
376
+ /** The session's header (validated cwd, lineage metadata). */
377
+ readonly header: { readonly cwd?: string }
378
+ }
379
+ }
380
+
381
+ declare module 'cordis' {
382
+ interface Context {
383
+ webServer: SidebarWebServer
384
+ sessions: SidebarSessionStore & SidebarSessionsService
385
+ connection: SidebarConnectionHandle
386
+ loader: SidebarLoader
387
+ slots: SidebarSlotsService
388
+ workspaces: SidebarWorkspacesService
389
+ settings: SidebarSettingsService
390
+ invariants: SidebarInvariantsService
391
+ tools: SidebarToolsService
392
+ /**
393
+ * The client locale service (`@deepseek-ai/dsh-client-locale`): the
394
+ * sidebar's copy follows its active locale and registers its
395
+ * dictionaries under the `betterSidebar` namespace. Client side only.
396
+ */
397
+ locale: SidebarLocaleService
398
+ /**
399
+ * The host background-job registry (`ctx.get('jobs')`; optional — the
400
+ * sidebar routes degrade to a 503 when the deployment lacks it).
401
+ */
402
+ jobs: SidebarJobsService
403
+ /**
404
+ * The host live-agent registry (`ctx.get('agents')`; optional — used to
405
+ * resolve the caller the jobs fence compares against).
406
+ */
407
+ agents: SidebarAgentsService
408
+ /**
409
+ * The client-side sidebar registry: external plugins register tab types
410
+ * and file previewers here. Provided by the client half (see
411
+ * {@link ./client/index.tsx}); undefined on the host side.
412
+ */
413
+ betterSidebar: BetterSidebarService
414
+ /**
415
+ * Subscribe to the session append feed (mirror of the cordis event API):
416
+ * the listener receives every appended session event with the LIVE
417
+ * Session instance that appended it. The api-proxy pushes the same feed
418
+ * to browsers; the sidebar uses it to mirror job_output events the
419
+ * session store's own log can lag behind (restart divergence). Returns
420
+ * the disposer.
421
+ */
422
+ on(event: string, listener: (session: unknown, event: SidebarSessionEvent) => void): () => void
423
+ /**
424
+ * Register a lifecycle callback (DSH-vendored cordis): runs at plugin
425
+ * activation; its returned cleanup runs at disposal.
426
+ */
427
+ effect(fn: () => void | (() => void), label?: string): void
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Dual cordis-scope augmentation: DSH's runtime (and the public packages)
433
+ * resolve against the vendored `@deepseek-ai/cordis` scope, which DSH's own
434
+ * packages already augment with `effect`/`on`/service members. This side only
435
+ * adds the sidebar service member so consumers importing `Context` from
436
+ * `@deepseek-ai/cordis` (instead of the public `cordis` above) still see
437
+ * `ctx.betterSidebar`.
438
+ */
439
+ declare module '@deepseek-ai/cordis' {
440
+ interface Context {
441
+ betterSidebar: BetterSidebarService
442
+ }
443
+ }
444
+
445
+ export type { Context }