dsh-more 0.0.3 → 0.0.4

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/CONTRIBUTING.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  - Node.js `22.19.x` 或 `>= 24`;
14
14
  - pnpm;
15
- - 与项目当前依赖匹配的 DSH。本版本面向 DSH `0.1.0-rc.7`。
15
+ - 与项目当前依赖匹配的 DSH。本版本面向 DSH `0.1.2-rc.1`。
16
16
 
17
17
  首次检出后执行:
18
18
 
package/README.md CHANGED
@@ -6,7 +6,7 @@ Practical, independently switchable patches for missing context and history cont
6
6
 
7
7
  `dsh-more` integrates with the existing DSH interface instead of adding a separate management dashboard. Its current patch set can edit a user message and restart from that point, remove one message while preserving the surrounding context, and permanently delete a session while leaving native archive behavior intact.
8
8
 
9
- > **Compatibility:** the current release targets DeepSeek Harness `0.1.0-rc.7` and requires Node.js `>= 24`. DSH is still a release candidate, so upgrades may require changes in the centralized adapter layer described below.
9
+ > **Compatibility:** the current release targets DeepSeek Harness `0.1.2-rc.1` and requires Node.js `>= 24`. DSH is still a release candidate, so upgrades may require changes in the centralized adapter layer described below.
10
10
 
11
11
  ## Features
12
12
 
@@ -23,7 +23,7 @@ All three switches live under **Settings → Plugins → Plugin configuration
23
23
  ### Requirements
24
24
 
25
25
  - Node.js `>= 24`
26
- - A working `dsh` CLI and a Web profile compatible with DSH `0.1.0-rc.7`
26
+ - A working `dsh` CLI and a Web profile compatible with DSH `0.1.2-rc.1`
27
27
  - Access to the npm registry that publishes `dsh-more`
28
28
 
29
29
  pnpm is needed only when installing or developing from source. For a published release, use the DSH plugin command so the package is installed into the selected DSH profile; do not install it globally with `npm install -g`.
@@ -233,7 +233,9 @@ The repository does not currently contain a full browser end-to-end suite agains
233
233
 
234
234
  ## Compatibility notes
235
235
 
236
- - The current dependency set targets DSH `0.1.0-rc.7`; pre-release API or DOM changes can require adapter updates.
236
+ This update migrates to the split Client services, `useChat` snapshots, settings registration, and session projection APIs, and updates message action row targeting. Dependencies and the lockfile target `0.1.2-rc.1` without the retired `dsh-client-runtime`. For an existing local linked install, rebuild, restart DSH, and reload the page to load both the new Host and Client.
237
+
238
+ - The current dependency set targets DSH `0.1.2-rc.1`; pre-release API or DOM changes can require adapter updates.
237
239
  - In-product DSH More copy is currently primarily Simplified Chinese, although native Copy/Archive selectors recognize both Chinese and English labels.
238
240
  - Message actions operate only on ordinary user messages and assistant messages still present in the current model surface; editing requires a completed owning turn.
239
241
  - Permanent deletion supports DSH persistence locations of kind `jsonl` only.
package/README.zh-CN.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  `dsh-more` 不创建平行的“管理中心”,而是把能力直接嵌入 DSH 现有界面。当前补丁可以编辑用户消息并从该处重新开始、在保留前后上下文的前提下删除单条消息,以及在不改变原生归档语义的情况下永久删除会话。
8
8
 
9
- > **兼容性:** 当前发布面向 DeepSeek Harness `0.1.0-rc.7`,要求 Node.js `>= 24`。DSH 仍是 RC 版本,升级后可能需要调整下文所述的集中适配层。
9
+ > **兼容性:** 当前发布面向 DeepSeek Harness `0.1.2-rc.1`,要求 Node.js `>= 24`。DSH 仍是 RC 版本,升级后可能需要调整下文所述的集中适配层。
10
10
 
11
11
  ## 功能概览
12
12
 
@@ -23,7 +23,7 @@
23
23
  ### 环境要求
24
24
 
25
25
  - Node.js `>= 24`
26
- - 可正常使用的 `dsh` CLI,以及与 DSH `0.1.0-rc.7` 兼容的 Web profile
26
+ - 可正常使用的 `dsh` CLI,以及与 DSH `0.1.2-rc.1` 兼容的 Web profile
27
27
  - 能访问发布 `dsh-more` 的 npm registry
28
28
 
29
29
  只有从源码安装或参与开发时才需要 pnpm。安装已发布版本时,应通过 DSH 插件命令把包加入指定 profile;不要使用 `npm install -g` 全局安装。
@@ -233,7 +233,9 @@ pnpm run package # 构建并生成 npm tarball
233
233
 
234
234
  ## 兼容性说明
235
235
 
236
- - 当前依赖面向 DSH `0.1.0-rc.7`;预发布 API 或 DOM 变化可能要求更新 adapter。
236
+ 本次适配迁移到新版拆分后的 Client 服务、`useChat` 聊天快照、设置注册和会话投影接口,并更新消息操作行定位。依赖与锁文件统一到 `0.1.2-rc.1`;不再加载已停止更新的 `dsh-client-runtime`。已有本地链接安装在重新构建后,需要重启 DSH 并刷新页面以加载新的 Host 和 Client。
237
+
238
+ - 当前依赖面向 DSH `0.1.2-rc.1`;预发布 API 或 DOM 变化可能要求更新 adapter。
237
239
  - DSH More 的产品内文案目前以简体中文为主;对 DSH 原生 Copy/Archive 元素的定位同时识别中英文标签。
238
240
  - 消息操作只面向仍在当前 model surface 中的普通用户消息和助手消息;编辑要求所属轮次已经完成。
239
241
  - 永久删除只支持 DSH 返回的 `jsonl` 持久化定位。
package/dist/client.js CHANGED
@@ -506,6 +506,7 @@ function installSessionDeleteMenuItems(onDelete) {
506
506
  frame = null;
507
507
  for (const [archiveButton, deleteItem] of deleteItems) {
508
508
  if (!archiveButton.isConnected) {
509
+ deleteItem.remove();
509
510
  deleteItems.delete(archiveButton);
510
511
  } else if (!deleteItem.isConnected) {
511
512
  archiveButton.removeAttribute(DELETE_SOURCE_ATTRIBUTE);
@@ -654,8 +655,7 @@ var clientPatch = {
654
655
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({
655
656
  name: "shell.overlay",
656
657
  id: `${PLUGIN_NAME}-${SESSION_DELETE_PATCH_ID}`,
657
- order: 90,
658
- registrant: PLUGIN_NAME
658
+ order: 90
659
659
  }, (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SessionDeleteController, { ...props, ctx, activation })));
660
660
  }
661
661
  };
@@ -769,8 +769,7 @@ var clientPatch2 = {
769
769
  ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
770
770
  name: "conversation.session.header.utilities",
771
771
  id: `${PLUGIN_NAME}-${CONVERSATION_MARKDOWN_EXPORT_PATCH_ID}`,
772
- order: 100,
773
- registrant: PLUGIN_NAME
772
+ order: 100
774
773
  }, (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ConversationMarkdownExportButton, { ...props, activation })));
775
774
  }
776
775
  };
