Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

6 Commits

Folders and files

Repository files navigation

dsh-session-pin

Pin conversations (置顶) to the top of each workspace group in the DeepSeek Harness (DSH) web workspace list, and keep every group ordered by recency.

English | 中文

A DSH web plugin + a tiny patch to the official workspace browser. DSH's workspace/session list exposes no plugin extension point for menu buttons or ordering, so this package ships:

  1. A real, packaged plugin (dsh.client → lib/client.js) that:

    • renders a 「管理工作区」 (Manage workspace) button in the workspace header (left of search/view/add);
    • opens a manage panel (in the shell.overlay slot) listing all sessions with pin/unpin, AI rename, and per-session auto-rename opt-out;
    • exposes a window-scoped store window.__dspSessionPin (pin toggle, auto-rename toggle, per-session opt-out, persist + broadcast);
    • its server half (lib/index.js) auto-reapplies the tiny patch on every dsh web start, and exposes POST /dsh-session-pin/retitle so the panel can ask the host to regenerate a session's title from its content (DSH sessionTitle.refresh → configured LLM).
  2. A small patch to @deepseek-ai/dsh-client-ui-workspace/lib/client.js, split into four independently verifiable feature groups:

    • manage-button — the 「管理工作区」 gear button calling window.__dspSessionPin.open();
    • pin-order / pin-order-flat — list order becomes pinned-first, then newest-first (both pinned and unpinned follow recency, so a freshly-created session appears right below the pinned ones), in the grouped tree view and the flat view;
    • pinned-prop-plumbing — the pinned-id bus wired from WorkspaceBrowser into both lists.

    A group whose anchors drifted with a DSH upgrade fails alone: the other features keep working, the host log names the broken group, and node lib/patch-official.mjs --audit prints which anchor is missing.

