generalize continuity plugin engine and generic adapter

This commit is contained in:
Eve
2026-04-24 19:28:20 +08:00
parent ed4fe3ea6c
commit 1fe6474009
13 changed files with 679 additions and 153 deletions

View File

@@ -1,10 +1,14 @@
# Continuity PluginMVP
# Continuity PluginMVP → generalized checkpoint
> English version: `README.md`
這個套件把目前 approved-plan continuity hard gate 抽離成一個可安裝、可測試、可在 hook 內重用的 OpenClaw plugin MVP
這個套件把目前 approved-plan continuity hard gate 抽離成一個可安裝、可測試、可在 hook 內重用的 OpenClaw plugin。
目標不是重新發明規則,而是把既有 continuity 判斷、receipt contract、force-recall adapter 收斂成一個可以被其他 OpenClaw workspace 直接帶走的最小可用包。
它仍保留既有 approved-plan 行為,但現在往比較通用的 **engine + adapter** 結構前進了一步:
- 有 host-agnostic 的 continuity engine input/output contract
- 保留既有 `force-recall` parity adapter
- 新增 `generic-preflight` adapter 與 manual runner讓未使用 `force-recall` 的 workspace 也能接
## 目前能做什麼
@@ -14,6 +18,8 @@
- 評估 approved-plan continuity gate
- 產生可注入 prompt 的 continuity gate block
- 透過 `force-recall` adapter把 hook 端的 wrapper/planner 結果轉成 continuity input
- 透過 `generic-preflight` adapter直接吃 host-agnostic continuity input
- 透過 manual preflight runner在沒有 `force-recall` 的情況下也能直接呼叫
## 安裝位置
@@ -23,7 +29,7 @@
<workspace>/plugins/continuity
```
以目前 MVP 慣例,相關檔案位置如下:
現在可支援兩類整合路徑:原本的 `force-recall` 路徑,以及較通用的 generic path。
```text
<workspace>/
@@ -59,10 +65,12 @@ plugins/continuity/
index.mjs
adapters/
force-recall.mjs
generic-preflight.mjs
config/
defaults.mjs
schema.mjs
continuity/
engine.mjs
evaluator.mjs
receipt-store.mjs
receipt-validator.mjs
@@ -80,10 +88,12 @@ plugins/continuity/
- `src/config/schema.mjs`
- `src/config/defaults.mjs`
- `src/continuity/engine.mjs`
- `src/continuity/evaluator.mjs`
- `src/continuity/receipt-validator.mjs`
- `src/continuity/receipt-store.mjs`
- `src/adapters/force-recall.mjs`
- `src/adapters/generic-preflight.mjs`
- `src/index.mjs`
`src/index.mjs` 目前會 re-export
@@ -91,11 +101,42 @@ plugins/continuity/
- `defaultConfig`
- `cloneDefaultConfig()`
- `validateContinuityConfig()` / `normalizeContinuityConfig()`
- `normalizeContinuityEngineInput()`
- `createContinuityEngineResult()` / `createContinuityEngineContract()`
- `evaluateContinuity()` / `buildContinuityGateBlock()`
- `validateReceipt()` / `isValidReceipt()`
- `slugifyReceiptSegment()` / `buildReceiptFilename()` / `writeReceipt()`
- `buildApprovedPlanContinuityInput()`
- `createForceRecallContinuityAdapter()` / `runForceRecallContinuityAdapter()`
- `buildGenericContinuityInput()`
- `createGenericPreflightContinuityAdapter()` / `runGenericPreflightContinuityAdapter()`
- `runManualContinuityPreflight()`
## Host-agnostic engine contract
generalized engine 會吃一個正規化後的 continuity input常用欄位包括
- `planId`
- `currentTask`
- `taskState`
- `nextDerivedAction`
- `replyClosureState`
- `dispatchReceipt`
- `nextTaskKnown`
- `sameApprovedPlan`
- `taskBoundaryStop`
- `highRiskStop`
generalized adapter 會回傳共同 contract
- `input`
- `result`
- `evaluation`
- `block`
- `meta.adapterName`
- `meta.hostAgnostic`
精簡契約請見 `src/continuity/types.md`
## Example config
@@ -118,6 +159,10 @@ plugins/continuity/
"forceRecall": {
"enabled": true,
"injectBlockLabel": "APPROVED_PLAN_CONTINUITY_GATE"
},
"genericPreflight": {
"enabled": true,
"injectBlockLabel": "APPROVED_PLAN_CONTINUITY_GATE"
}
}
}
@@ -125,23 +170,9 @@ plugins/continuity/
預設值定義在 `src/config/defaults.mjs`
## Hook 接法
## 整合路徑 A`force-recall`
MVP 的主要整合點是 `hooks/force-recall/handler.ts`
目前 hook 端做法是:
1. 先完成 long-task preflight / gate lock / auto-chain planner
2. 再動態載入 `plugins/continuity/src/index.mjs`
3. 呼叫 `runForceRecallContinuityAdapter({ wrapperResult, autoChainPlanResult, config })`
4. 把 adapter 產出的 block 注入 `bodyForAgent`
`handler.ts` 內已有 plugin 路徑接點,關鍵符號是:
- `runForceRecallContinuityAdapter`
- `[APPROVED_PLAN_CONTINUITY_GATE]`
最小接法示意:
原本的 MVP 整合點`hooks/force-recall/handler.ts`
```js
import plugin from './plugins/continuity/src/index.mjs';
@@ -157,7 +188,48 @@ if (out?.block) {
}
```
若要覆蓋 block label可改 `adapter.forceRecall.injectBlockLabel`
## 整合路徑 Bgeneric / manual preflight
如果你的 workspace **沒有** 使用 `force-recall`,現在也可以安裝這個 plugin直接呼叫 generalized adapter 或 manual runner。
### Generic preflight adapter
```js
import plugin from './plugins/continuity/src/index.mjs';
const out = plugin.runGenericPreflightContinuityAdapter({
config: plugin.defaultConfig,
source: {
planId: 'approved-plan-1',
currentTask: 'task-3',
taskState: 'complete',
nextTaskKnown: true,
sameApprovedPlan: true,
taskBoundaryStop: true,
nextTaskId: 'task-4',
nextDerivedAction: { type: 'message_subagent', task: 'continue' },
replyClosureState: 'completed',
dispatchReceipt: null,
},
});
```
### Manual runner
```js
import plugin from './plugins/continuity/src/index.mjs';
const out = plugin.runManualContinuityPreflight({
config: plugin.defaultConfig,
planId: 'approved-plan-1',
currentTask: 'task-3',
taskState: 'complete',
nextDerivedAction: { type: 'message_subagent', task: 'continue' },
replyClosureState: 'waiting_user',
});
```
`out.block` 非空,就把它 prepend 到 agent 會看到的 prompt/body。
## Receipt contract
@@ -171,24 +243,6 @@ if (out?.block) {
- `childSessionKey`
- `replyClosureState`
範例可參考 `examples/approved-plan-receipt.example.json`
```json
{
"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"
}
```
若要把 receipt 落盤,可用:
```js
@@ -202,51 +256,51 @@ await writeReceipt({
## Smoke test / 驗證
至少執行以下驗證:
plugin 本體必要驗證:
```bash
cd plugins/continuity
npm test
node test/continuity.smoke.test.mjs
```
若你的 workspace 有使用 `force-recall`,再補跑:
```bash
cd /path/to/workspace
node scripts/test_force_recall_long_task_preflight.mjs
node --check hooks/force-recall/handler.ts
```
若只想先做 plugin 本體最小檢查,也可以:
```bash
cd plugins/continuity
node test/continuity.smoke.test.mjs
```
## 安裝與套用步驟(給其他 OpenClaw 使用者)
1.`plugins/continuity` 複製到你的 workspace。
2. 確認 `hooks/force-recall/handler.ts` 會載入 `plugins/continuity/src/index.mjs`
2. 選一條整合路徑:
- `force-recall`:載入 `runForceRecallContinuityAdapter(...)`
- 沒有 `force-recall`:呼叫 `runGenericPreflightContinuityAdapter(...)``runManualContinuityPreflight(...)`
3. 視需要調整 continuity config至少確認
- `planMatchers`
- `legalTerminalStates`
- `receiptDir`
- `adapter.forceRecall.injectBlockLabel`
- `adapter.genericPreflight.injectBlockLabel`
4. 若你的 dispatch 流程會產生 child run/session請同步寫出 receipt。
5.smoke test 與 hook preflight 測試
5.plugin 測試與對應 workspace smoke path
6. 確認 agent prompt 內可見 continuity gate block且 dry-run dispatch 不會被誤判為 pass。
## 目前限制
- 目前是 **approved-plan continuity hard gate** 的 MVP 抽離,不是通用 workflow engine。
- 主要 adapter 只有 `force-recall`,尚未抽象成多 hook / 多事件通用介面
- config 目前是模組內預設 + 呼叫端傳入,還沒有完整的 OpenClaw plugin 安裝器/註冊流程文件
- 它仍以 approved-plan continuity hard gate 為中心,不是完整通用 workflow engine。
- generalized engine contract 目前刻意保持最小且保守
- `force-recall` 仍是目前最成熟的 adapter
- receipt store 只負責寫檔,不含 retention、cleanup、indexing。
- receipt validator 目前只檢查最小 contract不驗證每個 `nextDerivedAction` 子欄位語意。
- 文件描述的是「依現有 hook 整合」的安裝方式;若未採用 `force-recall` preflight 鏈,仍需自行補 glue code。
## 備註
- 預設 legal terminal states`waiting_user``blocked``pending_verification`
- evaluator 保留既有行為,包括 `missing_dispatch_receipt``missing_auto_next_dispatch`
- adapter 維持與 `hooks/force-recall/handler.ts` 的 continuity input mapping 一致
- 新增 generic path 後,即使沒有 `force-recall` hook也比較能重用這個 plugin
- `HOOK.md` 說明的是 plugin/hook adapter 契約定位,不是完整安裝說明
## 英文文件