| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- import { describe, expect, it } from 'vitest'
- import { redactSessionSnapshotIds } from '../src/identity.ts'
- const parentId = '11111111-1111-4111-8111-111111111111'
- const childId = '22222222-2222-4222-8222-222222222222'
- const messageId = '33333333-3333-4333-8333-333333333333'
- const approvalId = '44444444-4444-4444-8444-444444444444'
- const runId = '55555555-5555-4555-8555-555555555555'
- const otherId = '66666666-6666-4666-8666-666666666666'
- const proseUuid = '77777777-7777-4777-8777-777777777777'
- describe('session snapshot identity redaction', () => {
- it('preserves feedback versions and target relationships without redacting unrelated prose', () => {
- const source = [
- { type: 'session', id: parentId },
- { type: 'assistant/message', data: { message: { id: messageId, role: 'assistant', content: [], source: {} } } },
- { type: 'feedback/message-put', data: { sessionId: parentId, item: { messageId, version: approvalId, note: proseUuid } } },
- { type: 'feedback/message-put', data: { sessionId: parentId, item: { messageId, version: runId } } },
- { type: 'feedback/message-delete', data: { sessionId: parentId, messageId } },
- { type: 'example', data: { version: proseUuid } },
- ].map(record => JSON.stringify(record)).join('\n')
- const [output] = redactSessionSnapshotIds([source])
- expect(output).toContain('"version":"{{id:1}}"')
- expect(output).toContain('"version":"{{id:2}}"')
- expect(output?.match(/"messageId":"{{message:1}}"/g)).toHaveLength(3)
- expect(output).toContain(proseUuid)
- expect(redactSessionSnapshotIds([output!])).toEqual([output])
- })
- it('preserves typed relationships across parent and child logs', () => {
- const parent = [
- JSON.stringify({ type: 'session', id: parentId, createdAt: 1, cwd: '/tmp/work' }),
- JSON.stringify({
- type: 'agent/inbox/spliced',
- data: {
- inserted: [{
- role: 'user',
- content: [{ type: 'text', text: `keep unrelated ${proseUuid}; session ${childId}` }],
- source: { kind: 'user' },
- id: messageId,
- }],
- },
- }),
- JSON.stringify({ type: 'approval/asked', data: { id: approvalId } }),
- JSON.stringify({ type: 'tool-workflow/run-start', data: { runId } }),
- JSON.stringify({ type: 'example', data: { requestId: otherId, echoed: otherId } }),
- '',
- ].join('\n')
- const child = [
- JSON.stringify({ type: 'session', id: childId, parentSession: parentId, createdAt: 2, cwd: '/tmp/work' }),
- JSON.stringify({
- type: 'user/message',
- data: {
- role: 'user', content: [], source: { kind: 'user' }, id: messageId,
- },
- }),
- '',
- ].join('\n')
- const redacted = redactSessionSnapshotIds([parent, child])
- expect(redacted[0]).toContain('"id":"{{session:1}}"')
- expect(redacted[1]).toContain('"id":"{{session:2}}"')
- expect(redacted[1]).toContain('"parentSession":"{{session:1}}"')
- expect(redacted.join('\n').match(/\{\{message:1\}\}/g)).toHaveLength(2)
- expect(redacted[0]).toContain('"id":"{{approval:1}}"')
- expect(redacted[0]).toContain('"runId":"{{workflow:1}}"')
- expect(redacted[0]).toContain('"requestId":"{{id:1}}"')
- expect(redacted[0]).toContain('"echoed":"{{id:1}}"')
- expect(redacted[0]).toContain(proseUuid)
- expect(redacted[0]).toContain('session {{session:2}}')
- expect(redactSessionSnapshotIds(redacted)).toEqual(redacted)
- })
- it('classifies semantic text plus command, RPC, and retry identity fields', () => {
- const semanticMessage = '88888888-8888-4888-8888-888888888888'
- const anonymousUser = '99999999-9999-4999-8999-999999999999'
- const retryId = 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa'
- const source = [
- JSON.stringify({ type: 'not-a-session', data: { value: 'plain' } }),
- JSON.stringify({
- type: 'example',
- data: {
- commandId: 'command-7',
- rpcId: 'rpc-9',
- retryId,
- requestId: 'stable-readable-id',
- text: `Retain this as message ${semanticMessage}. Anonymous user: ${anonymousUser}`,
- },
- }),
- ].join('\n')
- const [redacted] = redactSessionSnapshotIds([source])
- expect(redacted).toContain('"commandId":"{{command:1}}"')
- expect(redacted).toContain('"rpcId":"{{rpc:1}}"')
- expect(redacted).toContain('"retryId":"{{retry:1}}"')
- expect(redacted).toContain('as message {{message:1}}')
- expect(redacted).toContain('Anonymous user: {{id:1}}')
- expect(redacted).toContain('"requestId":"stable-readable-id"')
- expect(redacted?.endsWith('\n')).toBe(false)
- })
- it('keeps a canonical token first seen through a generic id key', () => {
- const canonical = '{{message:7}}'
- const nextMessage = 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb'
- const source = [
- JSON.stringify({ type: 'example', data: { requestId: canonical } }),
- JSON.stringify({
- type: 'user/message',
- data: { role: 'user', content: [], source: { kind: 'user' }, id: canonical },
- }),
- JSON.stringify({
- type: 'user/message',
- data: { role: 'user', content: [], source: { kind: 'user' }, id: nextMessage },
- }),
- '',
- ].join('\n')
- const [redacted] = redactSessionSnapshotIds([source])
- expect(redacted?.match(/\{\{message:7\}\}/g)).toHaveLength(2)
- expect(redacted).toContain('"id":"{{message:8}}"')
- expect(redacted).not.toContain('{{id:')
- })
- })
|