4.4 KiB
Agent Note: DeepSeek request user and session identity headers
Status: implemented
English | 中文
Problem
Direct DeepSeek requests already carried x-deepseek-harness-session-id when the caller supplied GenerateOptions.sessionId, which lets provider-side support and diagnostics correlate turns within one conversation. They lacked a stable identity across sessions even though the harness already persists an anonymous user id for telemetry and feedback. A separate id would break correlation, while putting it in the provider-neutral attribution helper would send a stable per-user identifier through every HTTP adapter.
The user id is transport metadata, not model input. It must not enter the request body, prompt, token accounting, KV-cache identity, or session log. The destination is the adapter's resolved baseURL, which can be DeepSeek itself or a configured gateway, so the privacy boundary must be explicit.
Decision
dsh-llm-deepseek sends x-deepseek-harness-user-id on every provider request sent after successful credential resolution. The value comes from @deepseek-ai/dsh-user-id and therefore matches the OpenTelemetry Resource user.id and /feedback acknowledgement for the same $DSH_HOME. The adapter continues to send x-deepseek-harness-session-id only when GenerateOptions.sessionId is present; the agent loop supplies the current durable Session.id for ordinary agent, title-generation, and compaction requests.
The plugin resolves the user id lazily after credentials succeed and memoizes it for that plugin instance. A missing credential therefore does not create .userid, while the first authorized provider request can create it even when DSH_TELEMETRY_DISABLED is set. The direct adapter constructor accepts a resolveUserId dependency so wire behavior remains deterministic in unit tests.
Both headers are model-hidden HTTP metadata sent to the resolved baseURL. They are absent from the JSON request body and do not become model-visible inputs or session events. A configured gateway receives them. Telemetry sharing controls only telemetry export and does not disable provider request identity.
Verification
- The mock provider asserts that an authorized request carries the same user id returned by
getOrCreateAnonymousUserId()and omits the session header when no session id is supplied. - The session-identity wire test asserts both headers and preserves the exact supplied session id.
- A direct-adapter test asserts that user-id resolution happens once per stream, while the keyless configuration test proves a credential failure does not create
.userid. - The real Loader composition test asserts that the assembled plugin uses the shared user-id package rather than a test-only value.
- No keyless snapshot changes because the headers are not model-visible or user-visible transcript content.
Alternatives considered
| Rejected | Reason |
|---|---|
Add the id to generic attributionHeaders() |
That helper is provider-neutral and static; a per-user value there would reach unrelated providers and violate its app-identity privacy contract |
Configure a fixed custom header in cordis.yml |
Deployment configuration cannot derive the current session id and would expose a stable identity as mutable config instead of using its owning runtime contract |
| Mint a DeepSeek-specific user id | Provider requests could not correlate with telemetry and feedback for the same harness home |
| Disable the header with telemetry sharing | Provider request identity and telemetry export have different recipients and purposes; one switch would hide the actual privacy boundary |
Put the id in OpenAI-compatible user or metadata request fields |
Body fields can affect provider schema, logging, caching, tokenization, or model-visible reconstruction; HTTP metadata preserves the intended boundary |
Consequences
- DeepSeek support can correlate requests across sessions by one anonymous harness-home id and within a conversation by the durable session id.
- The first authorized DeepSeek request may create
$DSH_HOME/.useridindependently of telemetry export. - Custom DeepSeek gateways receive the stable user id and any available session id, so operators must treat the configured
baseURLas an identity recipient. - The request body, prompt, token count, KV-cache identity, and session log remain unchanged.