import assert from 'node:assert/strict'; import plugin, { createForceRecallContinuityAdapter, createGenericPreflightContinuityAdapter, defaultConfig, evaluateContinuity, runManualContinuityPreflight, } from '../src/index.mjs'; function test(name, fn) { try { fn(); console.log(`ok - ${name}`); } catch (error) { console.error(`not ok - ${name}`); throw error; } } test('index exports plugin surface', () => { assert.equal(plugin.name, '@openclaw/plugin-continuity'); assert.equal(typeof evaluateContinuity, 'function'); assert.equal(defaultConfig.adapter.forceRecall.enabled, true); assert.equal(defaultConfig.adapter.genericPreflight.enabled, true); assert.equal(typeof plugin.runGenericPreflightContinuityAdapter, 'function'); }); test('adapter preserves current hook parity for plain wrapper next-action mapping', () => { const adapter = createForceRecallContinuityAdapter(defaultConfig); const out = adapter.evaluate({ wrapperResult: { classification: 'long_task', planId: 'plan-1', currentTask: 'task-7', taskState: 'complete', nextDerivedAction: { type: 'message_subagent', task: 'continue' }, replyClosureState: 'completed', dispatchReceipt: null, }, }); assert.equal(out.result.ok, true); assert.equal(out.meta.adapterName, 'force-recall'); assert.equal(out.meta.hostAgnostic, true); assert.match(out.block, /status=pass/); }); test('adapter fails when planner-derived auto-next boundary exists without dispatch receipt', () => { const adapter = createForceRecallContinuityAdapter(defaultConfig); const out = adapter.evaluate({ wrapperResult: { classification: 'long_task', planId: 'plan-2', currentTask: 'task-8', replyClosureState: 'completed', dispatchReceipt: null, }, autoChainPlanResult: { derivedAction: 'continue_task_9', dispatchMode: 'message_subagent', }, }); assert.equal(out.result.ok, false); assert.equal(out.result.reason, 'missing_auto_next_dispatch'); assert.match(out.block, /continuity_failure/); }); test('generic preflight adapter evaluates host-agnostic source payload', () => { const adapter = createGenericPreflightContinuityAdapter(defaultConfig); const out = adapter.evaluate({ planId: 'plan-generic', currentTask: 'task-generic', taskState: 'complete', nextTaskKnown: true, sameApprovedPlan: true, taskBoundaryStop: true, nextTaskId: 'task-next', nextDerivedAction: { type: 'message_subagent', task: 'continue' }, replyClosureState: 'completed', dispatchReceipt: null, }); assert.equal(out.result.ok, false); assert.equal(out.result.reason, 'missing_auto_next_dispatch'); assert.equal(out.meta.adapterName, 'generic-preflight'); assert.equal(out.meta.hostAgnostic, true); assert.equal(out.input.planId, 'plan-generic'); }); test('manual continuity preflight runner works without force-recall hook', () => { const out = runManualContinuityPreflight({ config: defaultConfig, planId: 'plan-manual', currentTask: 'task-manual', taskState: 'complete', nextDerivedAction: { type: 'message_subagent', task: 'continue' }, replyClosureState: 'waiting_user', }); assert.equal(out.result.ok, true); assert.match(out.block, /APPROVED_PLAN_CONTINUITY_GATE/); assert.equal(out.meta.adapterName, 'generic-preflight'); }); console.log('continuity.plugin.test.mjs PASS');