From b3483098c1f67b9e71b4ab20fd716f6e15051504 Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 24 Apr 2026 16:45:06 +0800 Subject: [PATCH] chore: add continuity plugin package skeleton --- plugins/continuity/HOOK.md | 20 +++++++ plugins/continuity/README.md | 41 +++++++++++++ plugins/continuity/README.zh-TW.md | 41 +++++++++++++ .../approved-plan-receipt.example.json | 13 ++++ .../examples/openclaw.continuity.example.json | 21 +++++++ plugins/continuity/package.json | 13 ++++ .../continuity/src/adapters/force-recall.mjs | 7 +++ plugins/continuity/src/config/defaults.mjs | 21 +++++++ plugins/continuity/src/config/schema.mjs | 59 +++++++++++++++++++ .../continuity/src/continuity/evaluator.mjs | 7 +++ .../src/continuity/receipt-validator.mjs | 7 +++ plugins/continuity/src/index.mjs | 46 +++++++++++++++ plugins/continuity/test/.gitkeep | 1 + 13 files changed, 297 insertions(+) create mode 100644 plugins/continuity/HOOK.md create mode 100644 plugins/continuity/README.md create mode 100644 plugins/continuity/README.zh-TW.md create mode 100644 plugins/continuity/examples/approved-plan-receipt.example.json create mode 100644 plugins/continuity/examples/openclaw.continuity.example.json create mode 100644 plugins/continuity/package.json create mode 100644 plugins/continuity/src/adapters/force-recall.mjs create mode 100644 plugins/continuity/src/config/defaults.mjs create mode 100644 plugins/continuity/src/config/schema.mjs create mode 100644 plugins/continuity/src/continuity/evaluator.mjs create mode 100644 plugins/continuity/src/continuity/receipt-validator.mjs create mode 100644 plugins/continuity/src/index.mjs create mode 100644 plugins/continuity/test/.gitkeep diff --git a/plugins/continuity/HOOK.md b/plugins/continuity/HOOK.md new file mode 100644 index 0000000..b0646db --- /dev/null +++ b/plugins/continuity/HOOK.md @@ -0,0 +1,20 @@ +# HOOK.md + +This document reserves the hook adapter contract for the continuity plugin MVP. + +## Target adapter + +Primary MVP integration target: + +- `force-recall` + +## Planned responsibilities + +- derive continuity input from hook context +- invoke the plugin evaluator +- return a prompt block / gate result without duplicating continuity rules + +## Current status + +- contract placeholder only +- implementation deferred to later plan tasks diff --git a/plugins/continuity/README.md b/plugins/continuity/README.md new file mode 100644 index 0000000..0dea7e4 --- /dev/null +++ b/plugins/continuity/README.md @@ -0,0 +1,41 @@ +# Continuity Plugin (MVP) + +This package is the skeleton for extracting the current approved-plan continuity hard gate into an installable plugin. + +## MVP status + +- Task 2: package skeleton created +- Task 3: config schema contract scaffolded +- Plugin logic intentionally not implemented yet + +## Layout + +```text +plugins/continuity/ + README.md + README.zh-TW.md + HOOK.md + package.json + examples/ + src/ + test/ +``` + +## Planned public surface + +- `src/config/schema.mjs` +- `src/config/defaults.mjs` +- `src/continuity/evaluator.mjs` +- `src/continuity/receipt-validator.mjs` +- `src/adapters/force-recall.mjs` +- `src/index.mjs` + +## Example config + +See `examples/openclaw.continuity.example.json`. + +## Notes + +- Current terminal states preserved by default: `waiting_user`, `blocked`, `pending_verification` +- Default receipt directory target: `state/approved-plan-continuity` +- `npm test` is reserved for the full plugin test suite defined by the implementation plan diff --git a/plugins/continuity/README.zh-TW.md b/plugins/continuity/README.zh-TW.md new file mode 100644 index 0000000..f9f5891 --- /dev/null +++ b/plugins/continuity/README.zh-TW.md @@ -0,0 +1,41 @@ +# Continuity Plugin(MVP) + +這個套件目前是把既有 approved-plan continuity hard gate 抽離成可安裝 plugin 的骨架。 + +## MVP 狀態 + +- Task 2:已建立 package skeleton +- Task 3:已先放入 config schema contract 骨架 +- 目前刻意不實作 plugin logic + +## 目錄 + +```text +plugins/continuity/ + README.md + README.zh-TW.md + HOOK.md + package.json + examples/ + src/ + test/ +``` + +## 預計公開介面 + +- `src/config/schema.mjs` +- `src/config/defaults.mjs` +- `src/continuity/evaluator.mjs` +- `src/continuity/receipt-validator.mjs` +- `src/adapters/force-recall.mjs` +- `src/index.mjs` + +## 範例設定 + +請參考 `examples/openclaw.continuity.example.json`。 + +## 備註 + +- 預設保留目前 terminal states:`waiting_user`、`blocked`、`pending_verification` +- 預設 receipt 目錄:`state/approved-plan-continuity` +- `npm test` 先保留給後續依計畫補上的完整測試流程 diff --git a/plugins/continuity/examples/approved-plan-receipt.example.json b/plugins/continuity/examples/approved-plan-receipt.example.json new file mode 100644 index 0000000..a69c106 --- /dev/null +++ b/plugins/continuity/examples/approved-plan-receipt.example.json @@ -0,0 +1,13 @@ +{ + "planId": "example-plan", + "currentTask": "task-01", + "nextDerivedAction": { + "kind": "delegate", + "target": "subagent", + "task": "placeholder" + }, + "dispatchedAt": "2026-04-24T16:43:00+08:00", + "dispatchRunId": "example-run", + "childSessionKey": "session-placeholder", + "replyClosureState": "pending_verification" +} diff --git a/plugins/continuity/examples/openclaw.continuity.example.json b/plugins/continuity/examples/openclaw.continuity.example.json new file mode 100644 index 0000000..4ab8db8 --- /dev/null +++ b/plugins/continuity/examples/openclaw.continuity.example.json @@ -0,0 +1,21 @@ +{ + "enabled": true, + "planMatchers": [ + "approved-plan" + ], + "legalTerminalStates": [ + "waiting_user", + "blocked", + "pending_verification" + ], + "receiptDir": "state/approved-plan-continuity", + "requireRealDispatchReceipt": true, + "allowReplyClosureWithoutDispatch": false, + "debug": false, + "adapter": { + "forceRecall": { + "enabled": true, + "injectBlockLabel": "APPROVED_PLAN_CONTINUITY_GATE" + } + } +} diff --git a/plugins/continuity/package.json b/plugins/continuity/package.json new file mode 100644 index 0000000..e9a0e01 --- /dev/null +++ b/plugins/continuity/package.json @@ -0,0 +1,13 @@ +{ + "name": "@openclaw/plugin-continuity", + "version": "0.1.0", + "private": true, + "type": "module", + "description": "Continuity plugin MVP skeleton for approved-plan dispatch gating.", + "exports": { + ".": "./src/index.mjs" + }, + "scripts": { + "test": "node test/continuity.config.test.mjs && node test/continuity.receipt-validator.test.mjs && node test/continuity.receipt-store.test.mjs && node test/continuity.evaluator.test.mjs && node test/continuity.plugin.test.mjs && node test/continuity.smoke.test.mjs" + } +} diff --git a/plugins/continuity/src/adapters/force-recall.mjs b/plugins/continuity/src/adapters/force-recall.mjs new file mode 100644 index 0000000..6c5d8c7 --- /dev/null +++ b/plugins/continuity/src/adapters/force-recall.mjs @@ -0,0 +1,7 @@ +export function createForceRecallContinuityAdapter() { + throw new Error('Not implemented: force-recall continuity adapter contract placeholder'); +} + +export function runForceRecallContinuityAdapter() { + throw new Error('Not implemented: force-recall continuity adapter contract placeholder'); +} diff --git a/plugins/continuity/src/config/defaults.mjs b/plugins/continuity/src/config/defaults.mjs new file mode 100644 index 0000000..0ed8484 --- /dev/null +++ b/plugins/continuity/src/config/defaults.mjs @@ -0,0 +1,21 @@ +export const defaultConfig = Object.freeze({ + enabled: true, + planMatchers: ['approved-plan'], + legalTerminalStates: ['waiting_user', 'blocked', 'pending_verification'], + receiptDir: 'state/approved-plan-continuity', + requireRealDispatchReceipt: true, + allowReplyClosureWithoutDispatch: false, + debug: false, + adapter: { + forceRecall: { + enabled: true, + injectBlockLabel: 'APPROVED_PLAN_CONTINUITY_GATE', + }, + }, +}); + +export function cloneDefaultConfig() { + return structuredClone(defaultConfig); +} + +export default defaultConfig; diff --git a/plugins/continuity/src/config/schema.mjs b/plugins/continuity/src/config/schema.mjs new file mode 100644 index 0000000..db5402c --- /dev/null +++ b/plugins/continuity/src/config/schema.mjs @@ -0,0 +1,59 @@ +import defaultConfig, { cloneDefaultConfig } from './defaults.mjs'; + +export const continuityConfigSchema = Object.freeze({ + enabled: 'boolean', + planMatchers: 'string[]', + legalTerminalStates: 'string[]', + receiptDir: 'string', + requireRealDispatchReceipt: 'boolean', + allowReplyClosureWithoutDispatch: 'boolean', + debug: 'boolean', + adapter: { + forceRecall: { + enabled: 'boolean', + injectBlockLabel: 'string', + }, + }, +}); + +function isPlainObject(value) { + return Boolean(value) && typeof value === 'object' && !Array.isArray(value); +} + +export function normalizeContinuityConfig(input = {}) { + const base = cloneDefaultConfig(); + + if (!isPlainObject(input)) { + return base; + } + + return { + ...base, + ...input, + planMatchers: Array.isArray(input.planMatchers) ? [...input.planMatchers] : [...base.planMatchers], + legalTerminalStates: Array.isArray(input.legalTerminalStates) + ? [...input.legalTerminalStates] + : [...base.legalTerminalStates], + adapter: { + ...base.adapter, + ...(isPlainObject(input.adapter) ? input.adapter : {}), + forceRecall: { + ...base.adapter.forceRecall, + ...(isPlainObject(input.adapter?.forceRecall) ? input.adapter.forceRecall : {}), + }, + }, + }; +} + +export function validateContinuityConfig(input = {}) { + const normalizedConfig = normalizeContinuityConfig(input); + return { + ok: true, + errors: [], + normalizedConfig, + }; +} + +export { defaultConfig }; + +export default continuityConfigSchema; diff --git a/plugins/continuity/src/continuity/evaluator.mjs b/plugins/continuity/src/continuity/evaluator.mjs new file mode 100644 index 0000000..00ba3bb --- /dev/null +++ b/plugins/continuity/src/continuity/evaluator.mjs @@ -0,0 +1,7 @@ +export function evaluateContinuity() { + throw new Error('Not implemented: continuity evaluator contract placeholder'); +} + +export function buildContinuityGateBlock() { + throw new Error('Not implemented: continuity gate block contract placeholder'); +} diff --git a/plugins/continuity/src/continuity/receipt-validator.mjs b/plugins/continuity/src/continuity/receipt-validator.mjs new file mode 100644 index 0000000..ed77817 --- /dev/null +++ b/plugins/continuity/src/continuity/receipt-validator.mjs @@ -0,0 +1,7 @@ +export function validateReceipt() { + throw new Error('Not implemented: receipt validator contract placeholder'); +} + +export function isValidReceipt() { + throw new Error('Not implemented: receipt validator contract placeholder'); +} diff --git a/plugins/continuity/src/index.mjs b/plugins/continuity/src/index.mjs new file mode 100644 index 0000000..84636ab --- /dev/null +++ b/plugins/continuity/src/index.mjs @@ -0,0 +1,46 @@ +import { defaultConfig, cloneDefaultConfig } from './config/defaults.mjs'; +import { + continuityConfigSchema, + validateContinuityConfig, + normalizeContinuityConfig, +} from './config/schema.mjs'; +import { + evaluateContinuity, + buildContinuityGateBlock, +} from './continuity/evaluator.mjs'; +import { + validateReceipt, + isValidReceipt, +} from './continuity/receipt-validator.mjs'; +import { + createForceRecallContinuityAdapter, + runForceRecallContinuityAdapter, +} from './adapters/force-recall.mjs'; + +export { + defaultConfig, + cloneDefaultConfig, + continuityConfigSchema, + validateContinuityConfig, + normalizeContinuityConfig, + evaluateContinuity, + buildContinuityGateBlock, + validateReceipt, + isValidReceipt, + createForceRecallContinuityAdapter, + runForceRecallContinuityAdapter, +}; + +export default { + name: '@openclaw/plugin-continuity', + defaultConfig, + continuityConfigSchema, + validateContinuityConfig, + normalizeContinuityConfig, + evaluateContinuity, + buildContinuityGateBlock, + validateReceipt, + isValidReceipt, + createForceRecallContinuityAdapter, + runForceRecallContinuityAdapter, +}; diff --git a/plugins/continuity/test/.gitkeep b/plugins/continuity/test/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/plugins/continuity/test/.gitkeep @@ -0,0 +1 @@ +