AI re-title (auto + manual)

  • The manage panel has a global switch (default off): when on, sessions with no new message for over 1 day are auto re-titled from their content (runs DSH's LLM title provider via sessionTitle.refresh).
  • Each session row has an 「AI」 button to re-title it immediately (regardless of age).
  • Each session row has an opt-out toggle: turn it on to stop that one session from being auto re-titled (even if it passes 1 day).
  • "Over 1 day" is measured from the session's last message time (updatedAt).

The auto/manual re-title calls the host bridge (/dsh-session-pin/retitle) which runs DSH's built-in content-based title generation — the name is model-generated from the conversation, matching DSH's own automatic titles. Requires a registered LLM title provider (the shipped dsh-session-title-*-llm plugins) and a model to be configured.

⚠️ This is a plugin + patch, not a pure plugin. The patch is version-shaped, so it is tested against a fixture set of the upstream bundles (see fixtures/) and it disables only what it cannot apply — never the whole feature set, and never half-written code.


中文

把左侧「工作区」会话列表里的对话置顶,并让每个分组都按最近更新排序;还能按内容给会话重新命名(AI)。

  • 工作区标题右侧出现一个 ⚙ 管理工作区 按钮;点开面板即可对任意会话置顶/取消置顶、AI 重命名、单独停用某会话的自动重命名。
  • 排序:置顶会话在顶部,**置顶与非置顶都按最近更新(最新在前)**排序;置顶项自身也随时序更新,新建的会话会出现在置顶区下方。
  • AI 重命名:面板顶部有一个全局开关(默认关)。开启后,超过 1 天没有新消息的会话会自动按内容重新命名(复用 DSH 的 LLM 标题生成 sessionTitle.refresh);每个会话行有 「AI」按钮随时手动重命名,以及一个停用开关(打开后该会话即使超 1 天也不会被自动重命名)。
  • “超过 1 天”按最后一条消息时间(updatedAt)算。
  • 置顶/开关/停用状态存 localStorage,刷新/重启后保留。
  • 插件服务端每次启动会幂等重放官方补丁,并逐特性打印结果;另提供 POST /dsh-session-pin/retitle 桥接,让面板能把“按内容重新命名”的请求交给主机(由你配置的模型生成新标题)。
  • 补丁按特性分组(齿轮按钮 / 置顶排序 / 置顶状态接线),官方升版导致某一组锚点失效时只关掉那一组,其余照常工作,并会在宿主日志里点名是哪一组。
  • 补丁对 0.1.0-rc.7 / 0.1.0-rc.8 / 0.1.5-rc.1 三份官方 bundle 都有离线验证:node tests/patch-fixtures.test.mjs(50 项,不联网、不需要装 DSH)。
  • 齿轮没出现、排序也没变?先跑 node lib/patch-official.mjs --audit,它会明确指出哪一组、哪一步的锚点对不上;确认浏览器真正收到的那份文件可以用 README 上方 F12 那行命令。

Permissions & security (权限与风险披露)

This plugin is high-permission by design, and DSH STORE classifies it as a user-reviewed (per-install confirm) plugin rather than auto-approved:

  • Patches the official DSH bundle at boot: lib/patch-official.mjs reads/writes @deepseek-ai/dsh-client-ui-workspace/lib/client.js (node fs), backs it up (*.bak-session-pin), and runs node --check on the result (node child_process). It is seed-guarded (aborts loudly on version mismatch — never silently corrupts) and idempotent (skips when already patched). This is the only way to add an ordering/menu hook to a workspace list that exposes no plugin extension point.
  • Binds a local HTTP route POST /dsh-session-pin/retitle on the DSH web server (loopback). It resolves a session id and calls DSH's sessionTitle.refresh(session) — the configured LLM title provider — to re-title a conversation from its content. No credentials, no external host, no reads outside the DSH install.
  • Browser state is per-browser localStorage only (pins, auto-rename toggle, opt-outs); nothing is sent anywhere. The manage panel lives in shell.overlay.
  • Failure bounds: the patch writes nothing on version mismatch; the retitle route returns 400/404/500 without side effects on bad input; the server half never breaks DSH boot.

中文:该插件包含对官方 bundle 的补丁(写文件 + 运行 node --check),且提供本地 retitle 路由调用 LLM 重命名——因此属于高权限 / 用户逐次确认类别。补丁带种子校验、版本不符会安全中止;浏览器状态仅存于本机 localStorage;不访问凭据、不外联。

Acceptance evidence (install / config-synthesis / uninstall on a disposable profile) is recorded in docs/acceptance.md.


Structure

lib/
  client.js          # browser half (window.__dspSessionPin + manage panel)
  index.js           # server half (mount + boot-time patch report + retitle bridge)
  patch-spec.mjs     # WHAT gets patched: grouped, multi-version, seed-guarded spec
  patch-official.mjs # patch engine (candidate walk, per-group apply, --audit CLI)
cordis.patch.yml     # bundle patch (inserts dsh-session-pin into the roster)
fixtures/            # pristine upstream bundles, used by the offline fixture test
tests/               # node tests/patch-fixtures.test.mjs  (no network, no DSH install)
package.json         # dsh.client / dsh.bundle declarations, MIT

Install

# From this GitHub repo (or a tarball of it)
dsh plugin --profile web add https://github.com/Lstalu/dsh-session-pin

# or from a local checkout / tarball
dsh plugin --profile web add F:/path/to/dsh-session-pin
dsh plugin --profile web add ./dsh-session-pin-0.3.0.tgz

Then restart the web app (the browser client bundle is served at boot; the patch is also applied at boot by the server half).

Re-applying / verifying the patch

The server half auto-applies the patch on every start (and logs each feature's result). To run it manually:

node node_modules/dsh-session-pin/lib/patch-official.mjs        # auto-locates the official bundle
node node_modules/dsh-session-pin/lib/patch-official.mjs <path> # or point it explicitly
node node_modules/dsh-session-pin/lib/patch-official.mjs --audit # report only, writes nothing

It prints per-group status: applied, already, partial (some groups could not be applied) or failed, and exits non-zero only on failed. Any written output must pass node --check or it is rolled back from the <path>.bak-session-pin backup.

The gear button is missing / the list order looks stock? Run the audit and read the report — it names the exact group and anchor step that no longer matches this DSH build:

node node_modules/dsh-session-pin/lib/patch-official.mjs --audit

To check the copy the browser is actually being served (that is the file that matters):

// F12 console
fetch('/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js').then(r => r.text())
  .then(t => console.log('pinFirstSessions:', t.includes('pinFirstSessions'), 'menu.manage:', t.includes('menu.manage')))

Compatibility & notes

DSH release workspace bundle state
0.1.0-rc.7 @deepseek-ai/[email protected] ✅ all 4 groups
0.1.0-rc.8 @deepseek-ai/[email protected] ✅ all 4 groups
0.1.5-rc.1 @deepseek-ai/[email protected] ✅ all 4 groups
  • Every claim above is exercised offline against the exact upstream bundles in fixtures/: node tests/patch-fixtures.test.mjs (50 checks — apply, assert, node --check, idempotence, per-group isolation, candidate resolution).
  • If your DSH release is not in that table, run --audit and open an issue with the version and the reported missing step — the report is designed to say exactly what to rebase.
  • Pinning is stored per-session-id in browser localStorage; it is per-account/browser, not synced to the host.

Contributing / 发版注意事项

Three release-time gotchas that bit this repo before — follow these when tagging a new version:

  1. Run the fixture test before pushing anything. node tests/patch-fixtures.test.mjs (or npm test) is the only thing that catches "the patch silently stopped fitting the official bundle"; the author's own machine can stay on a blessed DSH release forever, so local manual testing will never surface it.

  2. Rebasing onto a new DSH release. The patcher is version-shaped; treat a new release as a fixture-and-seeds task, not a code task:

    # 1. capture the new upstream bundle as a fixture (see fixtures/README.md)
    # 2. run the test — it will name the exact group/step that no longer matches
    node tests/patch-fixtures.test.mjs
    # 3. add a seed variant to the failing step in lib/patch-spec.mjs, then re-run
    # 4. update the compatibility table above + dsh.compatibility.dshReleases in package.json

    Keep the old variants: they are what makes the plugin work on machines that are behind.

  3. Push hygiene. Never rewrite package.json with PowerShell Set-Content -Encoding utf8. On Windows PowerShell that writes a UTF-8 BOM (which makes DSH's JSON.parse fail at boot and crashes the server) and also mojibakes any Chinese text (e.g. 置顶 → 缃嫥《). Use your editor, or write no-BOM UTF-8 explicitly:

    [System.IO.File]::WriteAllText($path, $json, (New-Object System.Text.UTF8Encoding $false))

    After any write, verify: node -e "JSON.parse(require('fs').readFileSync('package.json','utf8'))" and check the content has no BOM (first 3 bytes are not EF BB BF).

  4. Pushing over a China-network proxy / ghfast mirror. A ghfast.top-style url.*.insteadOf rewrite is read-only and cannot push; direct https://github.com HTTPS git is often connection-reset. Push through the local VPN's HTTP proxy, temporarily disabling the rewrite, then restore it:

    git config --global --unset url.https://ghfast.top/https://github.com/.insteadof
    git -C . -c http.proxy=http://127.0.0.1:2081 push
    git config --global url."https://ghfast.top/https://github.com/".insteadOf "https://github.com/"

    (The proxy host/port (127.0.0.1:2081) is whatever your VPN client exposes; check it with Test-NetConnection 127.0.0.1 -Port 2081.)

Also note: this plugin legitimately patches the official DSH bundle (node:fs write + node:child_process run node --check), so DSH STORE classifies it high-permission / user-reviewed (per-install confirm), not auto-approved source-verified.


License

MIT

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages