import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' import { fileURLToPath } from 'node:url' import { buildWorkbenchDocumentCommandFollowupGuidance, buildWorkbenchDraftDeletionGuidance, isWorkbenchDraftDeletionIntent, resolveLatestWorkbenchDocumentCommandContext, resolveLatestWorkbenchDraftPayload } from '../src/composables/workbenchAiMode/workbenchAiCommandIntentModel.js' const personalWorkbenchAiModeScript = readFileSync( fileURLToPath(new URL('../src/composables/workbenchAiMode/usePersonalWorkbenchAiMode.js', import.meta.url)), 'utf8' ) const commandIntentsScript = readFileSync( fileURLToPath(new URL('../src/composables/workbenchAiMode/useWorkbenchAiCommandIntents.js', import.meta.url)), 'utf8' ) test('workbench command intent detects draft deletion phrases without broad delete matching', () => { assert.equal(isWorkbenchDraftDeletionIntent('删除草稿'), true) assert.equal(isWorkbenchDraftDeletionIntent('把刚才保存的草稿删掉'), true) assert.equal(isWorkbenchDraftDeletionIntent('删除这个申请单'), true) assert.equal(isWorkbenchDraftDeletionIntent('删除附件'), false) assert.equal(isWorkbenchDraftDeletionIntent('草稿还要补什么'), false) }) test('workbench command intent resolves latest draft payload from conversation context', () => { const payload = resolveLatestWorkbenchDraftPayload([ { role: 'assistant', draftPayload: { claim_id: 'old-draft', claim_no: 'AOLD001', status: 'draft' } }, { role: 'assistant', suggestedActions: [{ action_type: 'open_application_detail', payload: { claim_id: 'latest-draft', claim_no: 'ALATEST1', status: 'draft' } }] } ]) assert.deepEqual(payload, { claimId: 'latest-draft', claimNo: 'ALATEST1', status: 'draft', documentType: 'application' }) }) test('workbench command intent skips submitted documents when deleting draft', () => { const payload = resolveLatestWorkbenchDraftPayload([ { role: 'assistant', draftPayload: { claim_id: 'submitted-application', claim_no: 'ASUBMIT1', status: 'submitted' } } ]) assert.equal(payload, null) }) test('workbench draft deletion guidance opens detail instead of deleting directly', () => { const guidance = buildWorkbenchDraftDeletionGuidance({ claimId: 'latest-draft', claimNo: 'ALATEST1', documentType: 'application' }) assert.match(guidance.content, /已识别到您想删除草稿/) assert.match(guidance.content, /不会直接替您删除/) assert.equal(guidance.suggestedActions.length, 1) assert.equal(guidance.suggestedActions[0].action_type, 'open_application_detail') assert.equal(guidance.suggestedActions[0].payload.claim_id, 'latest-draft') assert.equal(guidance.suggestedActions[0].payload.claim_no, 'ALATEST1') }) test('workbench command intent reuses previous approval candidates for follow-up approval command', () => { const context = resolveLatestWorkbenchDocumentCommandContext([ { role: 'assistant', content: [ '### 已查询到相关单据', '', '
', '查看详情', '
', '
', '查看详情', '
' ].join('\n') } ], { action: 'approve', safetyLevel: 'confirm_required' }) assert.equal(context?.candidates.length, 2) assert.deepEqual(context.candidates[0], { claimId: 'approval-1', claimNo: 'AP-APPROVAL-001', documentType: 'application', actionLabel: '查看详情' }) const guidance = buildWorkbenchDocumentCommandFollowupGuidance(context, { action: 'approve' }) assert.match(guidance.content, /已接上刚才查询到的待审单据/) assert.match(guidance.content, /AP-APPROVAL-001/) assert.match(guidance.content, /RE-APPROVAL-002/) assert.equal(guidance.suggestedActions.length, 2) assert.equal(guidance.suggestedActions[0].action_type, 'open_application_detail') assert.equal(guidance.suggestedActions[0].payload.claim_id, 'approval-1') assert.equal(guidance.suggestedActions[0].payload.command_action, 'approve') }) test('workbench draft deletion intent is wired before draft slot continuation', () => { assert.match(commandIntentsScript, /isWorkbenchDraftDeletionIntent/) assert.match(commandIntentsScript, /resolveLatestWorkbenchDocumentCommandContext/) assert.match(commandIntentsScript, /buildWorkbenchDocumentCommandFollowupGuidance/) assert.match(commandIntentsScript, /function handleInlineDraftDeletionIntent\(cleanPrompt, entry = \{\}\)/) assert.match(commandIntentsScript, /resolveLatestWorkbenchDraftPayload\(conversationMessages\.value\)/) assert.match(commandIntentsScript, /buildWorkbenchDraftDeletionGuidance\(draftPayload\)/) assert.match(personalWorkbenchAiModeScript, /useWorkbenchAiCommandIntents/) const startIndex = personalWorkbenchAiModeScript.indexOf('function startInlineConversation') const startBlock = personalWorkbenchAiModeScript.slice(startIndex) const deleteIntentIndex = startBlock.indexOf('if (commandIntents.handleInlineDraftDeletionIntent(cleanPrompt, entry))') const draftContinuationIndex = startBlock.indexOf('if (aiExpenseDraft.value && !isAiExpenseDraftComplete(aiExpenseDraft.value))') assert.ok(deleteIntentIndex >= 0) assert.ok(draftContinuationIndex > deleteIntentIndex) })