DSH plugin custom session titles: SessionTitleProvider
A session title provider is not “call a model and write a string”; it is a narrow contract: register one implementation, get invoked on the right cadence, return exact message attribution, and let the service normalize, cap, and persist the result. The service owns persistence and pinning; the provider only proposes a revision. Keeping that boundary clear avoids “generated but not written” and “the user rename was overwritten”.
DeepSeek Harness: register: one implementation with a lifecycle
One optional title provider per context, and registration is an effect with a lifecycle. Key points:(source)
register(provider)— pass id, automatic, and generate. Expected: returns an effect disposer that cleans up on unload.- Uniqueness — a duplicate registration replaces the old provider, which enters disposal. Expected: no two providers race to write.
- Late results do not commit — a generate call from the old provider cannot write after replacement. Expected: no title flapping.
- The disposer waits for quiescence — it settles after active calls finish. Expected: no half-written state on unload.
The provider id is recorded in the title source. Use a stable, recognizable name so later debugging shows who generated the title.
DeepSeek Harness: generate input
The provider receives a snapshot, not a live session read/write handle. Input fields:(source)
| Field | Meaning |
|---|---|
session | session being titled |
messages | eligible human text messages with seq and text |
route | current logged main request route, may be absent |
currentTitle | latest accepted title snapshot, possibly fallback |
signal | cancellation for supersession, disposal, timeout, or caller cancel |
messages contains human text only, not tool results or system prompts. Do not read the transcript yourself; use the snapshot.
currentTitle is “latest accepted”, not “the previous provider output”; it may be a fallback. The provider may ignore it or use it for incremental rewriting.
DeepSeek Harness: generate output
Return a title, exact attribution, and an optional model route; the service handles normalization, byte caps, and persistence. Three fields:(source)
title— proposed text. Expected: the service normalizes it and enforces a byte cap.messageSeqs— user seqs actually used, all from the current request. Expected: the service validates order and rejects out-of-range values.model— if a model was used, return provider and model identity. Expected: recorded in the title source and auxiliary request.
The provider must not write the session/title event; the service owns that. It also should not implement “empty title deletes the title”; normalization failure makes the service reject it.
DeepSeek Harness: Three generation strategies
first-prompt, all-prompts, and fallback-only trade cost against freshness. Three:(source)
- first-prompt — generate once after the first human message. Expected: fewer calls and stable titles; long conversations may drift.
- all-prompts — generate a revision on later human messages. Expected: closer to the topic, but more auxiliary requests and supersession.
- fallback-only — no provider; use the built-in fallback. Expected: zero cost, title from the first message or built-in rules.
With all-prompts, respect signal: a new message cancels the old call, and cancellation is not an error to log repeatedly.
DeepSeek Harness: Failure, replacement, and pinning
Failure should not make the session unusable; a user rename outranks any automatic result. Three points:(source)
- Generation failure — the title can stay at fallback or the last accepted revision. Expected: the session remains usable.
- Provider replacement — late results from the old provider do not commit. Expected: no overwrite of the new implementation.
- User rename — source user, automatic scheduling stops. Expected: only an explicit
refreshregenerates.
If the provider keeps a cache, clear it on replacement or disposal; otherwise the new implementation may read stale intermediate state.
DeepSeek Harness: Request records and byte caps
Title generation is an auxiliary model request, and the service records the model-visible input before dispatch. The record includes titleProvider, messageSeqs, route, system, messages, maxTokens, and reasoningEffort. Even a later failure is reproducible. (source)
Before storing, the service normalizes the title and enforces a byte cap; an over-limit title is not stored as returned. Do not assume “whatever length I return is what gets stored”.
A minimal provider skeleton
The provider implements only generate; writing and normalization belong to the service. Skeleton:
const provider = {
id: 'my-title-provider',
automatic: 'first-prompt',
async generate(request) {
const text = request.messages.map((m) => m.text).join('\n')
const title = await makeTitle(text, request.signal)
return {
title,
messageSeqs: request.messages.map((m) => m.seq),
}
},
}
const dispose = ctx.sessionTitle.register(provider)
generate must respect request.signal; the returned messageSeqs may only use the current request.messages, not other messages from the session.
DeepSeek Harness FAQ
Can a provider modify currentTitle directly? No. It is a snapshot; the provider returns a new title, and the service writes it.
generate was called but the title did not change—why?
Check whether messageSeqs came from the current request, whether the title normalized to empty, and whether it exceeded the byte cap. All three are rejected.
Can a provider restore a title after a user rename?
Not automatically; only an explicit refresh unpins and retries.
The short version: the provider proposes a revision with exact attribution; normalization, persistence, pinning, and replacement belong to the service. Writing the event yourself makes the result unpredictable.
FAQ
One optional provider per DeepSeek Harness context. register returns an effect disposer; registering a new implementation replaces the old one after disposal begins, and late results from the old provider never commit.
DeepSeek Harness: it receives the session, eligible human text messages with seq and text, the optional main request route, the latest accepted title snapshot currentTitle, and a cancellation signal.
DeepSeek Harness: they must come exactly from the current request.messages. The service validates order; out-of-range or fabricated seqs are not accepted, and the title is not written.
DeepSeek Harness: first-prompt generates once after the first human message, with lower cost and a stable title. all-prompts can revise on later human messages, staying closer to the topic at the cost of more requests. Choose by product experience and quota.
DeepSeek Harness: not automatically. A user rename writes a title with source user and pins it, stopping automatic scheduling. Only an explicit refresh deliberately unpins and retries.
Related Terms
- SessionTitleProvider
- SessionTitleProvider is an optional asynchronous title provider in DeepSeek Harness, with a stable id, an automatic cadence, and a generate method. One implementation is allowed per context.— DeepSeek Harness reference - Session titles
- automatic
- automatic is the title provider’s declared generation cadence, either first-prompt or all-prompts, deciding which human messages trigger generate.— DeepSeek Harness reference - Session titles
- currentTitle
- currentTitle is the latest accepted title snapshot at provider invocation, possibly from fallback or a previous provider. The provider decides whether to use it.— DeepSeek Harness reference - Session titles
- messageSeqs
- messageSeqs lists the user message seqs actually used to derive a title. They must come from the current request.messages, and the service records them as title attribution.— DeepSeek Harness reference - Session titles
Sources
- DeepSeek Harness reference - Session titles· deepseek-ai
- DeepSeek Harness reference - Sessions· deepseek-ai