deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
English | 简体中文
Smarter automatic session titles for DeepSeek Harness (DSH).
0.5.0-rc.7 — release candidate, intended for the npm next tag.
The settings page now shows model and title rules directly, with advanced options collapsed and visible save feedback. Batch optimization has a separate workspace with automatic loading, title/ID search, and back navigation. Batch routing reflects saved settings; fixed-model mode hides redundant automatic fallback. Date format appears only when a date affix is enabled.
Summarizes the first meaningful task into a short session title. By default, the title uses the exact provider and model used by that conversation. No separate model setup is required.
/retitle and a header button for explicit regeneration.DSH retains ownership of fallback titles, persistence, projections, cancellation, and stale-result protection. This plugin replaces only the title provider.
In the tested DSH version, the built-in provider rejects inputs above 4096 bytes
before calling the model. This plugin compresses them first. A 15,536-byte prompt
has been validated with a real model. Neither DSH Core nor app.asar is modified;
all model calls use DSH's existing LLM service.
Package: smart-session-title@next.
The next tag deliberately identifies a release candidate.
Use your DSH profile's package-management workflow to install the package into that profile. For a manually managed profile, run this inside the profile directory:
npm install smart-session-title@next --legacy-peer-deps
DSH supplies the runtime peers; --legacy-peer-deps prevents npm from
installing a separate Core stack. Follow the compatibility requirements below.
For local installation, copying this package into the profile's
node_modules/smart-session-title is also supported and has been verified on Desktop.
Back up the profile's package.json and cordis.patch.yml. Add the bundle after
the base and Web bundles, preserving all other existing bundles:
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"smart-session-title"
]
}
}
}
Restart DSH. The bundle disables the built-in session-title-llm provider before
registering this one. The startup log includes
smart-session-title provider registered ... automatic=all-prompts.
To uninstall, remove the bundle entry and restart, then remove the package. This restores the built-in provider. Disabling only this plugin's row while leaving its replacement patch active does not restore the built-in provider.
Validated with DSH Desktop 2.0.9, Core 0.1.5-rc.1, Cordis 4.0.2,
Schemastery 3.18.2, and Node ≥22.15.0.
DSH runtime peers use the prerelease-compatible range ^0.1.5-rc.1; Schemastery remains pinned to 3.18.2. Other Core versions have not been verified.
Startup checks the title, LLM, and Settings capabilities. Invalid settings fail before the provider is registered. The Web client uses official slots, locale, settingsScope, and Remote commands. No version-string hard rejection is used.
Open Settings → Smart Session Title.
The official DSH Settings service persists the smart-session-title namespace in
$DSH_HOME/settings.yaml, handling atomic writes, revisions, and file watching.
Changes affect the next generation; an in-flight request keeps its initial policy.
There is no separate config file, custom watcher, or HTTP server.
The footer of the page shows the loaded plugin version, so a bug report can name
the exact build.
| Field | Default | Purpose |
|---|---|---|
enabled |
true |
Master AI-title switch |
mode |
Follow the session¹ | current-session, configured, or disabled |
provider |
Unset | Existing DSH provider ID |
model |
Unset | Model ID belonging to that provider |
timeoutMs |
15000 | Per-attempt timeout; UI range 1000–120000 ms |
maxAttempts |
2 | Total attempts; range 1–3 |
maxTitleCharacters |
Unset (inherits 80 bytes) | Title character cap (Unicode code points); UI range 8–120 |
titleDatePosition |
Unset (no date) | prefix / suffix: put the session's creation date before or after the title |
titleDateFormat |
ymd |
ymd (2026-09-13) or md (09-13); unused while no position is chosen |
titleStyle |
Unset (plugin default, currently action + object) | action-object or short-name |
titleLanguage |
Unset (follows the task's main language) | zh or en, forced |
titleExclusions |
Unset (nothing excluded) | String list of words that must not appear in a generated title; at most 50 entries of at most 64 characters |
lockedSessionIds |
Unset (nothing locked) | String list of session ids whose title no entry point may rewrite; at most 500 entries |
¹ For older deployments only, an explicit provider/model pin in the bundle row
remains effective until the user chooses a mode. The shipped bundle has no pin.
Explicit current-session always uses the session route.
Edit settings, then click Save settings at the right of the model selection row. Changes stay local until saved; model and title rules are written together. Failed saves retain your edits. Batch optimization uses the last saved route. Provider and model must both be selected in fixed-model mode.
Advanced exposes timeout and attempts. Empty numeric fields inherit the bundle
configuration; deployment-level compression options are in cordis.patch.yml.
The Title shape and content block on the settings page needs no expanding:
maxTitleBytes: 80 (about 26 CJK characters or 80 Latin characters). A value
between 8 and 120 applies a code-point cap on top of the byte cap, and the
cut prefers a separator so a word is not sliced in half.session.header.createdAt, local time), not the moment of generation —
regenerating a title, or batch-retitling an old session, keeps that session's
own day. Choose a prefix (2026-09-13 Title), a suffix
(Title · 2026-09-13), or none.createdAt, or an
unresolvable time zone) the plugin adds no affix instead of a broken title.| Setting | Title for the same task ("检查登录接口并解决超时问题") |
|---|---|
| Default (action + object) + Auto | 修复登录接口超时 |
| Short task name + Chinese | 登录接口超时 |
| Action + object + English | Fix login API timeout |
Fill one word per line under Title exclusions (customer names, internal project codes), at most 50 entries of at most 64 characters:
Acme Corp
internal-project-x
With the task "修复客户甲的订单导出错误", an accepted result is "修复订单导出错误". Two layers:
If the last attempt still contains the term, the plugin deletes the term from the title rather than giving the title up — because giving it up leaves DSH's fallback title on the sidebar, and that fallback is the opening of the raw first message, i.e. precisely the path that would display the name. If deleting the term leaves nothing that passes validation, the generation fails outright and the sidebar keeps what it had.
Matching is literal: terms containing ASCII letters ignore case (Acme hides
ACME), everything else matches exactly. There is no stemming, alias or
translation — add 客户甲, ClientA and 甲方 separately.
It constrains only titles this plugin generates. That boundary is deliberate:
settings.yaml.So its accurate name is "title exclusions", not a privacy or redaction feature. Its most practical use is alongside Optimize past titles, to strip a name out of a whole batch of historical titles.
The lock button beside a session title (to the right of "Regenerate title") pins that title. The button shows the state itself: a closed padlock means locked, and the hover text and accessible label always name what a click will do ("Lock title" / "Unlock title").
All four entry points honour it:
| Rule | Actual behaviour |
|---|---|
| Automatic generation skips locked sessions | the provider abstains (locked) without a model call |
| Regeneration says why it will not run | the button is disabled and reads "Title is locked; unlock it first" |
| The batch list excludes locked sessions | they are not listed, and the count is joined by "Locked sessions skipped: N" |
| A batch run cannot reach them | even "Select all" cannot select a locked row |
lockedSessionIds in
settings.yaml), not in browser localStorage: it survives a new window, a
cleared browser profile, and a DSH restart.The same page lists your stored sessions with the title each one currently has, so you can batch-regenerate titles written before this plugin was installed. Pick the rows (filter by project, select all within the current filter), then start the run. Up to 300 rows are rendered at once — narrow with the project filter for larger histories.
/retitle per session, strictly sequential: a run of N sessions costs
N model calls and never runs two generations at once./retitle path is DSH's documented way to unpin a manual title.commands/execute accepts a trailing AbortSignal, so
the in-flight generation is cancelled rather than waited out, the queue does not
continue, and the interrupted session is counted as neither success nor failure.
Stop is reachable both in the settings page and from a small global progress
pill (shell.overlay), which is what makes a background run stoppable after the
settings page is closed.modelSelection
projection) and flags the ones DSH no longer serves, with a count, before
the run: those sessions cannot succeed while following the session model.settings.yaml:
that schema is host-owned and the client half may not extend it.request.route, never a
substituted global default model.enabled=false: zero title-model calls and zero provider-title
events. Fallback titles, manual renaming, and title projections keep working.Configured mode supports using a different provider and model from the conversation.
Run /retitle or click the regenerate button beside the session title.
Both invoke DSH's official SessionTitleService.refresh().
The button shows loading, prevents duplicate requests, and returns to idle afterward.
Explicit regeneration can replace a manual title. Automatic generation cannot.
Failures preserve the previous title; cancellation never retries.
When AI titles are disabled, /retitle returns AI title generation is disabled.
without a model call. A third party calling bare refresh() does not receive the
plugin's explicit permission token for replacing a manual title.
Hello → Thanks → Are you there? → Help me audit database performance spends no
title calls on the greetings and generates a title when the task appears.
Accepted provider titles do not drift with later messages. Child sessions do not
automatically receive titles.
The policy reads messages and titles replayed by Core, rather than maintaining its own history. Reopening a greeting-only session after a real process restart and sending a meaningful task has been verified through the Web UI.
Title generation is an additional model request, bounded by maxAttempts.
By default it uses the conversation's model. Configured mode sends the compressed
first meaningful task to the explicitly selected provider, which may be different.
Titles never enter model context. The plugin does not append an extra
session/title-llm-request prompt copy to the session log.
Only the thirteen declared Settings fields are accepted. Unknown credential-shaped
fields such as apiKey, token, cookie, credential, secret, and password are rejected.
Credentials remain entirely managed by DSH.
Plugin diagnostics contain no full prompt, model response, or credential. Raw adapter error messages are replaced with fixed failure descriptions to prevent sensitive input being echoed into diagnostic logs.
The plugin uses ctx.logger. Desktop's official FileExporter / LogFileSink
persists records in <DSH userData>/logs/host/dsh-YYYY-MM-DD.log, with warnings
and errors also written to .error.log. On macOS, userData is commonly
~/Library/Application Support/DSH Desktop/.
Search for smart-session-title generation. Metadata includes sessionId, route,
attempt, rawBytes, preparedBytes, elapsedMs, result/outcome, and failureReason.
Outcomes distinguish generated, abstained, failed, timed-out, retried, and cancelled.
Desktop's own masking may replace sessionId with ****; the plugin does not bypass it.
Enable info-level logging to retain success and abstention records.
/title-status shows process-local counters, which reset on restart. Persistent
history lives in the log files. Standalone SDK/Web runners may not install a file
exporter by default; the plugin does not create its own sink. Real disk persistence
has been tested using the shipped Desktop exporter in an isolated DSH runtime.
DSH permits only one title provider. An already registered error means another
provider is active and loading must fail. Do not enable another third-party title
provider alongside this plugin. The supplied bundle replaces the built-in one.
| Problem | Likely cause / action |
|---|---|
| Always fallback | Model failed, output was rejected, or AI is disabled; inspect diagnostics |
| already registered | Another title provider is active; remove the conflicting bundle |
| /retitle says disabled | Master switch is off or mode is disabled |
| Configured model fails | Provider/model unavailable or unsuitable for the short output budget; try Current session model |
| Title never updates | Inspect logs; existing manual/provider titles are automatically protected |
| Greeting remains | No meaningful task yet; send the actual task |
| Settings prevent startup | Invalid value or unknown field; correct the plugin namespace |
| No successful generations in log files | Check Desktop log level and its file exporter |
Batch: most old sessions fail with model error: upstream failure |
Those sessions logged a provider/model DSH no longer serves, so "Current session model" cannot work for them. The list flags them before the run; switch to Configured model (or tick the fallback) and use Retry failed |
| Configured model selected, but titles still follow the session model | The provider+model pair was not saved. Click Save settings to apply the selected mode and model |
| A batch vanished after I reloaded the window | The runner lives in the client half; a full window reload ends it. Titles already written stay |
| Stop does not seem to end the run | Stop aborts the in-flight generation; if it lingers, the adapter ignored cancellation — check the log for the session that was running |
This repository contains the installable plugin, bilingual documentation, and license. Private development reports, session records, and local test fixtures are excluded.
off.lib/*.js are tracked and shipped. Their original TypeScript sources
are unavailable, so JavaScript is maintained directly alongside lib/*.d.ts.
A persistent job queue and host-side route fallback are not implemented.Run npm test with Node.js 22.15 or newer. The repository includes client/i18n,
host-policy, and provider behavior tests; no additional dependencies are needed.
Validation files are excluded from the published package by the files whitelist.
Settings reject out-of-range or fractional numeric input with an explanation and
verify persisted values after writes. Automatic fallback preserves overall batch
counts and shows retry progress separately. Stopping it is reported as stopped;
mode changes observed during fallback are preserved when restoring the route.
The exclusion tests drive the real provider with a fake model stream and assert
that a term never reaches either half of the prompt, that a surviving term is
retried exactly once and then deleted, and that an all-excluded prompt abstains
without a model call.
The lock tests cover all four entry points: the provider abstains AND does not
consume the /retitle permission, the command handler refuses, the header button
disables itself with the reason, and the batch list drops locked rows so that even
"Select all" cannot reach them.
The settings page uses four collapsible cards: model, title shape and content, advanced parameters, and batch retitling. All four sections start collapsed and show concise saved-setting summaries; an active batch opens its controls automatically. The layout adapts to narrow windows, and form fields have associated labels and visible keyboard focus.
The expanded title-format section groups style, language and length together, places dates on a separate row, and gives exclusion words a full-width editor. Detailed rules stay in a collapsed help section.
The conversation header shows the current session ID in small, muted monospace text. Click to copy the full ID even when the display is truncated; hover to see the full value. Paste it into another conversation to help an agent locate this session (reading requires suitable tools and permissions). Open another session first to copy its ID. A failed clipboard write offers a read-only field for manual copying.
In plugin settings, Show SessionId in the conversation header changes this after clicking Save settings. It is on by default, persists as showSessionId, and works independently of AI title generation and title locking. It does not change session titles.
On DSH Desktop 2.0.9 the ID occupies a separate left-aligned line between the title toolbar and the conversation/trajectory tabs. This scoped layout depends on the verified host CSS class; if it changes, the ID falls back to the inline header slot.
The conversation header shows text labels for Regenerate title and Lock title / Title locked. Regeneration replaces the current title, including manual titles, without rerunning the conversation. Locking blocks automatic, manual and batch generation by this plugin; click Title locked to unlock. Chatting, manual renaming and DSH fallback titles remain available. The settings page explains these actions too.
Settings use compact label/control rows. A unified Save settings button sits beside the model choices, and usage help is collapsed at the bottom, and batch optimization keeps its separate entry.
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。