feat: 改寫為 Node.js,新增動漫角色建人格、劇場模式與記憶固化條件

腳本全面從 Python 改寫為 Node.js(ESM,只用內建模組,無 npm 依賴):
scripts/persona-lib.mjs(核心)、scripts/persona.mjs(CLI)、hooks/*.mjs(六個
hook)、scripts/selftest.mjs(68 項自我測試,全綠)。

新增:
- persona-anime skill:用「動漫作品+角色名」建立人格,先上網蒐集至少三個獨立
  來源的公開設定,映射成 OpenClaw 的 IDENTITY 五欄位與 SOUL 四段落,再固化成
  canon 基礎記憶(每則帶來源 URL)+原作人際關係圖+依角色型別的情緒基線;
  必寫 roleplay-frame 界線記憶(非官方、非本人)。
- 劇場模式:invite 後只顯示人格對話(`名字:內容`)。UserPromptSubmit hook 每輪
  注入強制規則、Stop hook 完全靜音,CLI 新增 --quiet 與 room script(乾淨對話稿)。
  leave 後沒客人自動關閉,也可用 room theater --on/--off 手動切換。
- 短期→長期記憶的成文轉入條件 R1–R6(promotionCandidates)與 candidates 子指令,
  hook 在達標時提醒固化;長期記憶新增 canon 型別與 rules 欄位。
- 人格改為「由使用者呼叫才載入」:SessionStart hook 只列出可用人格,不自動附身。

其他:版本號改回 0.0.1;README/AGENTS.md 同步更新。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-29 16:03:27 +00:00
co-authored by Claude Opus 5
parent 4426ede979
commit 2005f26c94
39 changed files with 1878 additions and 2695 deletions
+58 -13
View File
@@ -10,14 +10,18 @@ skills 是共通標準,**鎖與隔離的強制執行需要 hook,目前只有
---
## 條硬規則
## 條硬規則
| 規則 | 怎麼做到 |
| --- | --- |
| **1. 用與 OpenClaw 相同的描述建立人格** | `persona-create` 逐項索取 `Name` / `Creature` / `Vibe` / `Emoji` / `Avatar`(連括號提示文字都照 OpenClaw 原文),`SOUL.md` 沿用 `Core Truths` / `Boundaries` / `Vibe` / `Continuity` 段落結構 |
| **1a. 用與 OpenClaw 相同的描述建立人格** | `persona-create` 逐項索取 `Name` / `Creature` / `Vibe` / `Emoji` / `Avatar`(連括號提示文字都照 OpenClaw 原文),`SOUL.md` 沿用 `Core Truths` / `Boundaries` / `Vibe` / `Continuity` 段落結構 |
| **1b. 動漫作品+角色名快速建人格** | `persona-anime` 先上網蒐集該角色的公開設定(至少 3 個獨立來源),映射成上述五欄位與 SOUL,再把設定固化成 `canon` 基礎記憶(每則帶來源 URL)+原作人際關係圖+情緒基線 |
| **2. 同一個人格只能被一個程序載入(Sub Agent 不限)** | `state/lock.json`**session_id** 為主鍵、15 分鐘心跳租約;同一 session 的 sub agent 沿用同一把鎖,跨 session 搶佔會被拒;租約過期才可接手(並強制回報) |
| **3. 禁止跨人格讀取資料** | `PreToolUse` hook 對 Read/Write/Edit/Glob/Grep/Bash 做路徑判定(含 `../`、symlink、`$PERSONA_HOME` 繞路),非當前人格一律 deny;CLI 也驗 `--session` 防止冒用身分 |
| **4. 可以邀請人格透過 Sub Agent 一起聊** | `persona-invite` 建聊天室 + guest 唯讀租約,用 `persona-guest` sub agent 啟動對方;兩邊只能透過聊天室交換「說出口的話」,記憶與情緒互不可見 |
| **4. 邀請人格 Sub Agent 一起聊,且只顯示對話** | `persona-invite` 建聊天室 + guest 唯讀租約 + `persona-guest` sub agent;同時開啟**劇場模式**:hook 每輪強制「只輸出 `名字:內容`」、停掉所有系統提醒,CLI 有 `--quiet``room script`(乾淨對話稿) |
| **5. 腳本用 Node.js** | `scripts/*.mjs``hooks/*.mjs`,只用 Node 內建模組(fs/path/os/crypto),無 npm 依賴 |
| **6. 由使用者呼叫才載入並鎖定** | 人格不會自動附身:`SessionStart` hook 只列出可用人格,等使用者下 `/jsc-persona:persona-chat <slug>`;載入即取得獨占鎖並綁定該 session |
| **7. 短期記憶轉入長期記憶有成文條件** | `R1``R6` 六條規則寫在程式裡(`promotionCandidates`),`candidates` 子指令會列出達標的候選與依據,hook 在達標時提醒固化 |
---
@@ -85,14 +89,44 @@ flowchart TB
- **主導情緒**取「超出基線最多」的前三名,所以個性底色不會永遠霸榜。
- 觸發規則與事件→delta 對照表:`skills/persona-chat/reference/emotions.md`
## 短期 → 長期的轉入條件(R1–R6)
寫在 `scripts/persona-lib.mjs``promotionCandidates()`,用 `candidates` 子指令查:
| 規則 | 條件 | 建議固化為 |
| --- | --- | --- |
| **R1** | 單筆顯著度 ≥ 60 | `event` / `fact` |
| **R2** | 同一 topic ≥ 3 筆,或 ≥ 2 筆且平均顯著度 ≥ 45 | `preference` |
| **R3** | 單筆情緒變動總量 ≥ 25 | `event`(帶情緒錨點) |
| **R4** | `intent=commit` 或命中承諾/界線關鍵詞 | `promise` / `boundary`salience ≥ 80,不可遺忘) |
| **R5** | 同一人物(entity)≥ 2 筆 | `relationship`(並更新關係圖) |
| **R6** | 短期記憶 ≥ 40 筆(容量壓力) | 依顯著度清出空間 |
沒命中任何規則的就讓它被裁掉——**遺忘是功能**。達標時 `Stop``remember` 都會提醒去跑
`/jsc-persona:persona-memory`
## 劇場模式(多人格對話只顯示對話)
`invite` 成功即開啟(`leave` 沒有客人時自動關閉,也可 `room theater --on/--off` 手動切):
- `UserPromptSubmit` hook 每輪注入強制規則:輸出**只能**是 `名字:內容`
不得出現指令、指令輸出、狀態、分析、旁白、摘要。
- `Stop` hook 在劇場模式**完全不發系統訊息**(提醒會破壞畫面)。
- CLI 提供 `--quiet`(成功時零輸出)與 `room script`(只有 `emoji 名字(情緒):內容` 的乾淨對話稿)。
```
🪼 Lumi(喜悅42/期待31):所以你真的一個人把那台舊鐘修好了?
🌙 Shen(平靜50/信任38):修好了。它現在慢三分鐘,我決定不修那三分鐘。
```
## HooksClaude Code
| Hook | 做什麼 |
| --- | --- |
| `SessionStart` | 清死鎖、接續人格、把 `PERSONA_SESSION=<session_id>` 與規則注入上下文 |
| `UserPromptSubmit` | 注入 `<persona-context>`:身分、情緒、短期記憶、關鍵詞命中的長期記憶、相關人際關係;並記原始逐字 |
| `UserPromptSubmit` | 注入 `<persona-context>`:身分、情緒、短期記憶、關鍵詞命中的長期記憶、相關人際關係;劇場模式時追加「只輸出人格對話」的強制規則;並記原始逐字 |
| `PreToolUse` | **人格隔離與鎖驗證的唯一強制點**deny 帶原因) |
| `Stop` | 情緒隨時間衰減、續租、記錄回覆、短期記憶過多時提醒固化 |
| `Stop` | 情緒隨時間衰減、續租、記錄回覆、達固化條件時提醒(劇場模式時完全靜音) |
| `SubagentStop` | 解除 guest sub agent 的 pin |
| `SessionEnd` | 釋放鎖與 guest 租約,人格才能被下一個程序載入 |
@@ -111,6 +145,12 @@ flowchart TB
- **Claude Code / Antigravity**`/jsc-persona:persona-create` **Codex**`$persona-create`
### `persona-anime`
用「動漫作品+角色名」建立人格:上網蒐集角色公開設定 → 映射成 OpenClaw 五欄位與 SOUL → 固化成 `canon` 基礎記憶(附來源)+原作關係圖+情緒基線。
- **Claude Code / Antigravity**`/jsc-persona:persona-anime 《作品》 角色名` **Codex**`$persona-anime`
### `persona-chat`
載入人格並對話:取得獨占鎖 → 每輪做語意分析 → 更新十二情緒 → 回想記憶與關係 → 以人格語氣回覆 → 寫回記憶。
@@ -119,13 +159,13 @@ flowchart TB
### `persona-invite`
邀請另一個人格透過 `persona-guest` sub agent 加入聊天室,形成多人格對話;結束後讓它離場並把見聞留在它自己的 inbox。
邀請另一個人格透過 `persona-guest` sub agent 加入聊天室,進入**劇場模式**(畫面只留 `名字:內容`人格對話;結束後讓它離場並把見聞留在它自己的 inbox。
- **Claude Code / Antigravity**`/jsc-persona:persona-invite <slug>` **Codex**`$persona-invite`
### `persona-memory`
記憶固化:短期 → 長期(一則一檔)、淘汰雜訊、更新心智圖與思維導圖、消化 guest inbox、重建索引。
記憶固化:依 R1R6 條件把短期記憶轉入長期(一則一檔)、淘汰雜訊、更新心智圖與思維導圖、消化 guest inbox、重建索引。
- **Claude Code / Antigravity**`/jsc-persona:persona-memory` **Codex**`$persona-memory`
@@ -149,14 +189,19 @@ flowchart TB
### CLI 與自我測試
所有狀態變更都經過 `scripts/persona.py`(純標準庫,無外部依賴):
所有狀態變更都經過 `scripts/persona.mjs`**Node.js ≥ 18**,只用內建模組,無 npm 依賴):
```bash
python3 scripts/persona.py --help
python3 scripts/persona.py list
python3 scripts/selftest.py # 51 項驗證:鎖、隔離、情緒、記憶、聊天室、hooks
node scripts/persona.mjs --help
node scripts/persona.mjs list
node scripts/persona.mjs candidates --session <PERSONA_SESSION> # 看哪些短期記憶該固化
node scripts/persona.mjs room script --session <id> --room <room> # 乾淨對話稿(劇場模式用)
node scripts/selftest.mjs # 68 項驗證:鎖、隔離、情緒、固化條件、劇場模式、hooks
```
檔案結構:`scripts/persona-lib.mjs`(核心:鎖/隔離/情緒/記憶)、`scripts/persona.mjs`CLI)、
`hooks/*.mjs`(六個 hook)、`scripts/selftest.mjs`(自我測試)。
---
## 跨助理支援度
@@ -249,7 +294,7 @@ cp -r ~/plugins/persona/skills/* ~/.config/opencode/skills/
## 新增/修改 skill
1. 複製一個現有 skill 目錄,改 `SKILL.md``name``description`(描述要寫清楚何時用、何時不用)。
2. 需要新的狀態操作 → 加到 `scripts/persona.py` 的子指令,並在 `scripts/selftest.py` 補測試。
3. 動到隔離規則 → 一定要在 `selftest.py` 的第 ③/⑦ 區加對應案例,跑到全綠。
2. 需要新的狀態操作 → 加到 `scripts/persona.mjs` 的子指令,並在 `scripts/selftest.mjs` 補測試。
3. 動到隔離規則 → 一定要在 `selftest.mjs` 的第 ③(隔離)/⑧(guest 與劇場模式)區加對應案例,跑到全綠。
4. 把 skill 補進上方「Skills 目錄」區塊。
5. bump `.claude-plugin/plugin.json``.codex-plugin/plugin.json``plugin.json` 三處 `version`commit 後 push。