'use strict'; const assert = require('assert/strict'); const { APPLIED_ACTION_STATUSES, CONFIRMING_EVIDENCE_TYPES, createJourneyDefinition, createJourneyState, applyJourneyFacts, isStageComplete, planAppliedAction, executeAppliedAction, confirmAppliedAction, serializeJourneyState, buildConversationStateSnapshot, } = require('../mcp/src/core/agent-conversation-journey'); function confirmation(type, id, at) { return { type, id, at, success: true }; } function main() { const definition = createJourneyDefinition({ id: 'generic-consultation', initialStageId: 'understand', stages: [ { id: 'understand', requiredFacts: ['goal'] }, { id: 'qualify', requiredFacts: ['constraints.budget', 'constraints.timeline'] }, { id: 'recommend', requiredFacts: ['selection'] }, { id: 'commit', requiredFacts: ['commitment'] }, ], }); assert.deepEqual(APPLIED_ACTION_STATUSES, ['planned', 'executed', 'confirmed']); assert.deepEqual(CONFIRMING_EVIDENCE_TYPES, ['send', 'tool', 'handoff']); const initial = createJourneyState(definition, { at: '2026-08-06T01:00:00.000Z' }); const observed = applyJourneyFacts(definition, initial, { constraints: { timeline: 'soon', budget: 100 }, goal: 'compare options', }, { at: '2026-08-06T01:01:00.000Z' }); assert.equal(initial.facts.goal, undefined); assert.equal(observed.currentStageId, 'understand'); assert.equal(isStageComplete(definition, observed, 'qualify'), true); const planned = planAppliedAction(definition, observed, { key: 'reply:message-1', kind: 'reply', transition: { mode: 'auto' }, payload: { draftId: 'draft-1' }, at: '2026-08-06T01:02:00.000Z', }); assert.equal(planned.currentStageId, 'understand'); assert.equal(planned.appliedActions[0].status, 'planned'); const executed = executeAppliedAction(definition, planned, { key: 'reply:message-1', evidence: { type: 'send', id: 'attempt-1', success: false }, at: '2026-08-06T01:03:00.000Z', }); assert.equal(executed.currentStageId, 'understand'); assert.equal(executed.appliedActions[0].status, 'executed'); assert.throws(() => confirmAppliedAction(definition, executed, { key: 'reply:message-1', evidence: { type: 'draft', id: 'draft-1', success: true }, }), /Unsupported action evidence type/); assert.throws(() => confirmAppliedAction(definition, executed, { key: 'reply:message-1', evidence: { type: 'send', id: 'message-1', success: false }, }), /successful evidence/); const advanced = confirmAppliedAction(definition, executed, { key: 'reply:message-1', evidence: confirmation('send', 'message-1', '2026-08-06T01:04:00.000Z'), }); assert.equal(advanced.currentStageId, 'recommend'); assert.equal(advanced.appliedActions[0].status, 'confirmed'); assert.deepEqual(advanced.appliedActions[0].transitionResult.skippedStageIds, ['qualify']); assert(advanced.stageHistory.some(entry => entry.kind === 'skip' && entry.stageId === 'qualify')); const backPlanned = planAppliedAction(definition, advanced, { key: 'handoff:requalify', kind: 'human_handoff', transition: { mode: 'back', targetStageId: 'qualify' }, }); const backed = confirmAppliedAction(definition, backPlanned, { key: 'handoff:requalify', evidence: confirmation('handoff', 'handoff-1', '2026-08-06T01:05:00.000Z'), }); assert.equal(backed.currentStageId, 'qualify'); assert.equal(backed.stageHistory.at(-1).kind, 'back'); const reenterPlanned = planAppliedAction(definition, backed, { key: 'tool:refresh-qualification', kind: 'refresh', transition: { mode: 'reenter', targetStageId: 'qualify' }, }); const reentered = confirmAppliedAction(definition, reenterPlanned, { key: 'tool:refresh-qualification', evidence: confirmation('tool', 'tool-run-1', '2026-08-06T01:06:00.000Z'), }); assert.equal(reentered.currentStageId, 'qualify'); assert.equal(reentered.stageHistory.at(-1).kind, 'reenter'); const skipPlanned = planAppliedAction(definition, reentered, { key: 'reply:direct-commit', kind: 'reply', transition: { mode: 'to', targetStageId: 'commit' }, }); const skipped = confirmAppliedAction(definition, skipPlanned, { key: 'reply:direct-commit', evidence: confirmation('send', 'message-2', '2026-08-06T01:07:00.000Z'), }); assert.equal(skipped.currentStageId, 'commit'); assert.deepEqual(skipped.appliedActions.at(-1).transitionResult.skippedStageIds, ['recommend']); const deduped = planAppliedAction(definition, skipped, { key: 'reply:direct-commit', kind: 'reply', transition: { mode: 'to', targetStageId: 'commit' }, }); assert.equal(deduped.appliedActions.length, skipped.appliedActions.length); assert.equal(deduped.sequence, skipped.sequence); assert.throws(() => planAppliedAction(definition, skipped, { key: 'reply:direct-commit', kind: 'different-action', transition: { mode: 'none' }, }), /key conflict/); const serialized = serializeJourneyState(skipped); assert.equal(serialized, serializeJourneyState(JSON.parse(serialized))); const reordered = { ...skipped, facts: { goal: skipped.facts.goal, constraints: { budget: skipped.facts.constraints.budget, timeline: skipped.facts.constraints.timeline, }, }, }; assert.equal(serialized, serializeJourneyState(reordered)); assert(skipped.appliedActions.every(action => APPLIED_ACTION_STATUSES.includes(action.status))); const snapshot = buildConversationStateSnapshot({ inboundContent: '预算 20 万,想预约明天下午到店', messages: [{ role: 'user', content: '想了解企业服务收费' }], profile: { budget: '20万' }, }); assert.equal(snapshot.knownConstraints.budget.source, 'conversation'); assert.equal(snapshot.highIntent.active, true); assert.equal(snapshot.highIntent.kind, 'schedule_intake'); assert(snapshot.knownFields.includes('budget')); assert(snapshot.knownFields.includes('appointmentIntent')); process.stdout.write(`${JSON.stringify({ status: 'ok', checks: 30, finalStage: skipped.currentStageId, actions: skipped.appliedActions.length, historyEntries: skipped.stageHistory.length, snapshotFields: snapshot.knownFields, }, null, 2)}\n`); } try { main(); } catch (error) { process.stderr.write(`${error.stack || error.message}\n`); process.exit(1); }