docs: add agent guide and generic integration runbooks
This commit is contained in:
591
plugins/continuity/GENERIC_INTEGRATION.zh-TW.md
Normal file
591
plugins/continuity/GENERIC_INTEGRATION.zh-TW.md
Normal file
@@ -0,0 +1,591 @@
|
||||
# Continuity Plugin:Generic / Manual Integration Runbook
|
||||
|
||||
> 目標讀者:**沒有 `force-recall` hook 的宿主 / orchestrator / planner 實作者**
|
||||
>
|
||||
> 這份文件專門說明:當你的 host 沒有 `hooks/force-recall/handler.ts` 時,如何仍然安裝 continuity plugin、提供 payload、執行 manual/generic preflight,並驗證 continuity gate block 真的有生效。
|
||||
|
||||
> 英文版:`GENERIC_INTEGRATION.md`
|
||||
|
||||
---
|
||||
|
||||
## 0. 這份 runbook 解決什麼問題
|
||||
|
||||
如果你的宿主屬於以下任一情境,請優先看這份:
|
||||
|
||||
- 你沒有 `force-recall` hook。
|
||||
- 你有自己的 planner / preflight / orchestrator。
|
||||
- 你想手動把 approved-plan 狀態餵給 continuity plugin。
|
||||
- 你需要一份「最少要給哪些欄位」的 payload 規格。
|
||||
- 你需要知道如何驗證 continuity block 有真的擋住錯誤結案,而不是只是在文件裡說可以。
|
||||
|
||||
一句話總結:
|
||||
|
||||
**沒有 `force-recall` 也能裝 continuity plugin,但你必須自己提供 generic continuity input,並把回傳的 block 接進 agent prompt。**
|
||||
|
||||
---
|
||||
|
||||
## 1. 最小安裝目標
|
||||
|
||||
安裝完成後,至少要達到以下結果:
|
||||
|
||||
- 你的 host 能 import `plugins/continuity/src/index.mjs`
|
||||
- 你的 host 能呼叫:
|
||||
- `runGenericPreflightContinuityAdapter(...)`,或
|
||||
- `runManualContinuityPreflight(...)`
|
||||
- 你的 host 能把回傳的 `out.block` prepend 到 agent 會看到的 prompt/body
|
||||
- 當任務完成、但還有下一步且沒有真實 dispatch receipt 時,continuity gate 會擋住「直接結案」
|
||||
- `cd plugins/continuity && npm test` 可通過
|
||||
|
||||
---
|
||||
|
||||
## 2. 安裝位置與檔案
|
||||
|
||||
建議放在:
|
||||
|
||||
```text
|
||||
<workspace>/plugins/continuity
|
||||
```
|
||||
|
||||
generic/manual 路徑至少需要這些檔案:
|
||||
|
||||
```text
|
||||
plugins/continuity/
|
||||
package.json
|
||||
src/index.mjs
|
||||
src/adapters/generic-preflight.mjs
|
||||
src/config/defaults.mjs
|
||||
src/config/schema.mjs
|
||||
src/continuity/engine.mjs
|
||||
src/continuity/evaluator.mjs
|
||||
src/continuity/receipt-validator.mjs
|
||||
src/continuity/receipt-store.mjs
|
||||
src/continuity/types.md
|
||||
examples/openclaw.continuity.example.json
|
||||
examples/approved-plan-receipt.example.json
|
||||
README.zh-TW.md
|
||||
README.md
|
||||
GENERIC_INTEGRATION.zh-TW.md
|
||||
```
|
||||
|
||||
可先用這個命令確認:
|
||||
|
||||
```bash
|
||||
cd <workspace>
|
||||
find plugins/continuity -maxdepth 3 -type f | sort
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. generic/manual 路徑會用到哪些 API
|
||||
|
||||
`src/index.mjs` 已經 re-export 這幾個你最需要的 API:
|
||||
|
||||
- `defaultConfig`
|
||||
- `normalizeContinuityConfig()`
|
||||
- `validateContinuityConfig()`
|
||||
- `buildGenericContinuityInput()`
|
||||
- `createGenericPreflightContinuityAdapter()`
|
||||
- `runGenericPreflightContinuityAdapter()`
|
||||
- `runManualContinuityPreflight()`
|
||||
- `validateReceipt()`
|
||||
- `isValidReceipt()`
|
||||
- `writeReceipt()`
|
||||
|
||||
如果你只想最短路徑完成整合,通常會直接用:
|
||||
|
||||
- `runGenericPreflightContinuityAdapter(...)`
|
||||
- `runManualContinuityPreflight(...)`
|
||||
- `writeReceipt(...)`
|
||||
|
||||
---
|
||||
|
||||
## 4. continuity input 最小 contract
|
||||
|
||||
generic adapter 吃的是 **host-agnostic continuity input**。
|
||||
|
||||
依 `src/continuity/types.md`,實務上最少要準備:
|
||||
|
||||
- `planId`: string
|
||||
- `currentTask`: string
|
||||
- `taskState`: string | null
|
||||
- `nextDerivedAction`: object | null
|
||||
- `replyClosureState`: string | null
|
||||
- `dispatchReceipt`: object | null
|
||||
- `nextTaskKnown`: boolean
|
||||
- `sameApprovedPlan`: boolean
|
||||
- `taskBoundaryStop`: boolean
|
||||
- `highRiskStop`: boolean
|
||||
|
||||
可選欄位:
|
||||
|
||||
- `nextTaskId`: string | null
|
||||
- `nextTaskKey`: string | null
|
||||
- `derivedAction`: object | null
|
||||
- `metadata`: object
|
||||
|
||||
### 最小可運作 payload 範例
|
||||
|
||||
```js
|
||||
const source = {
|
||||
planId: 'approved-plan-1',
|
||||
currentTask: 'task-3',
|
||||
taskState: 'complete',
|
||||
nextTaskKnown: true,
|
||||
sameApprovedPlan: true,
|
||||
taskBoundaryStop: true,
|
||||
highRiskStop: false,
|
||||
nextTaskId: 'task-4',
|
||||
nextDerivedAction: {
|
||||
type: 'message_subagent',
|
||||
task: 'continue with task-4',
|
||||
},
|
||||
replyClosureState: 'completed',
|
||||
dispatchReceipt: null,
|
||||
metadata: {
|
||||
host: 'custom-orchestrator',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 什麼情況下這個 payload 會觸發 gate?
|
||||
|
||||
常見高風險情境:
|
||||
|
||||
- `taskState = 'complete'`
|
||||
- `nextTaskKnown = true`
|
||||
- `sameApprovedPlan = true`
|
||||
- `nextDerivedAction != null`
|
||||
- `dispatchReceipt = null`
|
||||
- `replyClosureState = 'completed'`
|
||||
|
||||
這類情況通常代表:**目前 task 做完了、下一步也知道了,但你還沒有真實派發 receipt,卻準備把整件事收掉。**
|
||||
|
||||
這正是 continuity gate 要攔的核心情境。
|
||||
|
||||
---
|
||||
|
||||
## 5. generic adapter 接法
|
||||
|
||||
### 最小接法
|
||||
|
||||
```js
|
||||
import plugin from './plugins/continuity/src/index.mjs';
|
||||
|
||||
const out = plugin.runGenericPreflightContinuityAdapter({
|
||||
config: plugin.defaultConfig,
|
||||
source,
|
||||
});
|
||||
|
||||
if (out?.block) {
|
||||
agentPromptBody = `${out.block}\n${agentPromptBody}`;
|
||||
}
|
||||
```
|
||||
|
||||
### 回傳值你至少要看哪些欄位
|
||||
|
||||
`out` 常用欄位:
|
||||
|
||||
- `out.input`
|
||||
- `out.result`
|
||||
- `out.evaluation`
|
||||
- `out.block`
|
||||
- `out.meta.adapterName`
|
||||
- `out.meta.hostAgnostic`
|
||||
|
||||
### 什麼時候不該忽略 `out.block`
|
||||
|
||||
只要 `out.block` 非空,就表示 plugin 產生了應注入 prompt 的 continuity gate block。
|
||||
|
||||
**不要只把 `evaluation` 印 log,就算整合完成。**
|
||||
|
||||
真正有完成整合的最低標準,是:
|
||||
|
||||
- 有拿到 `out.block`
|
||||
- 有把它接到 agent prompt/body
|
||||
- agent 後續真的會看到該 block
|
||||
|
||||
---
|
||||
|
||||
## 6. manual runner 接法
|
||||
|
||||
如果你沒有完整 generic source builder,或者只想先做一次單點 preflight 驗證,可用 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 with task-4',
|
||||
},
|
||||
replyClosureState: 'completed',
|
||||
nextTaskKnown: true,
|
||||
sameApprovedPlan: true,
|
||||
taskBoundaryStop: true,
|
||||
dispatchReceipt: null,
|
||||
});
|
||||
|
||||
if (out?.block) {
|
||||
agentPromptBody = `${out.block}\n${agentPromptBody}`;
|
||||
}
|
||||
```
|
||||
|
||||
### manual runner 適合什麼情況
|
||||
|
||||
- 你還沒有完整 planner,但想先驗證 gate 行為
|
||||
- 你要做 integration smoke test
|
||||
- 你在 migration 階段,先用手動欄位對接舊宿主
|
||||
|
||||
### manual runner 不代表可以跳過正式整合
|
||||
|
||||
manual runner 比較像 **preflight harness**,不是完整 host integration 的替代品。
|
||||
|
||||
等你確認行為正確後,仍建議回到 generic adapter,把 host 真實資料映射進 source。
|
||||
|
||||
---
|
||||
|
||||
## 7. config 怎麼準備
|
||||
|
||||
建議先從:
|
||||
|
||||
```text
|
||||
plugins/continuity/examples/openclaw.continuity.example.json
|
||||
```
|
||||
|
||||
開始,內容大致如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"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"
|
||||
},
|
||||
"genericPreflight": {
|
||||
"enabled": true,
|
||||
"injectBlockLabel": "APPROVED_PLAN_CONTINUITY_GATE"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### generic/manual 路徑先檢查這些欄位
|
||||
|
||||
- [ ] `enabled = true`
|
||||
- [ ] `planMatchers` 符合你的 approved plan 名稱
|
||||
- [ ] `legalTerminalStates` 符合你的 closure policy
|
||||
- [ ] `receiptDir` 可寫
|
||||
- [ ] `requireRealDispatchReceipt = true`(通常建議保持)
|
||||
- [ ] `allowReplyClosureWithoutDispatch = false`(通常建議保持)
|
||||
- [ ] `adapter.genericPreflight.enabled = true`
|
||||
- [ ] `adapter.genericPreflight.injectBlockLabel` 有你想要的 label
|
||||
|
||||
---
|
||||
|
||||
## 8. receipt 怎麼提供
|
||||
|
||||
如果你的宿主真的有派發下一步,不要只在 metadata 或 log 裡寫「已派發」,請提供真實 `dispatchReceipt`。
|
||||
|
||||
### 最小 receipt shape
|
||||
|
||||
依目前 validator,至少要有:
|
||||
|
||||
- `planId`
|
||||
- `currentTask`
|
||||
- `nextDerivedAction`
|
||||
- `dispatchedAt`
|
||||
- `dispatchRunId`
|
||||
- `childSessionKey`
|
||||
- `replyClosureState`
|
||||
|
||||
對照範例:
|
||||
|
||||
```text
|
||||
plugins/continuity/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
|
||||
import plugin from './plugins/continuity/src/index.mjs';
|
||||
|
||||
const receiptValidation = plugin.validateReceipt(receipt);
|
||||
if (!receiptValidation.ok) {
|
||||
throw new Error(`Invalid continuity receipt: ${receiptValidation.errors.join('; ')}`);
|
||||
}
|
||||
```
|
||||
|
||||
### 寫 receipt 範例
|
||||
|
||||
```js
|
||||
await plugin.writeReceipt({
|
||||
receiptDir: 'state/approved-plan-continuity',
|
||||
receipt,
|
||||
});
|
||||
```
|
||||
|
||||
### receiptDir 先建立
|
||||
|
||||
```bash
|
||||
cd <workspace>
|
||||
mkdir -p state/approved-plan-continuity
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. manual/generic preflight 驗證流程
|
||||
|
||||
這一段是本文件最重要的實作 checklist。
|
||||
|
||||
### Case A:應該被 gate 擋下
|
||||
|
||||
測這種輸入:
|
||||
|
||||
- task 已完成
|
||||
- 下一步已知
|
||||
- 同一 approved plan
|
||||
- 有 nextDerivedAction
|
||||
- 沒有 dispatchReceipt
|
||||
- closure 想直接完成
|
||||
|
||||
例如:
|
||||
|
||||
```js
|
||||
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,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
你應該預期:
|
||||
|
||||
- `out.block` 非空
|
||||
- `out.evaluation` 顯示這不是可安全直接結案的狀態
|
||||
- agent prompt 若接上此 block,應被提醒不可把整件事直接收掉
|
||||
|
||||
### Case B:有合法 terminal state,不應誤擋
|
||||
|
||||
例如:
|
||||
|
||||
- `replyClosureState = 'waiting_user'`
|
||||
- 或 `blocked`
|
||||
- 或 `pending_verification`
|
||||
|
||||
你應該預期:
|
||||
|
||||
- 不會把合法等待/阻塞/待驗證情境誤當成錯誤結案
|
||||
|
||||
### Case C:有真實 receipt,應與 dry-run 區分
|
||||
|
||||
把 `dispatchReceipt` 換成合法 receipt object,再重跑一次。
|
||||
|
||||
你要確認:
|
||||
|
||||
- plugin 有辨識 receipt
|
||||
- 行為和 `dispatchReceipt = null` 時不同
|
||||
- 不是只因為 `nextDerivedAction` 存在,就一律當成已派發
|
||||
|
||||
---
|
||||
|
||||
## 10. 如何驗證 continuity block 真的有被接進 prompt
|
||||
|
||||
這一步很常被忽略,但它才是整合成功與否的分水嶺。
|
||||
|
||||
### 最低驗證標準
|
||||
|
||||
你至少要證明以下三件事:
|
||||
|
||||
1. plugin 有回傳 `out.block`
|
||||
2. host 有把 `out.block` prepend 到 agent prompt/body
|
||||
3. 最終 agent 真正看到的 prompt 中,存在:
|
||||
|
||||
```text
|
||||
[APPROVED_PLAN_CONTINUITY_GATE]
|
||||
```
|
||||
|
||||
### 建議做法
|
||||
|
||||
- 在 preflight / orchestrator 測試輸出中印出最終 prompt 前段
|
||||
- 或在 debug mode 下 dump prompt body
|
||||
- 或加一個 integration smoke test,直接 assert block label 存在
|
||||
|
||||
### 不能只驗證什麼
|
||||
|
||||
以下都 **不夠**:
|
||||
|
||||
- 只看到 `out.evaluation`
|
||||
- 只看到 console log 說有 run adapter
|
||||
- 只看到 code 裡有 prepend 那一行,但沒有實際驗證 prompt 結果
|
||||
|
||||
---
|
||||
|
||||
## 11. 建議 smoke test 順序
|
||||
|
||||
### Step 1:plugin 本體測試
|
||||
|
||||
```bash
|
||||
cd <workspace>/plugins/continuity
|
||||
npm test
|
||||
```
|
||||
|
||||
### Step 2:快速 smoke
|
||||
|
||||
```bash
|
||||
cd <workspace>/plugins/continuity
|
||||
node test/continuity.smoke.test.mjs
|
||||
```
|
||||
|
||||
### Step 3:你的 generic/manual host integration test
|
||||
|
||||
如果你有自家 orchestrator,請至少補一個 smoke case:
|
||||
|
||||
- 無 receipt + 想 completed → 應被 gate
|
||||
- 合法 terminal state → 不應誤擋
|
||||
- 有合法 receipt → 行為應與無 receipt 不同
|
||||
|
||||
### Step 4:人工檢查 prompt block
|
||||
|
||||
確認輸出真的含有:
|
||||
|
||||
```text
|
||||
[APPROVED_PLAN_CONTINUITY_GATE]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 宿主實作者最常踩的坑
|
||||
|
||||
### 坑 1:只有 planner output,沒有實際 prompt injection
|
||||
|
||||
這會讓 continuity 只停在「有計算」,沒有真正影響 agent 行為。
|
||||
|
||||
### 坑 2:把 dry-run next action 當成已 dispatch
|
||||
|
||||
`nextDerivedAction != null` 不等於 `dispatchReceipt != null`。
|
||||
|
||||
### 坑 3:payload 缺少 `planId` 或 `currentTask`
|
||||
|
||||
`buildGenericContinuityInput()` 若拿不到這些關鍵欄位,可能直接回不出有效 input。
|
||||
|
||||
### 坑 4:把合法 terminal state 設定錯
|
||||
|
||||
例如你宿主其實接受 `waiting_user`,但 config 沒放進 `legalTerminalStates`,就可能造成誤判。
|
||||
|
||||
### 坑 5:receipt 寫檔了,但內容不合法
|
||||
|
||||
先 `validateReceipt()`,不要跳過。
|
||||
|
||||
### 坑 6:只測 happy path
|
||||
|
||||
至少要測:
|
||||
|
||||
- 應 fail 的情況
|
||||
- 應 pass / 不誤擋的情況
|
||||
- 有 receipt 與無 receipt 的差異
|
||||
|
||||
---
|
||||
|
||||
## 13. 一頁版 generic/manual checklist
|
||||
|
||||
### 安裝
|
||||
|
||||
- [ ] 複製 `plugins/continuity` 到 workspace
|
||||
- [ ] 確認 `src/index.mjs` 可 import
|
||||
- [ ] 建立 `state/approved-plan-continuity/`
|
||||
|
||||
### config
|
||||
|
||||
- [ ] 套用 example config
|
||||
- [ ] 開啟 `adapter.genericPreflight.enabled`
|
||||
- [ ] 確認 `receiptDir` 可寫
|
||||
|
||||
### host 接法
|
||||
|
||||
- [ ] 準備 generic continuity payload
|
||||
- [ ] 呼叫 `runGenericPreflightContinuityAdapter(...)` 或 `runManualContinuityPreflight(...)`
|
||||
- [ ] 將 `out.block` prepend 到 agent prompt/body
|
||||
|
||||
### receipt
|
||||
|
||||
- [ ] 真有派發時才產生 receipt
|
||||
- [ ] 先 `validateReceipt()`
|
||||
- [ ] 再 `writeReceipt()`
|
||||
|
||||
### 驗證
|
||||
|
||||
- [ ] `cd plugins/continuity && npm test`
|
||||
- [ ] 驗證無 receipt 時會擋錯誤結案
|
||||
- [ ] 驗證合法 terminal state 不誤擋
|
||||
- [ ] 驗證最終 prompt 含 continuity block
|
||||
|
||||
---
|
||||
|
||||
## 14. 什麼叫 generic/manual 整合完成
|
||||
|
||||
只有在以下條件都成立時,才算真的完成:
|
||||
|
||||
- 不是只完成 plugin 安裝,而是 host 真的有呼叫 adapter
|
||||
- 不是只拿到 evaluation,而是 block 真的有注入 agent prompt
|
||||
- 不是只做 dry-run,而是實際 dispatch 時可提供真實 receipt
|
||||
- 不是只測 happy path,而是有驗證 gate fail/pass 的差異
|
||||
- 文件足夠讓陌生宿主實作者知道自己要準備哪些欄位、怎麼驗證
|
||||
|
||||
如果這五點都做到,才算接近「沒有 `force-recall` 的宿主也能照文件安裝」。
|
||||
|
||||
---
|
||||
|
||||
## 15. 參考文件
|
||||
|
||||
- `plugins/continuity/README.zh-TW.md`
|
||||
- `plugins/continuity/README.md`
|
||||
- `plugins/continuity/AGENT_GUIDE.zh-TW.md`
|
||||
- `plugins/continuity/AGENT_GUIDE.md`
|
||||
- `plugins/continuity/src/continuity/types.md`
|
||||
- `plugins/continuity/examples/openclaw.continuity.example.json`
|
||||
- `plugins/continuity/examples/approved-plan-receipt.example.json`
|
||||
|
||||
如果你的宿主未來會新增專屬 adapter,也建議保留這份 generic/manual runbook,作為最低整合基準。
|
||||
Reference in New Issue
Block a user