Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
130 lines
5.3 KiB
TypeScript
130 lines
5.3 KiB
TypeScript
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
|
/**
|
|
* The `session-telemetry/record` waterfall contract: pass-through when no listener is
|
|
* mounted, listener stacking and replacement, ops-record coverage, the
|
|
* untouched canonical log, and the fail-closed containment of a throwing rule.
|
|
*/
|
|
|
|
import { describe, expect, it } from 'vitest'
|
|
import { Context } from '@deepseek-ai/cordis'
|
|
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
|
import {
|
|
SessionTelemetryCoordinator,
|
|
type SessionTelemetrySink,
|
|
type SessionTelemetryRecord,
|
|
} from '../src/index.ts'
|
|
|
|
const FIXTURE_SECRET = 'sk-fixture1234567890'
|
|
|
|
class CollectingBackend implements SessionTelemetrySink {
|
|
records: SessionTelemetryRecord[] = []
|
|
emit(record: SessionTelemetryRecord): void {
|
|
this.records.push(record)
|
|
}
|
|
async shutdown(): Promise<void> {}
|
|
}
|
|
|
|
async function setup() {
|
|
const backend = new CollectingBackend()
|
|
const ctx = new Context()
|
|
await ctx.plugin(SessionStore)
|
|
const fiber = await ctx.plugin({
|
|
name: 'fake-telemetry',
|
|
inject: ['sessions'],
|
|
apply: (inner: Context) => void new SessionTelemetryCoordinator(inner, backend),
|
|
})
|
|
return { ctx, backend, fiber }
|
|
}
|
|
|
|
describe('session-telemetry/record waterfall', () => {
|
|
it('passes records through unchanged when no listener is mounted', async () => {
|
|
const { ctx, backend } = await setup()
|
|
const session = ctx.sessions.create(SessionId('w'))
|
|
session.append('user/message', createUserMessage({
|
|
content: [{ type: 'text', text: `key ${FIXTURE_SECRET}` }], source: { kind: 'user' },
|
|
}), { surfaceOp: 'append' })
|
|
const body = backend.records[0]!.body as { content: { text: string }[] }
|
|
expect(body.content[0]!.text).toBe(`key ${FIXTURE_SECRET}`)
|
|
})
|
|
|
|
it('applies a mounted rule to every outbound record, ops records included', async () => {
|
|
const { ctx, backend, fiber } = await setup()
|
|
ctx.on('session-telemetry/record', (_record, next) => {
|
|
const record = next()
|
|
return { ...record, body: { scrubbed: true } }
|
|
})
|
|
const session = ctx.sessions.create(SessionId('rule'))
|
|
session.append('user/message', createUserMessage({
|
|
content: [{ type: 'text', text: FIXTURE_SECRET }], source: { kind: 'user' },
|
|
}), { surfaceOp: 'append' })
|
|
expect(backend.records[0]!.body).toEqual({ scrubbed: true })
|
|
// The dispose-time shutdown ops record passes through the same waterfall.
|
|
await fiber.dispose()
|
|
const ops = backend.records.filter(record => record.channel === 'ops')
|
|
expect(ops).toHaveLength(1)
|
|
expect(ops[0]!.body).toEqual({ scrubbed: true })
|
|
})
|
|
|
|
it('keeps the canonical log untouched by a mounted rule', async () => {
|
|
const { ctx } = await setup()
|
|
ctx.on('session-telemetry/record', (_record, next) => ({ ...next(), body: null }))
|
|
const session = ctx.sessions.create(SessionId('log'))
|
|
session.append('user/message', createUserMessage({
|
|
content: [{ type: 'text', text: FIXTURE_SECRET }], source: { kind: 'user' },
|
|
}), { surfaceOp: 'append' })
|
|
const logged = session.events[0]!.data as { content: { text: string }[] }
|
|
expect(logged.content[0]!.text).toBe(FIXTURE_SECRET)
|
|
})
|
|
|
|
it('stacks listeners outermost-first around next()', async () => {
|
|
const { ctx, backend } = await setup()
|
|
const order: string[] = []
|
|
ctx.on('session-telemetry/record', (_record, next) => {
|
|
order.push('outer-before')
|
|
const record = next()
|
|
order.push('outer-after')
|
|
return { ...record, attributes: { ...record.attributes, outer: 1 } }
|
|
})
|
|
ctx.on('session-telemetry/record', (_record, next) => {
|
|
order.push('inner')
|
|
const record = next()
|
|
return { ...record, attributes: { ...record.attributes, inner: 1 } }
|
|
})
|
|
const session = ctx.sessions.create(SessionId('stack'))
|
|
session.append('user/message', createUserMessage({
|
|
content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' },
|
|
}), { surfaceOp: 'append' })
|
|
expect(order).toEqual(['outer-before', 'inner', 'outer-after'])
|
|
expect(backend.records[0]!.attributes).toMatchObject({ outer: 1, inner: 1 })
|
|
})
|
|
|
|
it('a listener that skips next() replaces everything beneath it', async () => {
|
|
const { ctx, backend } = await setup()
|
|
const inner = { called: false }
|
|
ctx.on('session-telemetry/record', () => ({ channel: 'ops', time: 0, severity: 'info', attributes: {}, body: 'replaced' } satisfies SessionTelemetryRecord))
|
|
ctx.on('session-telemetry/record', (_record, next) => {
|
|
inner.called = true
|
|
return next()
|
|
})
|
|
const session = ctx.sessions.create(SessionId('veto'))
|
|
session.append('user/message', createUserMessage({
|
|
content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' },
|
|
}), { surfaceOp: 'append' })
|
|
expect(backend.records[0]!.body).toBe('replaced')
|
|
expect(inner.called).toBe(false)
|
|
})
|
|
|
|
it('a throwing rule withholds the record fail-closed without disturbing the log', async () => {
|
|
const { ctx, backend } = await setup()
|
|
ctx.on('session-telemetry/record', () => {
|
|
throw new Error('rule exploded')
|
|
})
|
|
const session = ctx.sessions.create(SessionId('closed'))
|
|
session.append('user/message', createUserMessage({
|
|
content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' },
|
|
}), { surfaceOp: 'append' })
|
|
expect(backend.records).toHaveLength(0)
|
|
expect(session.events).toHaveLength(1)
|
|
})
|
|
})
|