@@ -1066,9 +1065,14 @@ function ModelsPageEffortPortal(props) {
1066
1065
  syncButtons();
1067
1066
  const observer = new MutationObserver(schedule);
1068
1067
  observer.observe(document.body, { childList: true, subtree: true });
1068
+ document.addEventListener("input", schedule);
1069
+ document.addEventListener("change", schedule);
1069
1070
  return () => {
1070
1071
  observer.disconnect();
1072
+ document.removeEventListener("input", schedule);
1073
+ document.removeEventListener("change", schedule);
1071
1074
  if (frame !== null) window.cancelAnimationFrame(frame);
1075
+ document.querySelectorAll(`[${EFFORT_BUTTON_ATTR}]`).forEach((element) => element.remove());
1072
1076
  };
1073
1077
  }, [enabled, syncButtons]);
1074
1078
  (0, import_react5.useEffect)(() => {
@@ -1115,13 +1119,13 @@ var clientPatch3 = {
1115
1119
  getSnapshot: scope.getSnapshot.bind(scope),
1116
1120
  subscribe: scope.subscribe.bind(scope),
1117
1121
  set: scope.set.bind(scope),
1118
- unset: scope.unset.bind(scope)
1122
+ unset: scope.unset.bind(scope),
1123
+ mutate: scope.mutate.bind(scope)
1119
1124
  };
1120
1125
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({
1121
1126
  name: "shell.overlay",
1122
1127
  id: `${PLUGIN_NAME}-${MODEL_REASONING_EFFORTS_PATCH_ID}`,
1123
- order: 90,
1124
- registrant: PLUGIN_NAME
1128
+ order: 90
1125
1129
  }, (props) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ModelsPageEffortPortal, { ...props, scope: boundScope, activation })));
1126
1130
  }
1127
1131
  };
@@ -1168,14 +1172,11 @@ function sameTurns(left, right) {
1168
1172
  });
1169
1173
  }
1170
1174
  function createConversationTurnSelector() {
1171
- let previousOrder;
1172
1175
  let previous = [];
1173
1176
  return (snapshot) => {
1174
- if (snapshot.chat.order === previousOrder) return previous;
1175
- previousOrder = snapshot.chat.order;
1176
1177
  const next = [];
1177
- for (const key of snapshot.chat.order) {
1178
- const summary = turnSummary(snapshot.chat.nodes.get(key), key, next.length + 1);
1178
+ for (const key of snapshot.order) {
1179
+ const summary = turnSummary(snapshot.nodes.get(key), key, next.length + 1);
1179
1180
  if (summary !== null) next.push(summary);
1180
1181
  }
1181
1182
  if (!sameTurns(previous, next)) previous = next;
@@ -1183,9 +1184,9 @@ function createConversationTurnSelector() {
1183
1184
  };
1184
1185
  }
1185
1186
  function historyWindowSignature(snapshot) {
1186
- const first = snapshot.chat.order[0] ?? "";
1187
- const last = snapshot.chat.order.at(-1) ?? "";
1188
- return `${first}\0${last}\0${String(snapshot.chat.order.length)}`;
1187
+ const first = snapshot.order[0] ?? "";
1188
+ const last = snapshot.order.at(-1) ?? "";
1189
+ return `${first}\0${last}\0${String(snapshot.order.length)}`;
1189
1190
  }
1190
1191
  function shouldAutoLoadOlder(state) {
1191
1192
  return state.enabled && state.surfaceAvailable && state.hasMore && !state.loadingOlder && !state.paused;
@@ -1310,10 +1311,10 @@ function ConversationQuickNavigation(props) {
1310
1311
  const settings = (0, import_react6.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot, props.activation.getSnapshot);
1311
1312
  const enabled = settings[CONVERSATION_QUICK_NAVIGATION_PATCH_ID];
1312
1313
  const selector = (0, import_react6.useMemo)(createConversationTurnSelector, [props.sessionId]);
1313
- const allTurns = props.useSession(selector);
1314
+ const allTurns = props.useChat(selector);
1314
1315
  const hasMore = props.useSession((snapshot) => snapshot.hasMore);
1315
1316
  const loadingOlder = props.useSession((snapshot) => snapshot.loadingOlder);
1316
- const windowSignature = props.useSession(historyWindowSignature);
1317
+ const windowSignature = props.useChat(historyWindowSignature);
1317
1318
  const projected = props.useProjection(MESSAGE_VISIBILITY_PROJECTION_KEY);
1318
1319
  const hiddenSeqs = (0, import_react6.useMemo)(() => new Set(projected?.deletedSeqs ?? []), [projected]);
1319
1320
  const turns = (0, import_react6.useMemo)(
@@ -1406,7 +1407,7 @@ function ConversationQuickNavigation(props) {
1406
1407
  scrollport?.removeEventListener("scroll", schedule);
1407
1408
  if (frame !== null) window.cancelAnimationFrame(frame);
1408
1409
  };
1409
- }, [enabled, turns]);
1410
+ }, [enabled, turns, props.sessionId]);
1410
1411
  (0, import_react6.useEffect)(() => {
1411
1412
  if (!shouldAutoLoadOlder({ enabled, surfaceAvailable, hasMore, loadingOlder, paused: autoLoadPaused })) return;
1412
1413
  const pendingSignature = pendingLoadSignatureRef.current;
@@ -1567,8 +1568,7 @@ var clientPatch4 = {
1567
1568
  ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
1568
1569
  name: "conversation.session.header.utilities",
1569
1570
  id: `${PLUGIN_NAME}-${CONVERSATION_QUICK_NAVIGATION_PATCH_ID}`,
1570
- order: 120,
1571
- registrant: PLUGIN_NAME
1571
+ order: 120
1572
1572
  }, (props) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ConversationQuickNavigation, { ...props, activation })));
1573
1573
  }
1574
1574
  };
@@ -1681,8 +1681,7 @@ function installPatchConfigCard(ctx, activation) {
1681
1681
  ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
1682
1682
  name: "settings.plugin.item",
1683
1683
  key: PATCH_SETTINGS_NAMESPACE,
1684
- priority: 90,
1685
- registrant: PLUGIN_NAME
1684
+ priority: 90
1686
1685
  }, (props) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PatchConfigCard, { ...props, activation })));
1687
1686
  }
1688
1687
 
