@xp266/dshtui 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cordis.patch.yml CHANGED
@@ -3,6 +3,11 @@
3
3
  # session-projection-cache) is inserted by the dsh-base bundle since
4
4
  # 0.1.2-rc.1 with the same configuration this file used to carry —
5
5
  # re-inserting it here would collide with duplicate loader entry ids.
6
+ # The standard preset's subagent tool enables modelSelectionSettings,
7
+ # which requires this settings service in the host scope (the web bundle
8
+ # mounts the same row for its own roster).
9
+ - id: subagent-model-selection-settings
10
+ name: '@deepseek-ai/dsh-tool-subagent/model-selection-settings'
6
11
  - id: workspace
7
12
  name: '@deepseek-ai/dsh-workspace'
8
13
  - id: agent-presets
package/lib/index.mjs CHANGED
@@ -6200,7 +6200,7 @@ function MessageList({ messages, height, width, scrollTop, onScroll, interactive
6200
6200
  }
6201
6201
  //#endregion
6202
6202
  //#region src/contract/upstream.ts
6203
- const UPSTREAM_FLOOR_VERSION = "0.1.0-rc.6";
6203
+ const UPSTREAM_FLOOR_VERSION = "0.1.2-rc.1";
6204
6204
  const UPSTREAM_CEILING_VERSION = "0.2.0";
6205
6205
  const UPSTREAM_FRAMEWORK_MAJORS = {
6206
6206
  "@deepseek-ai/cordis": 4,
@@ -7203,9 +7203,9 @@ async function computeSessionList(ctx) {
7203
7203
  attached.add(id);
7204
7204
  if (session.header.origin === "subagent") continue;
7205
7205
  if (archived.has(id)) continue;
7206
- if (isBlankSession(session.events)) continue;
7207
- const title = titleService?.get?.(session)?.title ?? firstUserText(session.events);
7208
- summaries.push(toSummary(id, title, session.header.cwd, session.header.createdAt, lastPromptAt(session.events), workspacePaths));
7206
+ if (isBlankSession(session.snapshotEvents())) continue;
7207
+ const title = titleService?.get?.(session)?.title ?? firstUserText(session.snapshotEvents());
7208
+ summaries.push(toSummary(id, title, session.header.cwd, session.header.createdAt, lastPromptAt(session.snapshotEvents()), workspacePaths));
7209
7209
  }
7210
7210
  const persistence = ctx.get("sessionPersistence");
7211
7211
  await mergeColdSummaries(ctx, persistence, await listColdHeaders(ctx, persistence, attached), archived, workspacePaths, summaries);
@@ -9649,12 +9649,12 @@ function registerInteractionChannels(ctx, store) {
9649
9649
  const offApproval = ctx.on("approval/request", async (req) => {
9650
9650
  return store.pushApproval(req.toolName, req.callId, req.reason, req.signal);
9651
9651
  });
9652
- const unregisterProvider = ctx.get("userQuestions")?.registerProvider({ ask(request) {
9652
+ const offQuestions = ctx.on("user-questions/request", async (request) => {
9653
9653
  return store.pushQuestion(request, request.signal);
9654
- } });
9654
+ });
9655
9655
  return () => {
9656
9656
  offApproval();
9657
- unregisterProvider?.();
9657
+ offQuestions();
9658
9658
  };
9659
9659
  }
9660
9660
  //#endregion
@@ -10173,8 +10173,9 @@ async function createChatBridge(ctx) {
10173
10173
  };
10174
10174
  }
10175
10175
  function resolveSessionPreset(session) {
10176
- for (let index = session.events.length - 1; index >= 0; index -= 1) {
10177
- const event = session.events[index];
10176
+ const events = session.snapshotEvents();
10177
+ for (let index = events.length - 1; index >= 0; index -= 1) {
10178
+ const event = events[index];
10178
10179
  if (event?.type === "agent-preset/selected") {
10179
10180
  const data = event.data;
10180
10181
  if (data?.agentPreset !== void 0) return data.agentPreset;
@@ -10254,21 +10255,21 @@ async function createChatBridge(ctx) {
10254
10255
  async function openSession(id) {
10255
10256
  const sessionId = SessionId(id);
10256
10257
  if (activeAgent.id === sessionId) {
10257
- for (const event of activeAgent.session.events) emit(event);
10258
+ for (const event of activeAgent.session.snapshotEvents()) emit(event);
10258
10259
  return;
10259
10260
  }
10260
10261
  const existing = ctx.agents.get(sessionId);
10261
10262
  if (existing !== void 0) {
10262
10263
  await activateAgent(void 0, existing);
10263
10264
  syncCwd();
10264
- for (const event of activeAgent.session.events) emit(event);
10265
+ for (const event of activeAgent.session.snapshotEvents()) emit(event);
10265
10266
  refreshAgentState();
10266
10267
  return;
10267
10268
  }
10268
10269
  const handle = await resumeAgent(sessionId);
10269
10270
  await activateAgent(handle, handle.agent);
10270
10271
  syncCwd();
10271
- for (const event of activeAgent.session.events) emit(event);
10272
+ for (const event of activeAgent.session.snapshotEvents()) emit(event);
10272
10273
  refreshAgentState();
10273
10274
  }
10274
10275
  function syncCwd() {
@@ -10321,7 +10322,7 @@ async function createChatBridge(ctx) {
10321
10322
  if (presets === void 0) throw new Error("agent presets are not configured");
10322
10323
  if (currentPreset() === id) return;
10323
10324
  const session = activeAgent.session;
10324
- if (!isBlankSession(session.events)) throw new Error("the preset is fixed once the session has started; use /new to start a new session");
10325
+ if (!isBlankSession(session.snapshotEvents())) throw new Error("the preset is fixed once the session has started; use /new to start a new session");
10325
10326
  const preset = await presets.recompose(activeAgent.ctx, id);
10326
10327
  session.append("agent-preset/selected", { agentPreset: preset.id });
10327
10328
  }
@@ -10496,12 +10497,12 @@ async function createChatBridge(ctx) {
10496
10497
  currentEffort,
10497
10498
  effortName,
10498
10499
  selectEffort,
10499
- permissionMode: () => permission()?.current(activeAgent.session.events) ?? PERMISSION_PRESETS[0],
10500
+ permissionMode: () => permission()?.current(activeAgent.session) ?? PERMISSION_PRESETS[0],
10500
10501
  cyclePermission: () => {
10501
10502
  const service = permission();
10502
10503
  if (service === void 0) return;
10503
10504
  try {
10504
- const current = service.current(activeAgent.session.events);
10505
+ const current = service.current(activeAgent.session);
10505
10506
  const names = service.names.length > 0 ? service.names : PERMISSION_PRESETS;
10506
10507
  const next = names[(names.indexOf(current) + 1) % names.length];
10507
10508
  service.set(activeAgent.session, next);
@@ -11303,7 +11304,11 @@ function apply(ctx, config = Config(DEFAULT_CONFIG)) {
11303
11304
  const restorePerformance = startPerformanceGuard();
11304
11305
  const disposeCrashHandlers = installCrashHandlers({ exit: env.crashExit });
11305
11306
  const drift = upstreamDriftSummary();
11306
- if (drift !== void 0) warn("upstream", `upstream version drift (supported: ${UPSTREAM_SUPPORTED_RANGE}): ${drift.kind} ${drift.versions.join(", ")}`);
11307
+ if (drift !== void 0) {
11308
+ const message = `dshtui requires dsh harness packages in ${UPSTREAM_SUPPORTED_RANGE} (found ${drift.kind}: ${drift.versions.join(", ")}); upgrade the dsh CLI with: npm install -g @deepseek-ai/dsh@latest`;
11309
+ error("boot", message);
11310
+ throw new Error(message);
11311
+ }
11307
11312
  const capture = createScreenCapture();
11308
11313
  let bridge;
11309
11314
  let app;
@@ -11371,7 +11376,6 @@ function apply(ctx, config = Config(DEFAULT_CONFIG)) {
11371
11376
  };
11372
11377
  const themeScope = registerThemeSettings(ctx);
11373
11378
  openBootLog();
11374
- if (drift !== void 0) emitBootLine(`warning: upstream version drift (${drift.kind})`);
11375
11379
  emitBootLine("terminal: probing color support");
11376
11380
  (async () => {
11377
11381
  const probed = await probeColorLevel();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xp266/dshtui",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "description": "ink-based terminal UI plugin for DeepSeek Harness",
6
6
  "engines": {
@@ -38,17 +38,17 @@
38
38
  ],
39
39
  "license": "MIT",
40
40
  "peerDependencies": {
41
- "@deepseek-ai/cordis": "^4.0.1",
42
- "@deepseek-ai/dsh-agent": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.2",
43
- "@deepseek-ai/dsh-agent-default-model": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.2",
44
- "@deepseek-ai/dsh-agent-loop": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.2",
45
- "@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.2",
46
- "@deepseek-ai/dsh-attachment": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.2",
47
- "@deepseek-ai/dsh-credentials": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.2",
48
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.2",
49
- "@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.2",
50
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.2",
51
- "@deepseek-ai/schemastery": "^3.18.1"
41
+ "@deepseek-ai/cordis": "^4.0.2",
42
+ "@deepseek-ai/dsh-agent": "^0.1.2-rc.1",
43
+ "@deepseek-ai/dsh-agent-default-model": "^0.1.2-rc.1",
44
+ "@deepseek-ai/dsh-agent-loop": "^0.1.2-rc.1",
45
+ "@deepseek-ai/dsh-agent-presets": "^0.1.2-rc.1",
46
+ "@deepseek-ai/dsh-attachment": "^0.1.2-rc.1",
47
+ "@deepseek-ai/dsh-credentials": "^0.1.2-rc.1",
48
+ "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
49
+ "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
50
+ "@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
51
+ "@deepseek-ai/schemastery": "^3.18.2"
52
52
  },
53
53
  "peerDependenciesMeta": {
54
54
  "@deepseek-ai/cordis": {
@@ -94,23 +94,24 @@
94
94
  "string-width": "^8.2.2"
95
95
  },
96
96
  "devDependencies": {
97
- "@deepseek-ai/cordis": "^4.0.1",
98
- "@deepseek-ai/dsh-agent": "0.1.0-rc.6",
99
- "@deepseek-ai/dsh-agent-default-model": "0.1.0-rc.6",
100
- "@deepseek-ai/dsh-agent-loop": "0.1.0-rc.6",
101
- "@deepseek-ai/dsh-agent-presets": "0.1.0-rc.6",
102
- "@deepseek-ai/dsh-attachment": "0.1.0-rc.6",
103
- "@deepseek-ai/dsh-credentials": "0.1.0-rc.6",
104
- "@deepseek-ai/dsh-llm": "0.1.0-rc.6",
105
- "@deepseek-ai/dsh-session": "0.1.0-rc.6",
106
- "@deepseek-ai/dsh-settings": "0.1.0-rc.6",
107
- "@deepseek-ai/schemastery": "^3.18.1",
97
+ "@deepseek-ai/cordis": "^4.0.2",
98
+ "@deepseek-ai/dsh-agent": "0.1.2-rc.1",
99
+ "@deepseek-ai/dsh-agent-default-model": "0.1.2-rc.1",
100
+ "@deepseek-ai/dsh-agent-loop": "0.1.2-rc.1",
101
+ "@deepseek-ai/dsh-agent-presets": "0.1.2-rc.1",
102
+ "@deepseek-ai/dsh-attachment": "0.1.2-rc.1",
103
+ "@deepseek-ai/dsh-credentials": "0.1.2-rc.1",
104
+ "@deepseek-ai/dsh-llm": "0.1.2-rc.1",
105
+ "@deepseek-ai/dsh-session": "0.1.2-rc.1",
106
+ "@deepseek-ai/dsh-settings": "0.1.2-rc.1",
107
+ "@deepseek-ai/schemastery": "^3.18.2",
108
108
  "@types/node": "^24.0.0",
109
109
  "@types/prismjs": "^1.26.6",
110
110
  "@types/react": "^19.2.0",
111
111
  "tsdown": "^0.22.14",
112
112
  "tsx": "^4.0.0",
113
- "typescript": "^5.9.0"
113
+ "typescript": "^5.9.0",
114
+ "@deepseek-ai/dsh-tool-todo": "0.1.2-rc.1"
114
115
  },
115
116
  "dsh": {
116
117
  "bundle": {
@@ -136,14 +136,14 @@ async function sendContent(
136
136
  }
137
137
 
138
138
  interface PresetSessionLike {
139
- events: ReadonlyArray<{ type?: string; data?: unknown }>
139
+ snapshotEvents(): ReadonlyArray<{ type?: string; data?: unknown }>
140
140
  header?: { agentPreset?: string }
141
141
  }
142
142
 
143
143
  export const PERMISSION_PRESETS = ['workspace-write', 'danger-full-access', 'read-only'] as const
144
144
 
145
145
  interface PermissionPresetsLike {
146
- current(events: readonly SessionEvent[]): string
146
+ current(session: Session): string
147
147
  set(session: Session, name: string): void
148
148
  readonly names: readonly string[]
149
149
  readonly defaultPreset: string
@@ -672,8 +672,9 @@ export async function createChatBridge(ctx: Context): Promise<ChatBridge> {
672
672
  // creation-time header value, matching the upstream semantics on every
673
673
  // host version the peer range admits.
674
674
  function resolveSessionPreset(session: PresetSessionLike): string | undefined {
675
- for (let index = session.events.length - 1; index >= 0; index -= 1) {
676
- const event = session.events[index]
675
+ const events = session.snapshotEvents()
676
+ for (let index = events.length - 1; index >= 0; index -= 1) {
677
+ const event = events[index]
677
678
  if (event?.type === 'agent-preset/selected') {
678
679
  const data = event.data as { agentPreset?: string } | undefined
679
680
  if (data?.agentPreset !== undefined) return data.agentPreset
@@ -720,7 +721,7 @@ export async function createChatBridge(ctx: Context): Promise<ChatBridge> {
720
721
  const setup = async (agentCtx: Context) => {
721
722
  installSelection(agentCtx)
722
723
  const session = agentCtx.agent?.session
723
- const resolved = session === undefined ? undefined : resolveSessionPreset(session as PresetSessionLike)
724
+ const resolved = session === undefined ? undefined : resolveSessionPreset(session)
724
725
  await presets?.mount(agentCtx, resolved)
725
726
  }
726
727
  const spawnResumedAgent = async (snapshot: { session: { cwd?: string }; events: SessionEvent[] }): Promise<AgentHandle> => {
@@ -761,21 +762,21 @@ export async function createChatBridge(ctx: Context): Promise<ChatBridge> {
761
762
  async function openSession(id: string): Promise<void> {
762
763
  const sessionId = SessionId(id)
763
764
  if (activeAgent.id === sessionId) {
764
- for (const event of activeAgent.session.events) emit(event)
765
+ for (const event of activeAgent.session.snapshotEvents()) emit(event)
765
766
  return
766
767
  }
767
768
  const existing = ctx.agents.get(sessionId)
768
769
  if (existing !== undefined) {
769
770
  await activateAgent(undefined, existing)
770
771
  syncCwd()
771
- for (const event of activeAgent.session.events) emit(event)
772
+ for (const event of activeAgent.session.snapshotEvents()) emit(event)
772
773
  refreshAgentState()
773
774
  return
774
775
  }
775
776
  const handle = await resumeAgent(sessionId)
776
777
  await activateAgent(handle, handle.agent)
777
778
  syncCwd()
778
- for (const event of activeAgent.session.events) emit(event)
779
+ for (const event of activeAgent.session.snapshotEvents()) emit(event)
779
780
  refreshAgentState()
780
781
  }
781
782
 
@@ -828,7 +829,7 @@ export async function createChatBridge(ctx: Context): Promise<ChatBridge> {
828
829
  }
829
830
 
830
831
  function currentPreset(): string {
831
- const resolved = resolveSessionPreset(activeAgent.session as PresetSessionLike)
832
+ const resolved = resolveSessionPreset(activeAgent.session)
832
833
  return resolved ?? presets?.defaultId ?? 'standard'
833
834
  }
834
835
 
@@ -836,7 +837,7 @@ export async function createChatBridge(ctx: Context): Promise<ChatBridge> {
836
837
  if (presets === undefined) throw new Error('agent presets are not configured')
837
838
  if (currentPreset() === id) return
838
839
  const session = activeAgent.session
839
- if (!isBlankSession(session.events)) {
840
+ if (!isBlankSession(session.snapshotEvents())) {
840
841
  throw new Error('the preset is fixed once the session has started; use /new to start a new session')
841
842
  }
842
843
  const preset = await presets.recompose(activeAgent.ctx, id)
@@ -1039,12 +1040,12 @@ export async function createChatBridge(ctx: Context): Promise<ChatBridge> {
1039
1040
  currentEffort,
1040
1041
  effortName,
1041
1042
  selectEffort,
1042
- permissionMode: () => permission()?.current(activeAgent.session.events) ?? PERMISSION_PRESETS[0],
1043
+ permissionMode: () => permission()?.current(activeAgent.session) ?? PERMISSION_PRESETS[0],
1043
1044
  cyclePermission: () => {
1044
1045
  const service = permission()
1045
1046
  if (service === undefined) return
1046
1047
  try {
1047
- const current = service.current(activeAgent.session.events)
1048
+ const current = service.current(activeAgent.session)
1048
1049
  const names = service.names.length > 0 ? service.names : PERMISSION_PRESETS
1049
1050
  const index = names.indexOf(current)
1050
1051
  const next = names[(index + 1) % names.length]
@@ -308,22 +308,21 @@ type ApprovalRequestLike = {
308
308
 
309
309
  type CtxLike = {
310
310
  on(event: 'approval/request', handler: (req: ApprovalRequestLike) => Promise<ApprovalOutcome>): () => void
311
- get(service: 'userQuestions'): {
312
- registerProvider(provider: { ask(request: AskUserQuestionRequestLike): Promise<AskUserQuestionAnswerLike> }): () => void
313
- } | undefined
311
+ on(event: 'user-questions/request', handler: (req: AskUserQuestionRequestLike) => Promise<AskUserQuestionAnswerLike>): () => void
314
312
  }
315
313
 
316
314
  export function registerInteractionChannels(ctx: CtxLike, store: InteractionStore): () => void {
317
315
  const offApproval = ctx.on('approval/request', async req => {
318
316
  return store.pushApproval(req.toolName, req.callId, req.reason, req.signal)
319
317
  })
320
- const unregisterProvider = ctx.get('userQuestions')?.registerProvider({
321
- ask(request) {
322
- return store.pushQuestion(request, request.signal)
323
- },
318
+ // The 0.1.2 user-questions service routes asks through the scoped
319
+ // `user-questions/request` waterfall; answering without calling `next`
320
+ // claims the request for this terminal.
321
+ const offQuestions = ctx.on('user-questions/request', async request => {
322
+ return store.pushQuestion(request, request.signal)
324
323
  })
325
324
  return () => {
326
325
  offApproval()
327
- unregisterProvider?.()
326
+ offQuestions()
328
327
  }
329
328
  }
@@ -32,16 +32,16 @@ export async function computeSessionList(ctx: Context): Promise<SessionSummary[]
32
32
  const summaries: SessionSummary[] = []
33
33
  const attached = new Set<string>()
34
34
  const titleService = ctx.get('sessionTitle') as
35
- | { get?(session: { id: string; events: readonly SessionEvent[] }): { title?: string } | undefined }
35
+ | { get?(session: { id: string; snapshotEvents(): readonly SessionEvent[] }): { title?: string } | undefined }
36
36
  | undefined
37
37
  for (const session of ctx.sessions.list()) {
38
38
  const id = String(session.id)
39
39
  attached.add(id)
40
40
  if (session.header.origin === 'subagent') continue
41
41
  if (archived.has(id)) continue
42
- if (isBlankSession(session.events)) continue
43
- const title = titleService?.get?.(session)?.title ?? firstUserText(session.events)
44
- summaries.push(toSummary(id, title, session.header.cwd, session.header.createdAt, lastPromptAt(session.events), workspacePaths))
42
+ if (isBlankSession(session.snapshotEvents())) continue
43
+ const title = titleService?.get?.(session)?.title ?? firstUserText(session.snapshotEvents())
44
+ summaries.push(toSummary(id, title, session.header.cwd, session.header.createdAt, lastPromptAt(session.snapshotEvents()), workspacePaths))
45
45
  }
46
46
  const persistence = ctx.get('sessionPersistence') as PersistenceLike | undefined
47
47
  const cold = await listColdHeaders(ctx, persistence, attached)
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from 'node:fs'
2
2
  import { fileURLToPath } from 'node:url'
3
3
 
4
- export const UPSTREAM_FLOOR_VERSION = '0.1.0-rc.6'
4
+ export const UPSTREAM_FLOOR_VERSION = '0.1.2-rc.1'
5
5
 
6
6
  export const UPSTREAM_CEILING_VERSION = '0.2.0'
7
7
 
package/src/index.tsx CHANGED
@@ -23,7 +23,7 @@ import { warmRenderPipeline } from './ui/message/warmup.ts'
23
23
  import { createTuiExtensionPoint, exposeRuntimeFaces } from './ui/extension-point.ts'
24
24
  import { registerBuiltinToolViews } from './chat/builtin-tool-views.ts'
25
25
  import { closeBootLog, emitBootLine, openBootLog } from './boot-log.ts'
26
- import { configureLogs, error as logError, installCrashHandlers, warn } from './log.ts'
26
+ import { configureLogs, error, error as logError, installCrashHandlers } from './log.ts'
27
27
  import { env } from './env.ts'
28
28
  import { upstreamDriftSummary, UPSTREAM_SUPPORTED_RANGE } from './contract/upstream.ts'
29
29
 
@@ -67,9 +67,13 @@ export function apply(ctx: Context, config: Config = Config(DEFAULT_CONFIG)) {
67
67
  configureLogs({ stderr: env.debug, file: env.logFile, dir: env.logDir, level: env.logLevel, maxFileBytes: env.logMaxBytes })
68
68
  const restorePerformance = startPerformanceGuard()
69
69
  const disposeCrashHandlers = installCrashHandlers({ exit: env.crashExit })
70
+ // Fail before any surface mounts: an out-of-range host breaks the bridge
71
+ // in ways that render as a dead interface instead of an actionable error.
70
72
  const drift = upstreamDriftSummary()
71
73
  if (drift !== undefined) {
72
- warn('upstream', `upstream version drift (supported: ${UPSTREAM_SUPPORTED_RANGE}): ${drift.kind} ${drift.versions.join(', ')}`)
74
+ const message = `dshtui requires dsh harness packages in ${UPSTREAM_SUPPORTED_RANGE} (found ${drift.kind}: ${drift.versions.join(', ')}); upgrade the dsh CLI with: npm install -g @deepseek-ai/dsh@latest`
75
+ error('boot', message)
76
+ throw new Error(message)
73
77
  }
74
78
  const capture = createScreenCapture()
75
79
  let bridge: ChatBridge | undefined
@@ -138,9 +142,6 @@ export function apply(ctx: Context, config: Config = Config(DEFAULT_CONFIG)) {
138
142
  }
139
143
  const themeScope = registerThemeSettings(ctx)
140
144
  openBootLog()
141
- if (drift !== undefined) {
142
- emitBootLine(`warning: upstream version drift (${drift.kind})`)
143
- }
144
145
  emitBootLine('terminal: probing color support')
145
146
  void (async () => {
146
147
  const probed = await probeColorLevel()
@@ -1,3 +1,4 @@
1
+ import type {} from '@deepseek-ai/dsh-tool-todo'
1
2
  import { useEffect, useRef, useState } from 'react'
2
3
  import type { SessionEvent } from '@deepseek-ai/dsh-session'
3
4
  import type { ChatBridge, ChatToolPresenter, RegistryCommand } from '../../chat/bridge.ts'