refactor(telemetry): ship the redact waterfall without built-in rules
The seam keeps the telemetry/redact scrubbing interface but ships no rules of its own: the innermost next() passes records through unchanged, and deployments mount their rules as waterfall listeners. As an SDK we cannot know which patterns are secrets in a given deployment; a shipped list invites false confidence while catching only known shapes, and false positives would corrupt exported bodies. Mechanism stays with the seam, policy moves to the deployment; both READMEs and the Agent Note state the raw-export default plainly. The loader-composition e2e now mounts a deployment-style rule fixture and pins the same wire behavior: secret absent, placeholder present, canonical log untouched.
This commit is contained in:
@@ -19,7 +19,7 @@ The OpenTelemetry backend for [the telemetry seam](../session-telemetry/) — th
|
||||
|
||||
## What leaves the machine
|
||||
|
||||
Records carry the seam's REDACTED copy of `event.data` — user and assistant message content, tool arguments and results (command output, file contents), the full system prompt and tool schemas (`request/header`), todo text, compaction summaries, hook `stderrSummary`, and the session `cwd` (a local path) — after the seam's `telemetry/redact` waterfall has scrubbed credential-shaped substrings (see [the seam README](../session-telemetry/README.md#the-redact-waterfall)). Provider credentials never appear regardless: adapter API keys are constructor parameters, not session events, so they are structurally absent from the log and therefore from telemetry. A deployment with stricter requirements stacks `telemetry/redact` listeners or opts out structurally.
|
||||
Records carry the complete `event.data` as the seam's `telemetry/redact` waterfall returns it — user and assistant message content, tool arguments and results (command output, file contents), the full system prompt and tool schemas (`request/header`), todo text, compaction summaries, hook `stderrSummary`, and the session `cwd` (a local path). The seam ships no redaction rules: with no `telemetry/redact` listener mounted, that is the raw captured copy, so a deployment exporting beyond a trusted boundary mounts its own rules (see [the seam README](../session-telemetry/README.md#the-redact-waterfall)). Provider credentials never appear regardless: adapter API keys are constructor parameters, not session events, so they are structurally absent from the log and therefore from telemetry.
|
||||
|
||||
## Field mapping
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* a subprocess (per testing policy, through the same app/boot path a
|
||||
* deployment uses), run one mocked-model turn with a real bash round trip,
|
||||
* and assert against what the mock OTLP collector actually received on the
|
||||
* wire: ledger mirroring, default redaction, ops markers, and the untouched
|
||||
* canonical log.
|
||||
* wire: ledger mirroring, the deployment-mounted redact rule applied to the
|
||||
* exported copy, ops markers, and the untouched canonical log.
|
||||
*/
|
||||
|
||||
import { readFile, readdir } from 'node:fs/promises'
|
||||
@@ -12,7 +12,6 @@ import { join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
|
||||
import { REDACTION_PLACEHOLDER } from '@deepseek-ai/dsh-session-telemetry'
|
||||
|
||||
const driver = fileURLToPath(new URL(
|
||||
'../../../../examples/headless-agent/tests/fixtures/telemetry-otel-driver.ts',
|
||||
@@ -25,6 +24,7 @@ const configPath = fileURLToPath(new URL(
|
||||
const repoTsconfig = fileURLToPath(new URL('../../../../tsconfig.json', import.meta.url))
|
||||
|
||||
const FIXTURE_SECRET = 'sk-e2efixture1234567890'
|
||||
const FIXTURE_PLACEHOLDER = '[E2E-REDACTED]'
|
||||
|
||||
interface OtlpLogRecord {
|
||||
attributes?: { key: string; value: Record<string, unknown> }[]
|
||||
@@ -84,15 +84,16 @@ describe('session-telemetry-otel through a real headless cordis.yml', () => {
|
||||
}
|
||||
expect(records.some(({ scope }) => scope.endsWith('/ops'))).toBe(true)
|
||||
|
||||
// Default redaction on the wire: the fixture credential never leaves the
|
||||
// process, its surrounding prose does, and the placeholder marks the spot.
|
||||
// The deployment-mounted rule on the wire: the fixture credential never
|
||||
// leaves the process, its surrounding prose does, and the placeholder
|
||||
// marks the spot — the seam itself ships no rules.
|
||||
const wire = JSON.stringify(captures)
|
||||
expect(wire).not.toContain(FIXTURE_SECRET)
|
||||
expect(wire).toContain(REDACTION_PLACEHOLDER)
|
||||
expect(wire).toContain(FIXTURE_PLACEHOLDER)
|
||||
expect(wire).toContain('prove telemetry with key')
|
||||
|
||||
// The canonical session log is never rewritten.
|
||||
expect(logContent).toContain(FIXTURE_SECRET)
|
||||
expect(logContent).not.toContain(REDACTION_PLACEHOLDER)
|
||||
expect(logContent).not.toContain(FIXTURE_PLACEHOLDER)
|
||||
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user