test(ai-vault): add first-user-prompt UTF-16 safety tests

Ensure truncation at safety limits doesn't split UTF-16 surrogate pairs,
preventing corruption of astral characters in captured prompts.
This commit is contained in:
Jinjing
2026-08-01 15:43:03 -07:00
parent af184e3a40
commit 7515b8ef86
2 changed files with 49 additions and 0 deletions
@@ -0,0 +1,39 @@
import { describe, expect, it } from 'vitest'
import { normalizeFullFirstUserPromptText } from './session-scanner-first-user-prompt'
// Mirrors FULL_FIRST_USER_PROMPT_SAFETY_LIMIT in the module under test.
const SAFETY_LIMIT = 256 * 1024
describe('AI Vault full first-user-prompt normalization', () => {
it('drops an astral char straddling the safety limit instead of splitting it', () => {
const result = normalizeFullFirstUserPromptText(`${'a'.repeat(SAFETY_LIMIT - 1)}😀tail`)
expect(result).toHaveLength(SAFETY_LIMIT - 1)
expect(result?.endsWith('a')).toBe(true)
expect(hasUnpairedSurrogate(result ?? '')).toBe(false)
})
it('keeps a prompt shorter than the safety limit intact', () => {
expect(normalizeFullFirstUserPromptText('ship it 😀')).toBe('ship it 😀')
})
})
function hasUnpairedSurrogate(value: string): boolean {
for (let index = 0; index < value.length; index += 1) {
const code = value.charCodeAt(index)
const isHigh = code >= 0xd800 && code <= 0xdbff
const isLow = code >= 0xdc00 && code <= 0xdfff
if (isHigh) {
const next = value.charCodeAt(index + 1)
if (!(next >= 0xdc00 && next <= 0xdfff)) {
return true
}
index += 1
continue
}
if (isLow) {
return true
}
}
return false
}
@@ -41,6 +41,16 @@ describe('AI Vault Grok session parser', () => {
expect(result?.endsWith('...')).toBe(true)
})
it('drops an astral char straddling the preview scan cap instead of splitting it', () => {
// Hidden context is skipped by the fold, so the 4096-code-unit scan cap lands
// mid-emoji while the visible text stays well under the 220-char preview cap.
const hidden = `<system-reminder>${'x'.repeat(4057)}</system-reminder>`
const result = extractGrokContentText(`${hidden}ask😀tail`)
expect(hidden).toHaveLength(4092)
expect(result).toBe('ask')
})
it('stores the unwrapped user_query as firstUserPrompt under full capture', async () => {
const root = await mkdtemp(join(tmpdir(), 'orca-ai-vault-grok-first-'))
tempRoots.push(root)