feat(telemetry): session-telemetry seam with mandatory redaction + OTel backend

Revive the reviewed session-telemetry packages from the closed
session-telemetry-otlp-rfc branch (PR #222/#231) on current master, renamed
to @deepseek-ai/dsh-session-telemetry{,-otel} (the SDK component-telemetry
package holds the dsh-telemetry name).

Delta over the branch version: every record now passes a telemetry/redact
waterfall between projection and emit() — the innermost next() applies a
non-configurable conservative credential-shape rule set, listeners stack
stricter rules, a throwing rule withholds the record fail-closed, and the
canonical log is never rewritten. This answers the export-side concern that
closed PR #222; the boundary axiom (our aspect ends at emit(); delivery is
the reporting SDK's) is unchanged, and the runtime-telemetry RFC's outbox /
readCommitted lane is recorded as deferred in the Agent Note.

Covered by seam/redact/OTel-wire unit tiers (100% per-file) and a keyless
Loader-composition e2e that boots the examples fixture against a mock OTLP
collector and pins redaction on the wire plus the untouched canonical log.
This commit is contained in:
kingwl
2026-07-23 03:06:57 +08:00
parent f7b36bd36d
commit cf2e184112
39 changed files with 2207 additions and 7 deletions

View File

@@ -803,6 +803,33 @@ Emitted when any prompt provider changes. This registry notification is unfilter
Source: [`packages/core/system-prompt/src/index.ts:35`](../../packages/core/system-prompt/src/index.ts)
## `telemetry/*`
### `telemetry/redact` — waterfall
Redact one outbound record before it reaches the backend. The innermost `next()` applies the seam's conservative default rule set (credential-shape scrubbing); listeners stack stricter rules by transforming its return value, and returning without `next()` replaces the default — the exported record is then only as clean as the replacing rule. Dispatched synchronously on the capture hot path inside the coordinator's containment: a throwing listener withholds that one record (fail-closed) and never reaches the agent loop. Redaction applies to the exported copy only; the canonical session log is never rewritten.
```ts cordis-catalog
/**
* Redact one outbound record before it reaches the backend. The innermost
* `next()` applies the seam's conservative default rule set
* (credential-shape scrubbing); listeners stack stricter rules by
* transforming its return value, and returning without `next()` replaces
* the default — the exported record is then only as clean as the
* replacing rule. Dispatched synchronously on the capture hot path inside
* the coordinator's containment: a throwing listener withholds that one
* record (fail-closed) and never reaches the agent loop. Redaction
* applies to the exported copy only; the canonical session log is never
* rewritten.
* @param record - the candidate record, already the coordinator's own deep
* copy; listeners return a (possibly new) record and must not mutate it.
* @mode waterfall
*/
'telemetry/redact'(record: TelemetryRecord, next: () => TelemetryRecord): TelemetryRecord
```
Source: [`packages/telemetry/session-telemetry/src/index.ts:39`](../../packages/telemetry/session-telemetry/src/index.ts)
## `tools/*`
### `tools/change` — emit

View File

@@ -1533,6 +1533,29 @@ Types: [Agent](../core-data-structures/core.md) · [TaskDoneListener](../core-da
Source: [`packages/tasks/tasks/src/index.ts:77`](../../packages/tasks/tasks/src/index.ts)
## `ctx.telemetry` — `Telemetry` (abstract seam)
The backend contract in its loadable form: one implementation per context — the cordis `Service` registration under the `telemetry` key throws on a duplicate, cordis' standard behavior. A backend composes a TelemetryCoordinator in its constructor to install the capture side.
```ts cordis-catalog
/**
* See {@link TelemetryBackend.emit} — the seam declaration is the contract's one home.
* @param record - the logical record to report; owned by the backend after the call.
*/
abstract emit(record: TelemetryRecord): void
/** See {@link TelemetryBackend.flush}. */
flush?(): void
/**
* See {@link TelemetryBackend.shutdown}.
* @returns resolves when the backend's pipeline has quiesced.
*/
abstract shutdown(): Promise<void>
```
Source: [`packages/telemetry/session-telemetry/src/index.ts:123`](../../packages/telemetry/session-telemetry/src/index.ts)
## `ctx.tokenMeter` — `TokenMeterService`
Replay owner for one service-wide estimator and isolated per-session folds.