Record sdk-minimal as the narrow repository-owned exception to base-first profile composition: callers still launch only dsh and cannot provide an arbitrary Cordis tree, while the shipped bundle may own a complete explicit roster. Cross-link the launcher, profile-bundle, Python-runtime, minimal-agent, snapshot, and telemetry decisions; the supersession audit keeps each older note active because its remaining rationale is independent. Update the CLI, architecture, Python tutorial/reference, example, runtime-wheel reference, and bundle documentation. The docs distinguish the full sdk profile from sdk-minimal, explain explicit-home/plugin/patch customization, state the minimal permission and persistence choices, and retain the separately packaged web profile and frontend assets for direct dsh use. Correct dsh-base descriptions to cover base-backed profiles, make SDK startup configuration visible in the generated config catalog, add sdk-minimal to the module graph, and regenerate the base-composition graph. English and Chinese pairs are re-recorded at the exact reviewed contents.
5.3 KiB
Agent Note: Default session-telemetry mount (OTel reporting) in the dsh web composition
Status: implemented
English | 中文
Problem
The telemetry seam and OTel backend (revival Note) had never been wired into any deployment composition since completion: no roster row, no switch, no cadence ruling, and zero observability over user sessions for the internal deployment. A deployment decision was needed: which surfaces report, to where, on what cadence, how to opt out, and how CI stays isolated.
Decision
The shared dsh base bundle (packages/bundle/base/cordis.patch.yml) mounts the session-telemetry-otel row with a baked-in production endpoint, so every base-backed profile has one consistent telemetry capability. The standalone sdk-minimal profile deliberately omits that row. The default-off decision keeps the mounted row in DISABLED mode unless a deployment explicitly selects FULL or FEEDBACK_ONLY; the endpoint alone does not authorize reporting. Web and headless use the bounded, escalating process-shutdown controller on SIGINT/SIGTERM, giving an enabled backend's three-second shutdown deadline time to drain before the five-second launcher bound.
| Ruling | Value | Rationale |
|---|---|---|
| Mount surface | packages/bundle/base/cordis.patch.yml |
One capability row for every profile that loads the shared base |
| Sharing mode | DSH_TELEMETRY_MODE, default DISABLED; explicit FULL or FEEDBACK_ONLY opts in |
A fresh profile makes no telemetry network request, while internal deployments retain both upload policies |
| Endpoint | DSH_TELEMETRY_OTLP_URL, default https://harness-telemetry.deepseeksvc.com/v1/logs |
Internal collector; the env override serves local/dev runs |
| Hard opt-out | any non-empty DSH_TELEMETRY_DISABLED (including 0/false) disables the row |
The launcher patch takes effect before load-time transport validation and overrides every configured mode |
| Cadence | processor.scheduledDelayMillis: 10000 (10s/batch) in uploading modes |
Streaming while the session runs, never exit-time-only; a crash loses at most the last unexported interval |
| Exit-drain bound | exporter.timeoutMillis: 1000 + maxExportBatchSize: 2048 (== maxQueueSize) + exportTimeoutMillis: 1500 + shutdownTimeoutMillis: 3000 |
Ordinary unreachable-collector failure releases in ~1s: timeoutMillis is the per-attempt socket timeout and retry deadline, while one queue-sized batch avoids sequential drain multiplication. The DSH-owned 3s outer bound covers the SDK's preceding unbounded forceFlush() wait when the transport Promise never obtains a socket. |
| Compression | compression: gzip |
Event bodies carry full content; cross-datacenter bandwidth |
| CI isolation | top-level env: DSH_TELEMETRY_DISABLED: '1' in GitHub workflows |
Defense in depth keeps test sessions local even when a job explicitly selects an uploading mode |
The base bundle test pins the shipped DISABLED mode expression, the backend suite pins that omitted mode constructs no transport, and the real Loader composition suite explicitly selects each uploading mode when it verifies OTLP delivery.
Alternatives considered
No default mount; deployments add the row themselves. Rejected because the mounted DISABLED mode retains a local feedback warning and gives every base-backed profile one patch target without authorizing any upload.
A config field instead of an env patch for the switch. Infeasible: cordis rows have no config-level disable semantic, and exporter.url validation fails loud at plugin construction, so the switch must take effect before the Loader — AppCLIEntry's patch layer is the only seat.
A Promise.race timeout backstop around exit. Originally deferred because the SDK parameters appeared to bound the backend's drain to ~1.5-3s (typically <100ms), with measured SIGINT-to-exit of 110ms-1.1s. A Linux sandbox reproduction later proved that BatchLogRecordProcessor.shutdown() can wait forever in exporter.forceFlush() before reaching its exportTimeoutMillis-bounded completion Promise. The CLI shutdown fix therefore adds both a three-second backend bound for that specific gap and a five-second process-level bound plus repeated-signal escape for the whole plugin tree.
Consequences
- A developer running
dsh webwithout telemetry configuration makes no telemetry network request. An internal deployment setsDSH_TELEMETRY_MODEand may pointDSH_TELEMETRY_OTLP_URLat another collector. - No redaction rule is mounted: explicitly enabled exports are the raw captured copy (full user/assistant message text, tool arguments and results, the system prompt, the local
session.cwdpath). Crossing a trust boundary requiressession-telemetry/recordrules first — the redaction rule, remaining identity Resource attributes, and usage metrics remain separate deployment work. The anonymous user id ships through the anonymous-user-id Note. - Test rigs remain local by default; explicit uploading-mode tests provide their own collector and mode.