'use strict'; const assert = require('assert/strict'); const { FUTURE_PROMISE_REASON_CODES: REASONS, extractActionClaims, normalizeActionEvidence, verifyFuturePromises, } = require('../mcp/src/core/response-future-promise-verifier'); let assertions = 0; function check(condition, message) { assertions += 1; assert(condition, message); } function equal(actual, expected, message) { assertions += 1; assert.equal(actual, expected, message); } function hasReason(result, reason) { check(result.reasonCodes.includes(reason), `${reason} missing: ${result.reasonCodes.join(',')}`); } function successfulTool(tool, result, extra = {}) { return { tool, status: 'completed', result, ...extra }; } const noEvidence = verifyFuturePromises({ reply: '我稍后核实库存,再通知您。' }); equal(noEvidence.passed, false, 'future promises without evidence must fail'); equal(noEvidence.failedClaimIds.length, 2, 'verify and notify should be independent claims'); hasReason(noEvidence, REASONS.FUTURE_ACTION_MISSING_EVIDENCE); const matchingEvidence = verifyFuturePromises({ reply: '库存已经核实,结果已通知您。', toolTrace: [successfulTool('inventory_verify', { ok: true, subject: '库存', available: true })], outboundEvidence: { status: 'delivered', subject: '库存', result: { delivered: true } }, }); equal(matchingEvidence.passed, true, 'matching successful evidence should pass'); equal(matchingEvidence.claims.length, 2); check(matchingEvidence.claims.every(claim => claim.evidenceMatched), 'all matching claims should carry evidence'); check(matchingEvidence.claims.every(claim => claim.evidenceRefs.length === 1), 'evidence refs should be stable and compact'); const unrelatedTool = verifyFuturePromises({ reply: '库存已经核实。', toolTrace: [successfulTool('knowledge_search', { ok: true, subject: '品牌介绍' })], }); equal(unrelatedTool.passed, false, 'unrelated tool result must not support stock verification'); hasReason(unrelatedTool, REASONS.ACTION_EVIDENCE_UNRELATED); const citationBypass = verifyFuturePromises({ reply: '我会稍后核实价格。', citations: [{ content: '价格说明' }], }); equal(citationBypass.passed, false, 'citations are not execution evidence'); hasReason(citationBypass, REASONS.FUTURE_ACTION_MISSING_EVIDENCE); const scopedUncertainty = verifyFuturePromises({ reply: '价格已经确认,其他信息待核验。' }); equal(scopedUncertainty.passed, false, 'uncertainty in another clause must not protect a performed claim'); hasReason(scopedUncertainty, REASONS.PERFORMED_ACTION_MISSING_EVIDENCE); const conditionalOffer = verifyFuturePromises({ reply: '可以为您核验,是否需要?' }); equal(conditionalOffer.passed, true, 'conditional proposal is safe'); equal(conditionalOffer.claims[0].requiresEvidence, false); hasReason(conditionalOffer, REASONS.SAFE_CONDITIONAL_OFFER); const missingInput = verifyFuturePromises({ reply: '请提供订单号,我再为您核实。' }); equal(missingInput.passed, true, 'requesting missing input before continuing is safe'); equal(missingInput.claims[0].requiresEvidence, false); hasReason(missingInput, REASONS.SAFE_MISSING_INPUT_REQUEST); const handoffMissing = verifyFuturePromises({ reply: '已经转交人工专员。' }); equal(handoffMissing.passed, false, 'performed handoff requires evidence'); equal(handoffMissing.claims[0].action, 'handoff'); hasReason(handoffMissing, REASONS.PERFORMED_ACTION_MISSING_EVIDENCE); const handoffPassed = verifyFuturePromises({ reply: '已经转交人工专员。', handoffEvidence: { status: 'completed', handoffId: 'handoff-test' }, }); equal(handoffPassed.passed, true, 'completed handoff evidence should pass'); hasReason(handoffPassed, REASONS.PERFORMED_ACTION_VERIFIED); const outboundNotHandoff = verifyFuturePromises({ reply: '已经转交人工专员。', outboundEvidence: { status: 'delivered', result: { delivered: true } }, }); equal(outboundNotHandoff.passed, false, 'outbound delivery cannot prove a handoff'); hasReason(outboundNotHandoff, REASONS.ACTION_EVIDENCE_UNRELATED); const processingMissing = verifyFuturePromises({ reply: '正在处理您的退款。' }); equal(processingMissing.passed, false, 'in-progress processing requires evidence'); hasReason(processingMissing, REASONS.IN_PROGRESS_ACTION_MISSING_EVIDENCE); const activeCrossCheckMissing = verifyFuturePromises({ reply: '我现在核对执行条件,确认后回复您。' }); equal(activeCrossCheckMissing.passed, false, 'first-person active cross-check requires evidence'); hasReason(activeCrossCheckMissing, REASONS.IN_PROGRESS_ACTION_MISSING_EVIDENCE); const processingPassed = verifyFuturePromises({ reply: '正在处理您的退款。', toolTrace: [successfulTool('process_refund', { ok: true, subject: '退款' })], }); equal(processingPassed.passed, true, 'successful matching process evidence should pass'); hasReason(processingPassed, REASONS.IN_PROGRESS_ACTION_VERIFIED); const failedEvidence = verifyFuturePromises({ reply: '价格已经核实。', toolTrace: [{ tool: 'price_verify', status: 'failed', result: { ok: false, subject: '价格' } }], }); equal(failedEvidence.passed, false, 'failed matching tool must not support a claim'); hasReason(failedEvidence, REASONS.ACTION_EVIDENCE_FAILED); const callbackMissing = verifyFuturePromises({ reply: '我会尽快给您回电。' }); equal(callbackMissing.passed, false, 'callback promise requires successful call evidence'); equal(callbackMissing.claims[0].action, 'callback'); const callbackPassed = verifyFuturePromises({ reply: '已经给您回电。', toolTrace: [successfulTool('call_customer', { success: true, subject: '电话' })], }); equal(callbackPassed.passed, true, 'call evidence should prove callback'); const submitPassed = verifyFuturePromises({ reply: '退款申请已经提交。', toolTrace: [successfulTool('submit_refund_request', { ok: true, subject: '退款申请', requestId: 'fixture' })], }); equal(submitPassed.passed, true, 'matching submit evidence should prove submission'); equal(submitPassed.claims.length, 2, 'refund and submit actions are independently evidenced by one matching tool'); const cancelPassed = verifyFuturePromises({ reply: '订单已经取消。', toolTrace: [successfulTool('cancel_order', { ok: true, subject: '订单' })], }); equal(cancelPassed.passed, true, 'matching cancel evidence should pass'); const safeNecessity = verifyFuturePromises({ reply: '当前价格需要核验。' }); equal(safeNecessity.passed, true, 'honest necessity is not an execution claim'); equal(safeNecessity.claims.length, 0); hasReason(safeNecessity, REASONS.NO_ACTION_CLAIMS); const noClaims = verifyFuturePromises({ reply: '当前资料不足,请提供订单号。' }); equal(noClaims.passed, true); equal(noClaims.checks.length, 3); check(noClaims.checks.every(item => ( typeof item.id === 'string' && typeof item.passed === 'boolean' && typeof item.weight === 'number' && typeof item.hard === 'boolean' && typeof item.detail === 'string' )), 'checks must keep the stable quality-check shape'); const extracted = extractActionClaims('我稍后核实价格,再通知您。'); equal(extracted.length, 2); check(extracted.every((claim, index) => claim.id === `claim_${index + 1}`), 'claim ids must be deterministic'); const normalized = normalizeActionEvidence({ handoffEvidence: { status: 'completed' }, outboundEvidence: { delivered: true }, }); equal(normalized.length, 2); check(normalized.every(item => item.success), 'explicit success is required and preserved'); check(normalized.some(item => item.actionIds.includes('handoff'))); check(normalized.some(item => item.actionIds.includes('notify'))); console.log(JSON.stringify({ ok: true, assertions, cases: 21, checks: ['future_promise_evidence', 'performed_action_evidence', 'conditional_proposal_safety'], reasonCodes: Object.values(REASONS), }, null, 2));