107 lines
3.4 KiB
JavaScript
107 lines
3.4 KiB
JavaScript
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');
|