Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ui/src/features/agent/wizardTurnReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ type Translate = (key: string, options?: Record<string, unknown>) => string

/** Conservative presentation policy, not an authorization or execution classifier. */
function allowsExplanation(request: string): boolean {
const text = request.trim().replace(/^[¿¡]+/, '')
// JS `\b` is ASCII-only. Fold accents so "Qué" / "por qué" keep a word boundary.
const text = request.trim().replace(/^[¿¡]+/, '').normalize('NFD').replace(/[\u0300-\u036f]/g, '')
if (/^(?:hola|hello|hi|gracias|thanks)[\s!.]*$/i.test(text)) return true
// An informational prefix does not erase a later imperative in a mixed turn.
if (/(?:[,;.!?\n]|\b(?:and|then|also|y|luego|despu[eé]s))\s*(?:(?:please|por favor)[,\s]+)?(?:create|generate|make|update|delete|remove|add|save|export|start|retry|run|open|select|crea\w*|genera\w*|haz\w*|actualiza\w*|elimina\w*|borra\w*|a[nñ]ade\w*|guarda\w*|exporta\w*|inicia\w*|reintenta\w*|ejecuta\w*|abre|selecciona\w*)\b/i.test(text)) return false
Expand Down
7 changes: 6 additions & 1 deletion ui/tests/wizardTurnReport.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ test('informational conversation is preserved while navigation requires its own
const reply = 'Collections group existing assets.'
assert.equal(formatWizardTurnReply({ reply, actions: [] }, [], t, 'What are collections?'), reply)
assert.equal(formatWizardTurnReply({ reply, actions: [] }, [], t, '¿Cómo funcionan las colecciones?'), reply)
// Accented qué/por qué used to fail JS `\b` and show a false empty-turn receipt.
assert.equal(formatWizardTurnReply({ reply, actions: [] }, [], t, 'Qué son las colecciones?'), reply)
assert.equal(formatWizardTurnReply({ reply, actions: [] }, [], t, '¿Qué son las colecciones?'), reply)
assert.equal(formatWizardTurnReply({ reply, actions: [] }, [], t, 'Por qué no aparecen las colecciones?'), reply)
assert.equal(formatWizardTurnReply({ reply, actions: [] }, [], t, '¿Por qué falló la generación?'), reply)
assert.match(formatWizardTurnReply({ reply, actions: [{ type: 'open_tab', tab: 'workspaces' }] }, [], t), /No action was executed/)
})

Expand All @@ -110,7 +115,7 @@ test('empty and omitted actions never claim creation in response to an action re
for (const request of ['Create Nightwatch using my settings.', 'Can you create a collection?', 'Crea un proyecto Nightwatch.',
'Hola, crea un proyecto Nightwatch.', 'Hi, create Nightwatch.',
'Can you explain collections and create Nightwatch?', 'What are collections? Create one named Nightwatch.',
'Explica las colecciones y crea Nightwatch.', '']) {
'Explica las colecciones y crea Nightwatch.', 'Qué son las colecciones? Crea una llamada Nightwatch.', '']) {
const reply = formatWizardTurnReply(turn, [], t, request)
assert.match(reply, /No action was executed/)
assert.doesNotMatch(reply, /invented-999|Created/)
Expand Down
Loading