@@ -1727,13 +1726,10 @@ function createMessageSurfaceSelector(active = true) {
1727
1726
  if (!active) return () => EMPTY_MESSAGE_SURFACE;
1728
1727
  const nodeCache = /* @__PURE__ */ new WeakMap();
1729
1728
  let previous = EMPTY_MESSAGE_SURFACE;
1730
- let previousOrder;
1731
1729
  return (snapshot) => {
1732
- if (snapshot.chat.order === previousOrder) return previous;
1733
- previousOrder = snapshot.chat.order;
1734
1730
  const rows = [];
1735
- for (const key of snapshot.chat.order) {
1736
- const node = snapshot.chat.nodes.get(key);
1731
+ for (const key of snapshot.order) {
1732
+ const node = snapshot.nodes.get(key);
1737
1733
  if (node === void 0) continue;
1738
1734
  let row = nodeCache.get(node);
1739
1735
  if (row === void 0) {
@@ -1755,8 +1751,8 @@ function createMessageSurfaceSelector(active = true) {
1755
1751
  function hostForRow(row, kind) {
1756
1752
  const existing = row.querySelector(":scope [data-dshmore-message-actions]");
1757
1753
  if (kind === "assistant") {
1758
- const turnRoot = row.querySelector("[data-turn-tail][data-time-hover-root]");
1759
- const copyButton2 = turnRoot?.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"]');
1754
+ const turnRoot = row.querySelector("[data-turn-tail][data-actions-reveal]");
1755
+ const copyButton2 = turnRoot?.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"], button[aria-label="\u5DF2\u590D\u5236"], button[aria-label="Copied"]');
1760
1756
  const builtInActions2 = copyButton2?.parentElement;
1761
1757
  const placeBeforeClock = (host3) => {
1762
1758
  if (builtInActions2 === void 0 || builtInActions2 === null) return;
@@ -1780,7 +1776,7 @@ function hostForRow(row, kind) {
1780
1776
  else row.appendChild(host2);
1781
1777
  return host2;
1782
1778
  }
1783
- const copyButton = row.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"]');
1779
+ const copyButton = row.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"], button[aria-label="\u5DF2\u590D\u5236"], button[aria-label="Copied"]');
1784
1780
  const builtInActions = copyButton?.parentElement;
1785
1781
  if (existing !== null) {
1786
1782
  if (builtInActions !== void 0 && builtInActions !== null && (existing.parentElement !== builtInActions || existing.nextElementSibling !== copyButton)) {
@@ -1822,7 +1818,7 @@ function trajectorySourceSeq(row) {
1822
1818
  }
1823
1819
  function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys, active = true) {
1824
1820
  const selector = (0, import_react8.useMemo)(() => createMessageSurfaceSelector(active), [active, props.sessionId]);
1825
- const surface = props.useSession(selector);
1821
+ const surface = props.useChat(selector);
1826
1822
  const surfaceRef = (0, import_react8.useRef)(surface);
1827
1823
  const hiddenSeqsRef = (0, import_react8.useRef)(hiddenSeqs);
1828
1824
  const hiddenTrajectoryKeysRef = (0, import_react8.useRef)(hiddenTrajectoryKeys);
@@ -1935,7 +1931,10 @@ function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys, active = tru
1935
1931
  requestFullScanRef.current = requestFullScan;
1936
1932
  const observer = new MutationObserver((records) => {
1937
1933
  for (const record of records) {
1938
- if (record.removedNodes.length > 0) pruneDisconnected = true;
1934
+ if (record.removedNodes.length > 0) {
1935
+ pruneDisconnected = true;
1936
+ collectRows(record.target);
1937
+ }
1939
1938
  record.addedNodes.forEach(collectRows);
1940
1939
  }
1941
1940
  if (pendingChatRows.size > 0 || pendingTrajectoryRows.size > 0 || pruneDisconnected) schedule();
@@ -1951,7 +1950,7 @@ function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys, active = tru
1951
1950
  }
1952
1951
  for (const host of document.querySelectorAll("[data-dshmore-message-actions]")) host.remove();
1953
1952
  };
1954
- }, [active]);
1953
+ }, [active, props.sessionId]);
1955
1954
  (0, import_react8.useLayoutEffect)(() => {
1956
1955
  requestFullScanRef.current?.();
1957
1956
  }, [surface, hiddenSeqs, hiddenTrajectoryKeys]);
@@ -2004,8 +2003,7 @@ function apply(ctx) {
2004
2003
  ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
2005
2004
  name: "conversation.session.header.utilities",
2006
2005
  id: `${PLUGIN_NAME}-message-actions`,
2007
- order: 90,
2008
- registrant: PLUGIN_NAME
2006
+ order: 90
2009
2007
  }, (props) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MessageActionsController, { ...props, ctx, activation })));
2010
2008
  for (const patch of CLIENT_PATCHES) patch.install(ctx, activation);
2011
2009
  }
package/dist/index.js CHANGED
@@ -153,9 +153,8 @@ var MESSAGE_EDIT_PATCH_ID = "message-edit";
153
153
 
154
154
  // src/patches/message-edit/host/edit-continuation.ts
155
155
  import { createHash, randomUUID } from "node:crypto";
156
- import { resolveSessionPreset } from "@deepseek-ai/dsh-agent-presets";
157
156
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
158
- import { SessionId as SessionId2 } from "@deepseek-ai/dsh-session";
157
+ import { SessionId as SessionId2, SessionLogOffset, SessionSeq } from "@deepseek-ai/dsh-session";
159
158
 
160
159
  // src/platform/dsh/host/session-history.ts
161
160
  function completedTurns(events) {
@@ -233,18 +232,19 @@ function editedContent(original, text) {
233
232
  return [{ type: "text", text }, ...kept];
234
233
  }
235
234
  function inspectEditCut(session, targetSeq, editedText) {
235
+ if (!Number.isSafeInteger(targetSeq) || targetSeq < 0) throw new DshMoreError("bad-request", "\u6D88\u606F\u5E8F\u53F7\u65E0\u6548\u3002");
236
236
  const text = editedText.trim();
237
237
  if (text === "") throw new DshMoreError("bad-request", "\u7F16\u8F91\u540E\u7684\u6D88\u606F\u4E0D\u80FD\u4E3A\u7A7A\u3002");
238
238
  if (text.length > 1e5) throw new DshMoreError("bad-request", "\u7F16\u8F91\u540E\u7684\u6D88\u606F\u8FC7\u957F\u3002");
239
- const event = session.events[targetSeq];
239
+ const event = session.eventAt(SessionSeq(targetSeq));
240
240
  if (event?.type !== "user/message" || event.data.source.kind !== "user" || event.surfaceOp !== "append") {
241
241
  throw new DshMoreError("bad-request", "\u53EA\u80FD\u7F16\u8F91\u666E\u901A\u7528\u6237\u6D88\u606F\u3002");
242
242
  }
243
- const turns = completedTurns(session.events);
243
+ const turns = completedTurns(session.snapshotEvents());
244
244
  const owning = turns.find((turn) => targetSeq >= turn.startSeq && targetSeq <= turn.endSeq);
245
245
  if (owning === void 0) throw new DshMoreError("invalid-turn-range", "\u8FD9\u6761\u6D88\u606F\u6240\u5C5E\u8F6E\u6B21\u5C1A\u672A\u5B8C\u6210\u3002", 409);
246
246
  return {
247
- targetSeq,
247
+ targetSeq: event.seq,
248
248
  turn: owning.turn,
249
249
  turnStartSeq: owning.startSeq,
250
250
  laterTurnCount: turns.filter((turn) => turn.turn >= owning.turn).length,
@@ -252,17 +252,17 @@ function inspectEditCut(session, targetSeq, editedText) {
252
252
  };
253
253
  }
254
254
  function sourceMessage(session, targetSeq) {
255
- const event = session.events[targetSeq];
255
+ const event = session.eventAt(targetSeq);
256
256
  if (event?.type !== "user/message") throw new DshMoreError("bad-request", "\u7528\u6237\u6D88\u606F\u4E0D\u5B58\u5728\u3002");
257
257
  return event;
258
258
  }
259
259
  async function createEditedContinuation(ctx, sourceAgent, cut, editedText, childId = SessionId2(`session-${randomUUID()}`)) {
260
260
  const source = sourceAgent.session;
261
261
  const original = sourceMessage(source, cut.targetSeq);
262
- const presetId = resolveSessionPreset(source);
262
+ const presetId = ctx.sessionProjections.stateOf(source, "agentPreset") ?? void 0;
263
263
  const roster = ctx.get("agentPresets");
264
264
  const request = source.requestHeader()?.config;
265
- const seed = source.events.slice(0, cut.turnStartSeq);
265
+ const seed = source.snapshotEvents(SessionLogOffset(0), SessionLogOffset(cut.turnStartSeq));
266
266
  let releaseRuntimeContextReplay;
267
267
  const releaseReplay = () => {
268
268
  const release = releaseRuntimeContextReplay;
@@ -272,10 +272,11 @@ async function createEditedContinuation(ctx, sourceAgent, cut, editedText, child
272
272
  const child = await ctx.agents.create({
273
273
  sessionId: childId,
274
274
  seed,
275
+ inheritedEventCount: SessionLogOffset(seed.length),
275
276
  meta: {
276
277
  ...source.header.cwd === void 0 ? {} : { cwd: source.header.cwd },
277
278
  parentSession: source.id,
278
- seedLength: cut.turnStartSeq,
279
+ isSeeded: true,
279
280
  ...presetId === void 0 ? {} : { agentPreset: presetId }
280
281
  },
281
282
  agentOptions: {
@@ -367,9 +368,9 @@ var MESSAGE_DELETE_PATCH_ID = "message-delete";
367
368
 
368
369
  // src/patches/message-delete/host/message-selection.ts
369
370
  import { toolPairingBalancedAfter, toolPairingBalancedBefore } from "@deepseek-ai/dsh-compaction";
370
- import { isAppendSurfaceEvent } from "@deepseek-ai/dsh-session";
371
+ import { isAppendSurfaceEvent, SessionSeq as SessionSeq2 } from "@deepseek-ai/dsh-session";
371
372
  function assertDeletableTarget(session, seq) {
372
- const event = session.events[seq];
373
+ const event = session.eventAt(SessionSeq2(seq));
373
374
  if (event?.type === "user/message" && isAppendSurfaceEvent(event) && event.data.source.kind === "user") return;
374
375
  if (event?.type === "assistant/message" && isAppendSurfaceEvent(event)) return;
375
376
  throw new DshMoreError("bad-request", "\u53EA\u80FD\u5220\u9664\u4ECD\u5728\u5F53\u524D\u4E0A\u4E0B\u6587\u4E2D\u7684\u7528\u6237\u6D88\u606F\u6216\u52A9\u624B\u6D88\u606F\u3002");
@@ -377,7 +378,7 @@ function assertDeletableTarget(session, seq) {
377
378
  function selectMessageDeletion(session, targetSeq) {
378
379
  if (!Number.isSafeInteger(targetSeq) || targetSeq < 0) throw new DshMoreError("bad-request", "\u6D88\u606F\u5E8F\u53F7\u65E0\u6548\u3002");
379
380
  const nodes = [...session.surface.nodes];
380
- const targetIndex = nodes.indexOf(targetSeq);
381
+ const targetIndex = nodes.indexOf(SessionSeq2(targetSeq));
381
382
  if (targetIndex < 0) throw new DshMoreError("selection-empty", "\u8FD9\u6761\u6D88\u606F\u5DF2\u7ECF\u4E0D\u5728\u5F53\u524D\u6A21\u578B\u4E0A\u4E0B\u6587\u4E2D\u3002");
382
383
  assertDeletableTarget(session, targetSeq);
383
384
  let startIndex = targetIndex;
@@ -392,7 +393,7 @@ function selectMessageDeletion(session, targetSeq) {
392
393
  }
393
394
  const shadowedSeqs = nodes.slice(startIndex, endIndex + 1);
394
395
  if (shadowedSeqs.length === 0) throw new DshMoreError("selection-empty", "\u6CA1\u6709\u53EF\u5220\u9664\u7684\u4E0A\u4E0B\u6587\u8282\u70B9\u3002");
395
- return { targetSeq, shadowedSeqs };
396
+ return { targetSeq: SessionSeq2(targetSeq), shadowedSeqs };
396
397
  }
397
398
 
398
399
  // src/patches/message-delete/host/projection.ts
@@ -432,7 +433,7 @@ function deletionUpdate(event) {
432
433
  }
433
434
  var messageDeleteProjection = {
434
435
  key: MESSAGE_VISIBILITY_PROJECTION_KEY,
435
- schema,
436
+ stateSchema: schema,
436
437
  stateVersion: 3,
437
438
  init: () => ({ deletedSeqs: [], hiddenTrajectoryKeys: [] }),
438
439
  apply: (state, event) => {
@@ -443,14 +444,15 @@ var messageDeleteProjection = {
443
444
  if (nextSeqs.length === state.deletedSeqs.length && nextKeys.length === state.hiddenTrajectoryKeys.length) return state;
444
445
  return { deletedSeqs: nextSeqs, hiddenTrajectoryKeys: nextKeys };
445
446
  },
446
- view: (state) => state
447
+ wire: {
448
+ viewSchema: schema,
449
+ view: (state) => state
450
+ }
447
451
  };
448
452
 
449
453
  // src/patches/message-delete/host/rebuild.ts
450
454
  import { randomUUID as randomUUID3 } from "node:crypto";
451
- import { resolveSessionPreset as resolveSessionPreset2 } from "@deepseek-ai/dsh-agent-presets";
452
- import { CallId } from "@deepseek-ai/dsh-llm";
453
- import { Session, SessionId as SessionId4 } from "@deepseek-ai/dsh-session";
455
+ import { Session, SessionId as SessionId4, SessionLogOffset as SessionLogOffset2 } from "@deepseek-ai/dsh-session";
454
456
  function recordedDeletionSeqs(events) {
455
457
  const deleted = /* @__PURE__ */ new Set();
456
458
  for (const event of events) {
@@ -470,21 +472,10 @@ function recordedDeletionSeqs(events) {
470
472
  }
471
473
  return deleted;
472
474
  }
473
- function toolCalls(message) {
474
- const calls = [];
475
- for (const block of message.content) {
476
- const value = block;
477
- if (value.type !== "tool-call" || typeof value.name !== "string") continue;
478
- const callId = typeof value.id === "string" ? value.id : typeof value.callId === "string" ? value.callId : void 0;
479
- if (callId === void 0) continue;
480
- calls.push({ callId, name: value.name, arguments: typeof value.arguments === "string" ? value.arguments : "{}" });
481
- }
482
- return calls;
483
- }
484
475
  function buildCleanSeed(source, selection) {
485
- const deleted = recordedDeletionSeqs(source.events);
476
+ const deleted = recordedDeletionSeqs(source.snapshotEvents());
486
477
  for (const seq of selection.shadowedSeqs) deleted.add(seq);
487
- const survivors = source.surface.nodes.filter((seq) => !deleted.has(seq)).map((seq) => source.events[seq]).filter((event) => event !== void 0 && source.deriveEventMessage(event) !== null);
478
+ const survivors = source.surface.nodes.filter((seq) => !deleted.has(seq)).map((seq) => source.eventAt(seq)).filter((event) => event !== void 0 && source.deriveEventMessage(event) !== null);
488
479
  const rebuilt = Session.create(SessionId4(`session-rebuild-${randomUUID3()}`));
489
480
  let turn = 0;
490
481
  let step = 0;
@@ -534,14 +525,15 @@ function buildCleanSeed(source, selection) {
534
525
  ...event.data.usage === void 0 ? {} : { usage: event.data.usage }
535
526
  }, { surfaceOp: "append" });
536
527
  assistantSeen = true;
537
- for (const call of toolCalls(event.data.message)) {
538
- rebuilt.append("tool/call", { turn, step, callId: CallId(call.callId), name: call.name, arguments: call.arguments });
539
- loggedCalls.add(call.callId);
528
+ for (const call of event.data.message.content) {
529
+ if (call.type !== "tool-call") continue;
530
+ rebuilt.append("tool/call", { turn, step, callId: call.id, name: call.name, arguments: call.arguments });
531
+ loggedCalls.add(call.id);
540
532
  }
541
533
  continue;
542
534
  }
543
535
  if (event.type === "tool/result") {
544
- const callId = String(event.data.message.source.callId);
536
+ const callId = event.data.message.source.callId;
545
537
  if (!loggedCalls.has(callId)) {
546
538
  rebuilt.append("tool/call", { turn, step, callId: event.data.message.source.callId, name: "restored-tool", arguments: "{}" });
547
539
  loggedCalls.add(callId);
@@ -550,11 +542,11 @@ function buildCleanSeed(source, selection) {
550
542
  }
551
543
  }
552
544
  closeTurn();
553
- return rebuilt.events;
545
+ return rebuilt.snapshotEvents();
554
546
  }
555
547
  async function createDeletedContinuation(ctx, sourceAgent, selection, childId = SessionId4(`session-${randomUUID3()}`)) {
556
548
  const source = sourceAgent.session;
557
- const presetId = resolveSessionPreset2(source);
549
+ const presetId = ctx.sessionProjections.stateOf(source, "agentPreset") ?? void 0;
558
550
  const roster = ctx.get("agentPresets");
559
551
  const request = source.requestHeader()?.config;
560
552
  const provider = request?.provider ?? sourceAgent.options.provider;
@@ -574,10 +566,12 @@ async function createDeletedContinuation(ctx, sourceAgent, selection, childId =
574
566
  const child = await ctx.agents.create({
575
567
  sessionId: childId,
576
568
  seed,
569
+ // Rebuilt events are renumbered child-owned history, not a verbatim parent prefix.
570
+ inheritedEventCount: SessionLogOffset2(0),
577
571
  meta: {
578
572
  ...source.header.cwd === void 0 ? {} : { cwd: source.header.cwd },
579
573
  parentSession: source.id,
580
- seedLength: seed.length,
574
+ isSeeded: false,
581
575
  ...presetId === void 0 ? {} : { agentPreset: presetId }
582
576
  },
583
577
  agentOptions: {
@@ -611,7 +605,7 @@ async function createDeletedContinuation(ctx, sourceAgent, selection, childId =
611
605
 
612
606
  // src/patches/message-delete/host/index.ts
613
607
  function targetTurn(session, seq) {
614
- return completedTurns(session.events).find((turn) => seq >= turn.startSeq && seq <= turn.endSeq)?.turn ?? 0;
608
+ return completedTurns(session.snapshotEvents()).find((turn) => seq >= turn.startSeq && seq <= turn.endSeq)?.turn ?? 0;
615
609
  }
616
610
  function facts2(sessionId, session, selection, continuationSessionId) {
617
611
  const turn = targetTurn(session, selection.targetSeq);
@@ -973,7 +967,7 @@ function render(ctx, payload) {
973
967
  if ([...title].length > MAX_TITLE_LENGTH) throw new DshMoreError("bad-request", "\u4F1A\u8BDD\u6807\u9898\u8FC7\u957F\u3002");
974
968
  const session = requireLiveSession(ctx, sessionId);
975
969
  requireIdleAgent(ctx, sessionId, session);
976
- const transcript = conversationMarkdown(session.events, { title, sessionId });
970
+ const transcript = conversationMarkdown(session.snapshotEvents(), { title, sessionId });
977
971
  return new RawPatchResponse(transcript.markdown, "text/markdown; charset=utf-8");
978
972
  }
979
973
  var hostPatch4 = {
@@ -983,9 +977,6 @@ var hostPatch4 = {
983
977
  })
984
978
  };
985
979
 
986
- // src/patches/model-reasoning-efforts/host/index.ts
987
- import { settingsNamespace } from "@deepseek-ai/dsh-settings";
988
-
989
980
  // src/patches/model-reasoning-efforts/shared.ts
990
981
  var MODEL_REASONING_EFFORTS_PATCH_ID = "model-reasoning-efforts";
991
982
  var LLM_PI_AI_SETTINGS_NAMESPACE = "llm-pi-ai";
@@ -1028,7 +1019,7 @@ function validateReasoningEfforts(raw) {
1028
1019
  }
1029
1020
 
1030
1021
  // src/patches/model-reasoning-efforts/host/index.ts
1031
- var LLM_PI_AI_NS = settingsNamespace(LLM_PI_AI_SETTINGS_NAMESPACE);
1022
+ var LLM_PI_AI_NS = LLM_PI_AI_SETTINGS_NAMESPACE;
1032
1023
  function piAiSection(ctx) {
1033
1024
  const value = ctx.settings.get(LLM_PI_AI_NS);
1034
1025
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
@@ -1214,9 +1205,6 @@ var HostPatchActivation = class {
1214
1205
  }
1215
1206
  };
1216
1207
 
1217
- // src/kernel/host/settings.ts
1218
- import { installSettingsSection, settingsNamespace as settingsNamespace2 } from "@deepseek-ai/dsh-settings";
1219
-
1220
1208
  // src/generated/host-settings-schema.ts
1221
1209
  import z2 from "@deepseek-ai/schemastery";
1222
1210
  var PatchSettingsSchema = z2.object({
@@ -1229,11 +1217,10 @@ var PatchSettingsSchema = z2.object({
1229
1217
  });
1230
1218
 
1231
1219
  // src/kernel/host/settings.ts
1232
- var PATCH_SETTINGS_NAMESPACE2 = settingsNamespace2(PATCH_SETTINGS_NAMESPACE);
1220
+ var PATCH_SETTINGS_NAMESPACE2 = PATCH_SETTINGS_NAMESPACE;
1233
1221
  function installPatchSettings(ctx, activation) {
1234
1222
  let source = () => DEFAULT_PATCH_SETTINGS;
1235
- activation.apply(source());
1236
- installSettingsSection(ctx, PATCH_SETTINGS_NAMESPACE2, PatchSettingsSchema, DEFAULT_PATCH_SETTINGS, {
1223
+ ctx.settings.installSection(ctx, PATCH_SETTINGS_NAMESPACE2, PatchSettingsSchema, DEFAULT_PATCH_SETTINGS, {
1237
1224
  setSource: (next) => {
1238
1225
  source = next;
1239
1226
  },
@@ -1,3 +1,7 @@
1
- import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import type { Context as ClientContext } from '@deepseek-ai/cordis';
2
+ export type {} from '@deepseek-ai/dsh-client-ui-conversation/client';
3
+ export type {} from '@deepseek-ai/dsh-client-ui-layout/client';
4
+ export type {} from '@deepseek-ai/dsh-client-ui-settings/client';
5
+ export type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client';
2
6
  export declare const inject: string[];
3
7
  export declare function apply(ctx: ClientContext): void;
@@ -1,4 +1,5 @@
1
- import type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
2
+ import type { SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client';
2
3
  import { type PatchId, type PatchSettings } from '../../generated/patch-catalog.js';
3
4
  export interface PatchActivationSource {
4
5
  getSnapshot(): PatchSettings;
@@ -1,5 +1,6 @@
1
- import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import type { Context as ClientContext } from '@deepseek-ai/cordis';
2
2
  import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
3
+ export type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client';
3
4
  import type { PatchActivationSource } from './activation.js';
4
5
  type CardProps = PropsRuntime<'settings.plugin.item'>;
5
6
  export interface PatchConfigCardProps extends CardProps {
@@ -8,4 +9,3 @@ export interface PatchConfigCardProps extends CardProps {
8
9
  }
9
10
  export declare function PatchConfigCard({ activation, initiallyOpen }: PatchConfigCardProps): JSX.Element | null;
10
11
  export declare function installPatchConfigCard(ctx: ClientContext, activation: PatchActivationSource): void;
11
- export {};
@@ -1,3 +1,7 @@
1
+ export type {} from '@deepseek-ai/dsh-client-ui-renderer/client';
2
+ export type {} from '@deepseek-ai/dsh-client-ui-session/client';
3
+ export type {} from '@deepseek-ai/dsh-client-ui-chat/client';
4
+ export type {} from '@deepseek-ai/dsh-client-ui-conversation/client';
1
5
  import type { ReactNode } from 'react';
2
6
  import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
3
7
  export type ConversationHeaderProps = PropsRuntime<'conversation.session.header.utilities'>;
@@ -1,4 +1,4 @@
1
- import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import type { Context as ClientContext } from '@deepseek-ai/cordis';
2
2
  import type { PatchActivationSource } from './activation.js';
3
3
  import type { ConversationHeaderProps, MessageActions } from './message-actions.js';
4
4
  /** A self-contained client-side patch contributed to the umbrella plugin. */
@@ -1,5 +1,5 @@
1
1
  import type { Context } from '@deepseek-ai/cordis';
2
2
  import type { HostPatchActivation } from './activation.js';
3
- export declare const PATCH_SETTINGS_NAMESPACE: import("@deepseek-ai/dsh-settings").SettingsNamespace;
3
+ export declare const PATCH_SETTINGS_NAMESPACE = "dsh-more";
4
4
  /** Bind persisted DSH settings to live Host patch activation. */
5
5
  export declare function installPatchSettings(ctx: Context, activation: HostPatchActivation): void;
@@ -3,6 +3,11 @@ import type { PatchActivationSource } from '../../../kernel/client/activation.js
3
3
  import type { ClientPatch } from '../../../kernel/client/patch.js';
4
4
  import { type ConversationTurnSummary } from './navigation.js';
5
5
  type HeaderUtilityProps = PropsRuntime<'conversation.session.header.utilities'>;
6
+ export declare function conversationRow(root: HTMLElement, key: string): HTMLElement | null;
7
+ export declare function sessionRoot(marker: HTMLElement | null): HTMLElement | null;
8
+ export declare function conversationScrollport(marker: HTMLElement | null): HTMLElement | null;
9
+ /** The native pre-row paging control owns DSH's reader-position anchoring. */
10
+ export declare function nativeLoadOlderButton(scrollport: HTMLElement): HTMLButtonElement | null;
6
11
  export declare function ConversationNavigationMenu({ turns, activeKey, hasMore, loadingOlder, autoLoadPaused, collapsed, onRetry, onToggleCollapsed, onJump, listRef, }: {
7
12
  turns: readonly ConversationTurnSummary[];
8
13
  activeKey: string | null;
@@ -1,4 +1,4 @@
1
- import type { ConversationSnapshot } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import type { ChatSnapshot } from '@deepseek-ai/dsh-client-ui-chat/client';
2
2
  export interface ConversationTurnSummary {
3
3
  key: string;
4
4
  seq: number;
@@ -16,9 +16,9 @@ export interface AutoLoadOlderState {
16
16
  paused: boolean;
17
17
  }
18
18
  /** Select stable user-authored turn summaries without observing assistant streaming frames. */
19
- export declare function createConversationTurnSelector(): (snapshot: ConversationSnapshot) => readonly ConversationTurnSummary[];
19
+ export declare function createConversationTurnSelector(): (snapshot: ChatSnapshot) => readonly ConversationTurnSummary[];
20
20
  /** Stable evidence that an older history page prepended visible conversation material. */
21
- export declare function historyWindowSignature(snapshot: ConversationSnapshot): string;
21
+ export declare function historyWindowSignature(snapshot: ChatSnapshot): string;
22
22
  export declare function shouldAutoLoadOlder(state: AutoLoadOlderState): boolean;
23
23
  /** Pick the last turn at or above the reading line, falling back to the first turn. */
24
24
  export declare function activeTurnAt(positions: readonly TurnPosition[], readingLine: number): string | null;
@@ -1,4 +1,4 @@
1
- import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import type { Context as ClientContext } from '@deepseek-ai/cordis';
2
2
  import type { ConversationHeaderProps, MessageActions } from '../../../kernel/client/message-actions.js';
3
3
  export declare function useMessageActions(props: ConversationHeaderProps & {
4
4
  ctx: ClientContext;
@@ -1,7 +1,7 @@
1
- import { type Session } from '@deepseek-ai/dsh-session';
1
+ import { SessionSeq, type Session } from '@deepseek-ai/dsh-session';
2
2
  export interface MessageDeletionSelection {
3
- targetSeq: number;
4
- shadowedSeqs: readonly number[];
3
+ targetSeq: SessionSeq;
4
+ shadowedSeqs: readonly SessionSeq[];
5
5
  }
6
6
  /** Expand only as far as needed to keep tool call/result pairing valid. */
7
7
  export declare function selectMessageDeletion(session: Session, targetSeq: number): MessageDeletionSelection;
@@ -1,8 +1,31 @@
1
- import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection';
2
- import { MESSAGE_VISIBILITY_PROJECTION_KEY, type MessageVisibilityProjection } from '../../../kernel/message-visibility.js';
1
+ import { type SessionEvent } from '@deepseek-ai/dsh-session';
2
+ import { z } from 'zod';
3
+ import { type MessageVisibilityProjection } from '../../../kernel/message-visibility.js';
3
4
  declare module '@deepseek-ai/dsh-session-projection/types' {
5
+ interface SessionProjectionStateMap {
6
+ dshMoreMessageDelete: MessageVisibilityProjection;
7
+ }
4
8
  interface SessionProjectionMap {
5
9
  dshMoreMessageDelete: MessageVisibilityProjection;
6
10
  }
7
11
  }
8
- export declare const messageDeleteProjection: ProjectionDefinition<typeof MESSAGE_VISIBILITY_PROJECTION_KEY, MessageVisibilityProjection>;
12
+ export declare const messageDeleteProjection: {
13
+ key: "dshMoreMessageDelete";
14
+ stateSchema: z.ZodObject<{
15
+ deletedSeqs: z.ZodArray<z.ZodNumber>;
16
+ hiddenTrajectoryKeys: z.ZodArray<z.ZodString>;
17
+ }, z.core.$strip>;
18
+ stateVersion: number;
19
+ init: () => {
20
+ deletedSeqs: never[];
21
+ hiddenTrajectoryKeys: never[];
22
+ };
23
+ apply: (state: NoInfer<MessageVisibilityProjection>, event: SessionEvent) => MessageVisibilityProjection;
24
+ wire: {
25
+ viewSchema: z.ZodObject<{
26
+ deletedSeqs: z.ZodArray<z.ZodNumber>;
27
+ hiddenTrajectoryKeys: z.ZodArray<z.ZodString>;
28
+ }, z.core.$strip>;
29
+ view: (state: NoInfer<MessageVisibilityProjection>) => MessageVisibilityProjection;
30
+ };
31
+ };
@@ -1,4 +1,4 @@
1
- import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import type { Context as ClientContext } from '@deepseek-ai/cordis';
2
2
  import type { ConversationHeaderProps, MessageActions } from '../../../kernel/client/message-actions.js';
3
3
  export declare function useMessageActions(props: ConversationHeaderProps & {
4
4
  ctx: ClientContext;
@@ -1,10 +1,10 @@
1
1
  import type { Context } from '@deepseek-ai/cordis';
2
2
  import type { Agent } from '@deepseek-ai/dsh-agent';
3
- import { SessionId, type Session } from '@deepseek-ai/dsh-session';
3
+ import { SessionId, SessionSeq, type Session } from '@deepseek-ai/dsh-session';
4
4
  export interface EditCut {
5
- targetSeq: number;
5
+ targetSeq: SessionSeq;
6
6
  turn: number;
7
- turnStartSeq: number;
7
+ turnStartSeq: SessionSeq;
8
8
  laterTurnCount: number;
9
9
  contentDigest: string;
10
10
  }
@@ -1,5 +1,6 @@
1
- import type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
2
2
  import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
3
+ export type {} from '@deepseek-ai/dsh-client-ui-settings';
3
4
  import type { PatchActivationSource } from '../../../kernel/client/activation.js';
4
5
  import type { ClientPatch } from '../../../kernel/client/patch.js';
5
6
  import { type ThinkingLevel } from '../shared.js';
@@ -14,6 +15,21 @@ export interface ModelDraft {
14
15
  nonReasoning: boolean;
15
16
  levels: Partial<Record<ThinkingLevel, LevelDraft>>;
16
17
  }
18
+ /**
19
+ * Locate every model row of every open model catalog in the shipped Models
20
+ * settings page (the provider editor's 模型目录). The classes are the models
21
+ * package's own CSS-module hashes, stable per build.
22
+ */
23
+ export declare function findModelEntries(): HTMLElement[];
24
+ /**
25
+ * Resolve the provider route for one model entry: the editor header's route
26
+ * span when the route differs from the display name, else the owning provider
27
+ * card's display name (which for such providers equals the route). Returns
28
+ * null for the custom-provider creation card, which has no saved provider yet.
29
+ */
30
+ export declare function providerRouteOf(entry: HTMLElement): string | null;
31
+ /** Resolve the model id from a model entry's first row input (the id field). */
32
+ export declare function modelIdOf(entry: HTMLElement): string | null;
17
33
  /**
18
34
  * The level toggles + wire-value inputs shared by the editor modal. Draft
19
35
  * state lives in the caller so the modal survives models-page re-renders.
@@ -36,4 +52,3 @@ export declare function ModelsPageEffortPortal(props: OverlayProps & {
36
52
  activation: PatchActivationSource;
37
53
  }): JSX.Element | null;
38
54
  export declare const clientPatch: ClientPatch;
39
- export {};
@@ -1,4 +1,4 @@
1
- import type { ConversationSnapshot } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import type { ChatSnapshot } from '@deepseek-ai/dsh-client-ui-chat/client';
2
2
  import type { ConversationHeaderProps, MessageTarget } from '../../../kernel/client/message-actions.js';
3
3
  export interface MessageSurfaceRow {
4
4
  key: string;
@@ -10,5 +10,6 @@ export interface MessageSurfaceSnapshot {
10
10
  byKey: ReadonlyMap<string, MessageSurfaceRow>;
11
11
  }
12
12
  /** Select only facts used by message actions, retaining identity across unrelated stream frames. */
13
- export declare function createMessageSurfaceSelector(active?: boolean): (snapshot: ConversationSnapshot) => MessageSurfaceSnapshot;
13
+ export declare function createMessageSurfaceSelector(active?: boolean): (snapshot: ChatSnapshot) => MessageSurfaceSnapshot;
14
+ export declare function hostForRow(row: HTMLElement, kind: MessageTarget['kind']): HTMLElement;
14
15
  export declare function useMessageTargets(props: ConversationHeaderProps, hiddenSeqs: ReadonlySet<number>, hiddenTrajectoryKeys: ReadonlySet<string>, active?: boolean): readonly MessageTarget[];
@@ -1,4 +1,5 @@
1
- import type { ISessions, IWorkspaces } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import type { ISessions } from '@deepseek-ai/dsh-api-session-controller/client';
2
+ import type { IWorkspaces } from '@deepseek-ai/dsh-api-workspace-controller/client';
2
3
  export interface RefreshableSessions extends ISessions {
3
4
  refresh(): Promise<void>;
4
5
  }
@@ -1,4 +1,6 @@
1
- import type { SessionId, SessionListState, WorkspaceListState } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import type { SessionId } from '@deepseek-ai/dsh-session/types';
2
+ import type { SessionListState } from '@deepseek-ai/dsh-api-session-controller/client';
3
+ import type { WorkspaceSnapshot } from '@deepseek-ai/dsh-api-workspace-controller/client';
2
4
  import type { RefreshableSessions, RefreshableWorkspaces } from './runtime.js';
3
5
  /** Switch synchronously so a following archive/removal frame cannot expose the empty view. */
4
6
  export declare function openSessionWithoutGap(sessions: RefreshableSessions, sessionId: SessionId): void;
@@ -15,4 +17,4 @@ export declare function followSessionHandoff(sessions: RefreshableSessions, targ
15
17
  /** Prefer push-frame convergence after deletion; re-pull both baselines only as a fallback. */
16
18
  export declare function settleSessionRemoval(sessions: RefreshableSessions, workspaces: RefreshableWorkspaces, sessionId: SessionId): Promise<void>;
17
19
  /** Pick the nearest non-archived row so deleting the current session never exposes a blank gap. */
18
- export declare function adjacentVisibleSession(sessions: SessionListState, workspaces: WorkspaceListState, deletingSessionId: SessionId): SessionId | undefined;
20
+ export declare function adjacentVisibleSession(sessions: SessionListState, workspaces: WorkspaceSnapshot, deletingSessionId: SessionId): SessionId | undefined;
@@ -1,8 +1,8 @@
1
- import type { SessionEvent } from '@deepseek-ai/dsh-session';
1
+ import type { SessionEvent, SessionSeq } from '@deepseek-ai/dsh-session';
2
2
  export interface CompletedTurn {
3
3
  turn: number;
4
- startSeq: number;
5
- endSeq: number;
4
+ startSeq: SessionSeq;
5
+ endSeq: SessionSeq;
6
6
  }
7
7
  /** Return only turns with a durable start/end pair, in log order. */
8
8
  export declare function completedTurns(events: readonly SessionEvent[]): CompletedTurn[];
package/dsh.plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-more",
3
3
  "description": "Practical patches for missing DSH Web context and history controls",
4
- "version": "0.0.3",
4
+ "version": "0.0.4",
5
5
  "entry": {
6
6
  "name": "dsh-more",
7
7
  "inject": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-more",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Practical patches for missing DeepSeek Harness Web context and history controls.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -56,12 +56,16 @@
56
56
  "client": {
57
57
  "platform": "web",
58
58
  "inject": [
59
- "@deepseek-ai/dsh-client-runtime",
60
59
  "@deepseek-ai/dsh-client-ui-slots",
61
60
  "@deepseek-ai/dsh-client-ui-conversation",
62
61
  "@deepseek-ai/dsh-client-ui-primitives",
63
62
  "@deepseek-ai/dsh-client-ui-settings",
64
- "@deepseek-ai/dsh-client-ui-settings-plugins"
63
+ "@deepseek-ai/dsh-client-ui-settings-plugins",
64
+ "@deepseek-ai/dsh-api-session-controller",
65
+ "@deepseek-ai/dsh-api-workspace-controller",
66
+ "@deepseek-ai/dsh-client-ui-chat",
67
+ "@deepseek-ai/dsh-client-ui-session",
68
+ "@deepseek-ai/dsh-client-ui-renderer"
65
69
  ]
66
70
  }
67
71
  },
@@ -84,52 +88,57 @@
84
88
  "prepack": "npm run check"
85
89
  },
86
90
  "peerDependencies": {
87
- "@deepseek-ai/cordis": "^4.0.1",
88
- "@deepseek-ai/dsh-agent": "^0.1.0-rc.7",
89
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.7",
90
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.7",
91
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.7",
92
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.7",
93
- "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.0-rc.7",
94
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.7",
95
- "@deepseek-ai/dsh-compaction": "^0.1.0-rc.7",
96
- "@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.7",
97
- "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.7",
98
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.7",
99
- "@deepseek-ai/dsh-session": "^0.1.0-rc.7",
100
- "@deepseek-ai/dsh-session-persistence": "^0.1.0-rc.7",
101
- "@deepseek-ai/dsh-session-projection": "^0.1.0-rc.7",
102
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.7",
103
- "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.7",
104
- "@deepseek-ai/dsh-workspace": "^0.1.0-rc.7",
105
- "@deepseek-ai/dsh-web-app": "^0.1.0-rc.7",
106
- "@deepseek-ai/schemastery": "^3.18.1",
91
+ "@deepseek-ai/cordis": "^4.0.2",
92
+ "@deepseek-ai/dsh-agent": "^0.1.2-rc.1",
93
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-rc.1",
94
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-rc.1",
95
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-rc.1",
96
+ "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.2-rc.1",
97
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-rc.1",
98
+ "@deepseek-ai/dsh-compaction": "^0.1.2-rc.1",
99
+ "@deepseek-ai/dsh-agent-presets": "^0.1.2-rc.1",
100
+ "@deepseek-ai/dsh-host-webserver": "^0.1.2-rc.1",
101
+ "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
102
+ "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
103
+ "@deepseek-ai/dsh-session-persistence": "^0.1.2-rc.1",
104
+ "@deepseek-ai/dsh-session-projection": "^0.1.2-rc.1",
105
+ "@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
106
+ "@deepseek-ai/dsh-system-prompt": "^0.1.2-rc.1",
107
+ "@deepseek-ai/dsh-workspace": "^0.1.2-rc.1",
108
+ "@deepseek-ai/dsh-web-app": "^0.1.2-rc.1",
109
+ "@deepseek-ai/schemastery": "^3.18.2",
107
110
  "react": "^18.2.0",
108
111
  "react-dom": "^18.2.0",
109
- "zod": "^4.4.3"
112
+ "zod": "^4.4.3",
113
+ "@deepseek-ai/dsh-api-session-controller": "^0.1.2-rc.1",
114
+ "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-rc.1",
115
+ "@deepseek-ai/dsh-client-ui-chat": "^0.1.2-rc.1",
116
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.2-rc.1",
117
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-rc.1",
118
+ "@deepseek-ai/dsh-client-store": "^0.1.2-rc.1",
119
+ "@deepseek-ai/dsh-llm-retry": "^0.1.2-rc.1"
110
120
  },
111
121
  "devDependencies": {
112
- "@deepseek-ai/cordis": "4.0.1",
113
- "@deepseek-ai/dsh-agent": "0.1.0-rc.7",
114
- "@deepseek-ai/dsh-client-runtime": "0.1.0-rc.7",
115
- "@deepseek-ai/dsh-client-ui-conversation": "0.1.0-rc.7",
116
- "@deepseek-ai/dsh-client-ui-layout": "0.1.0-rc.7",
117
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.7",
118
- "@deepseek-ai/dsh-client-ui-settings": "0.1.0-rc.7",
119
- "@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.0-rc.7",
120
- "@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.7",
121
- "@deepseek-ai/dsh-compaction": "0.1.0-rc.7",
122
- "@deepseek-ai/dsh-agent-presets": "0.1.0-rc.7",
123
- "@deepseek-ai/dsh-host-webserver": "0.1.0-rc.7",
124
- "@deepseek-ai/dsh-llm": "0.1.0-rc.7",
125
- "@deepseek-ai/dsh-session": "0.1.0-rc.7",
126
- "@deepseek-ai/dsh-session-persistence": "0.1.0-rc.7",
127
- "@deepseek-ai/dsh-session-projection": "0.1.0-rc.7",
128
- "@deepseek-ai/dsh-settings": "0.1.0-rc.7",
129
- "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.7",
130
- "@deepseek-ai/dsh-workspace": "0.1.0-rc.7",
131
- "@deepseek-ai/dsh-web-app": "0.1.0-rc.7",
132
- "@deepseek-ai/schemastery": "3.18.1",
122
+ "@deepseek-ai/cordis": "4.0.2",
123
+ "@deepseek-ai/dsh-agent": "0.1.2-rc.1",
124
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.2-rc.1",
125
+ "@deepseek-ai/dsh-client-ui-layout": "0.1.2-rc.1",
126
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
127
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.2-rc.1",
128
+ "@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.2-rc.1",
129
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1",
130
+ "@deepseek-ai/dsh-compaction": "0.1.2-rc.1",
131
+ "@deepseek-ai/dsh-agent-presets": "0.1.2-rc.1",
132
+ "@deepseek-ai/dsh-host-webserver": "0.1.2-rc.1",
133
+ "@deepseek-ai/dsh-llm": "0.1.2-rc.1",
134
+ "@deepseek-ai/dsh-session": "0.1.2-rc.1",
135
+ "@deepseek-ai/dsh-session-persistence": "0.1.2-rc.1",
136
+ "@deepseek-ai/dsh-session-projection": "0.1.2-rc.1",
137
+ "@deepseek-ai/dsh-settings": "0.1.2-rc.1",
138
+ "@deepseek-ai/dsh-system-prompt": "0.1.2-rc.1",
139
+ "@deepseek-ai/dsh-workspace": "0.1.2-rc.1",
140
+ "@deepseek-ai/dsh-web-app": "0.1.2-rc.1",
141
+ "@deepseek-ai/schemastery": "3.18.2",
133
142
  "@types/node": "^24.0.0",
134
143
  "@types/react": "~18.3.0",
135
144
  "@types/react-dom": "~18.3.0",
@@ -138,6 +147,14 @@
138
147
  "react-dom": "18.3.1",
139
148
  "typescript": "^5.9.2",
140
149
  "vitest": "^4.0.0",
141
- "zod": "4.4.3"
150
+ "zod": "4.4.3",
151
+ "@deepseek-ai/dsh-api-session-controller": "0.1.2-rc.1",
152
+ "@deepseek-ai/dsh-api-workspace-controller": "0.1.2-rc.1",
153
+ "@deepseek-ai/dsh-client-ui-chat": "0.1.2-rc.1",
154
+ "@deepseek-ai/dsh-client-ui-session": "0.1.2-rc.1",
155
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.2-rc.1",
156
+ "@deepseek-ai/dsh-client-store": "0.1.2-rc.1",
157
+ "@deepseek-ai/dsh-llm-retry": "0.1.2-rc.1",
158
+ "happy-dom": "20.14.0"
142
159
  }
143
160
  }