 jiantw83andClaude Opus 5
|
eebb0c0cef
|
docs(語域): 兩支建人格的流程都寫同一格語域
persona-anime 新增〈從原作推導語域〉:四格對照表與指令,並沿用破口那條界線
(有原作依據才寫,掰不出來留白)——語域每輪都在注入,掰的自稱會被講一百次。
破口是「情緒上來會做什麼」,語域是「他平常怎麼講話」,兩件事分開寫。
persona-story 的語氣層表加一列 voice/idiolect.md,並要求用全書統計而不是逐章印象:
自稱與句尾在一本書裡出現幾百次,拿一章的樣本去定它等於讓那一章的情緒決定他平常
怎麼講話。與原作設定衝突時先問使用者——「他變了」跟「當初查錯了」處理方式不同。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 08:43:48 +00:00 |
|
 jiantw83andClaude Opus 5
|
45a6f2cad9
|
feat(語域): 自稱/句尾/口癖/不說做成第三種 voice kind,四格輪替注入
語氣層原本只有減法(SPEECH_BLACKLIST 刪 AI 腔)與原句樣本。刪完剩下的是「乾淨的
中文」,不是「這個人的中文」——把名字遮掉還認得出是誰,靠的是自稱、句尾、口癖。
做成 voice kind(voice/idiolect.md)而不是 IDENTITY 的新區塊,是為了讓
persona-story 與 persona-anime 天生寫同一格:同一個目錄、同一支 voice add、
同一條 Gitea Wiki 同步路徑(voice/ 是前綴比對,新檔自動涵蓋)。
TODO 要的「兩邊用同一個欄位」因此不必再另外對齊。
四格寫死在 IDIOLECT_FACETS。自由欄位會長出「風格」「語氣」這種什麼都能塞的格子,
注入端就不知道該怎麼講給人格聽。「不說」單獨一格而不是塞進禁忌:禁忌是話題
(不能提的事),不說是用詞(這個人不會用的字),混在一起會讓人格以為某個話題不能碰。
一輪注入 3 格、四格輪替,起點由第幾輪(felt.jsonl 筆數)算出來。不用隨機數——
那是本專案明文禁止的充人味手段,而且同一輪重跑要得到同樣的結果。
一次塞四格會變成模仿腔:他開始逐條照著演,每句都要有口癖。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 08:43:48 +00:00 |
|
 jiantw83andClaude Opus 5
|
fe17b20010
|
feat(稱呼): 害羞時叫不出名字,節點可設羞怯稱呼
既有的 except(anger>=40 → 退回全名)走的是生氣方向:規則被暫停、退回查表的
更正式講法。害羞是反方向——不是更正式,是叫不出來:改口、講到一半換稱呼、
把名字吞掉。所以另開一條路,不硬塞進 except。
shyAddress():羞恥度過 SHY_ADDRESS_AT(60)就換稱呼。節點設了新的 羞怯稱呼
facet 就用它,沒設就吞掉名字改用「你」——不叫名字本身就是訊號,這是預設值
而不是缺省行為。
門檻取 60,跟破口、標點、嘴硬那一級同一條線。女性預設 70 因此本來就落在這條線上,
那是 G3 要的效果,不是副作用。
羞怯稱呼不列進「他要求過的語氣規則」那一行:它只在過門檻時生效,兩邊都列會讓
人格同時看到「叫他⋯那個」跟「平常叫他⋯那個」。relation style 的清單裡照樣看得到。
toneDirective 改吃 opts,才拿得到這一輪的 state/prev/tone 去算羞恥度。
selftest 728 → 735。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 08:33:19 +00:00 |
|
 jiantw83andClaude Opus 5
|
b5a5f0a6bf
|
feat(分段訊息): 高張力或高羞恥時一輪可以補第二則
純文字唯一的「時間」訊號是「他又打了一行」——沒有停頓、沒有語調,真人在聊天軟體上
的情緒有一半是靠補那一句傳出去的。這個手段之前完全沒用到。
splitBudget():張力或羞恥度過 60,或對方那句帶怒意/厭惡,才給第二則額度。
splitDirective():第二則要短,而且是補救、嘴硬或改口,不可以是補充說明——
把第一則講得更清楚就只是話多,不是慌。
節流 SPLIT_COOLDOWN_TURNS = 4,冷卻靠 felt.jsonl 新增的 split 欄位算。
冷卻是「給了額度就開始算」,不等他真的用了才算:不然「上次給了沒用到」會讓
連續幾輪都允許補話,每輪都補一句是話多不是慌。
沒額度時整段不注入。講「這一輪不能拆成兩則」等於提醒他有這個功能,反而會想用。
selftest 721 → 728。低羞恥度的對照組另建一個男性人格——plainf 是女性預設 70,
本身就過門檻,拿它當「平靜」的對照會測不出東西。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 08:26:54 +00:00 |
|
 jiantw83andClaude Opus 5
|
f26d0315f2
|
feat(標點預算): 情緒決定標點分布,上限夾死免得變成強度計
只有文字的話標點就是語調:同一句「我知道了」,`我知道了。` 與 `我、我知道了⋯`
是兩種情緒,差別全在標點不在用詞。
PUNCTUATION_BUDGET 只寫拍板指名的四種(焦慮/憤怒/害羞/喜悅)。
其餘八種不配標點——硬給每種情緒配一套會讓標點變成裝飾,沒把握的就不編。
punctuationBudget() 每輪只回一種,門檻跟破口同一條線(40)。兩種情緒各配一套
會互相打架:憤怒幾乎不用問號、焦慮要多斷句,混起來不是複雜的情緒,是標點亂撒。
上限:MAX_EXCLAIM = 1、MAX_ELLIPSIS = 3,另擋 […⋯]{3,} 的連發
(中文刪節號本來就常寫兩個,所以三個起才算)。不夾死的話它會變成 emoji 的
替代品——情緒不夠就多打幾個驚嘆號。界線也寫進注入文字:標點管分布、強度歸 emoji。
selftest 711 → 721。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 08:21:37 +00:00 |
|
 jiantw83andClaude Opus 5
|
06774ceb2b
|
feat(記憶): 行為備註同時進 voice/reactions.md,5.7 完成
Q6 明示「記憶要記那個行為」,而 persona-story 的 voice/reactions.md 存的正是
「事件 → 他做了什麼」。兩邊各存一份就會有人只更新一邊,所以接成同一批資料:
remember --behavior 寫短期記憶時,同一筆也 addVoiceReaction()。
分工:記憶留時間軸(那一天發生過這件事),語氣層供每輪注入(他遇到這種事會怎麼反應)。
只有人格自己的行為進語氣層(role === "persona")。對方做了什麼是記憶,
不是他的反應方式——把使用者的動作寫進 reactions.md 會讓人格學別人的反應。
selftest 709 → 711(行為備註進得去、對方的行為不進去)。
TARGET.md 的 5.7 八個子項目全完成,勾上層。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 08:16:04 +00:00 |
|
 jiantw83andClaude Opus 5
|
0d298cbfce
|
merge(故事匯入): feat/persona-story-import 併入 develop
voice 語氣層、novel 機械指令與 persona-story 技能併進 develop,跟 G2/G3 那批合流。
四個共同修改的檔(README、persona-lib、persona.mjs、selftest)全部自動合併,無衝突。
驗證:selftest 646 + 63 = 709 項全綠,零重疊也零回歸。
併進來同時解掉 TARGET.md 兩條卡在跨分支的項目:5.7 的記憶行為欄位要跟
persona-story 的 1.12 對齊、5.6 的語域要吃 3A 的語氣統計——兩邊現在同一棵樹了。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 08:12:12 +00:00 |
|
 jiantw83andClaude Opus 5
|
febac85c79
|
chore(TARGET): 5.4 完成、5.7 剩一條待人工,進度表換掉暫停段
5.4 三個子項目全完成所以勾上層;5.7 八個子項目完成七個,只剩「記憶要記那個行為」
的欄位對齊卡在 persona-story 不在 develop,照規則(子項目全完成才勾上層)不勾。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 08:07:45 +00:00 |
|
 jiantw83andClaude Opus 5
|
f0d7d7422e
|
test(selftest): 補動作格渲染與三段式覆寫的案例,641 → 646
新增五項:room read 的括號帶動作格、一對一也拿到括號格式、害羞預設破口是臉紅、
三段式逐人格覆寫生效且不套用預設收尾、沒寫覆寫的人格照舊。
改一項:「劇場模式強制只輸出對話」原本斷言 ctx 含字面「名字:內容」,
Q6 把格式改成「名字(情緒・動作):內容」之後那個斷言必然失敗——
斷言跟著格式走,不是把格式改回去。
覆寫測試的 fixture 刻意用 sulk 這個 key 而不是「鬧彆扭」:後者是 MODESTY_SIGNALS
的推力詞,寫進 IDENTITY 會把羞恥度推過 75 落到惱羞那級,就測不到嘴硬的三段。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 08:07:45 +00:00 |
|
 jiantw83andClaude Opus 5
|
05031da6dc
|
feat(情緒表達): 三段式逐人格覆寫、括號動作算進額度、害羞預設臉紅、一對一也吃括號
補的是合併後對照查出的缺口,設計都在 Q6/Q7 拍板範圍內,沒有新決策。
三段式逐人格覆寫(G2 的核心顧慮)
- personaSulk() 讀 ## Tells 的 sulk/鬧彆扭/三段式 那一行,分隔吃 → 或頓號。
共用 ## Tells 這一層、不另開區塊——三段式跟破口是同一種東西(不自覺會做的事),
分兩個地方寫只會有人只改一邊。
- 只覆寫嘴硬那一級(60-74)。惱羞(75+)最後一段是翻臉、不是音量掉下來,
拿嘴硬的收尾去套會變成另一個形狀。
- 沒有覆寫就照舊用預設模板。原本所有人格嘴硬起來是同一個樣子,那正是 G2 要避免的。
- 順手把 personaTells() 讀區塊那段抽成 tellsSection(),personaSulk 共用;
不抽就得再寫一份一樣的 reader。
括號裡的動作算在額度內(Q7 的漏洞)
- 每輪指示原本只說「一輪最多露 2 個動作」,沒說括號那個算不算。
不講明就會出現「括號演一個、句子裡再演兩個」=一輪三個。
害羞的預設動作是臉紅
- EMOTION_TELLS.shame 原本三條全是句法(鬧彆扭/講反話/轉移話題),
沒有一條是看得見的,動作格拿不到預設值。臉紅擺第一個。
一對一也吃括號格式
- Q6 拍板的是格式,不是只給劇場。hooks 的劇場那段放寬成
「名字(情緒・動作):內容」,非劇場那段從完全沒提補成講清楚,
並重申 *別過頭* 與句子裡的「我愣了一下」仍然不行。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 08:07:45 +00:00 |
|
 jiantw83andClaude Opus 5
|
f02e01f629
|
fix(劇場渲染): room read 的括號吃掉動作格
roomScript() 用 roomTag() 組括號,room read 卻自己拼「(情緒)」——同一份逐字稿
用兩支路徑渲染,動作格在 room read 這邊會整格消失。改成也走 roomTag()。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 08:07:45 +00:00 |
|
 jiantw83andClaude Opus 5
|
f1f5ed61eb
|
chore(TARGET): 合併後逐項對照,5.8 與 5.5 完成、5.7 與 5.4 標出還缺哪幾格
feat/persona-anime-expression 併進來之後照解除條件逐項對照,不照 commit 訊息
照信,每條都回程式裡查過。
完成:5.8 emoji 表程度全部六個子項目、5.5 女性預設值兩個子項目。
5.7 與 5.4 大部分做完但不勾選,各自還缺幾格,逐條寫在該行下面:
- room read 還是只印(情緒),persona.mjs:1861 沒改用 roomTag(),動作格會被吃掉
(roomScript() 有吃,所以是一半)
- hooks/prompt_submit.mjs:34 劇場模式仍寫死「名字:內容」,非劇場那段更是完全沒提括號
- 每輪指示沒講「括號裡的動作算在兩個額度內」——emoji 那段有提括號,動作沒有
- EMOTION_TELLS.shame 沒把臉紅放進預設破口(actionLint 放行了,但預設值沒改)
- 三段式模板沒有 per-persona 覆寫,所有人格的三段式目前是同一個樣子
記憶行為欄位那條標 ⏭️ 待人工處理:persona-story 不在 develop(在 feat/persona-story-import),
「跟它 1.12 用同一個欄位」現在對齊不了,硬做只會變成各存一份,正是那條要避免的事。
voice/reactions.md 全 repo 零引用。
順便修掉收尾裡已經過期的半句(README 第 29 行那句已隨 dccd07a 移除)。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 07:56:07 +00:00 |
|
 jiantw83andClaude Opus 5
|
e12c16ca3f
|
merge(情緒表達): feat/persona-anime-expression 併入 develop
把 G2/G3 的第一批實作(emoji 表程度、名字後括號的動作格、一輪兩個動作、
鬧彆扭三級階梯、女性羞恥預設 62 → 70)併進 develop,與 TARGET.md 的清單合流。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 07:49:40 +00:00 |
|
 jiantw83andClaude Opus 5
|
35c8665db0
|
feat(情緒表達): emoji 表程度、名字後括號的動作格、鬧彆扭的階梯
只有文字這一個通道,所以情緒表達是排版問題。這批補的是三個沒被用到的位置。
emoji = 強度計(Q9)
- EMOTION_EMOJI 十二情緒各一個,EMOJI_INTENSITY 兩階(💦 / 💦❗)。
40-59 本體、60-79 加 💦、80+ 加 💦❗,低於 40 不顯示(跟破口同一條線)。
- emotionEmojiNow() 讀當下主導情緒算出來,不是自己挑;IDENTITY 的 ## Emoji
區塊可逐人格覆寫(跟 ## Tells 同一層)。
- 刻意不用重複本體表示程度(不是 😳😳😳)——疊字看起來像洗頁。
- room post 沒帶 --emotion 時自動帶入 emoji,強度不到才退回文字標註。
emoji 開放(Q5/Q10)
- speechLint 不再擋 emoji,超過六個才給 hint。用 linter 偷偷關回去等於撤銷
「開放」這個決定。
- 唯一的煞車是數字:emojiAudit() 掛在 emotion --audit,算幾則帶符號、平均幾個、
以及**只有符號沒有句子**那幾則(那才是真的退化)。
名字後面的括號有兩格(Q6)
- 括號原本就有(放情緒標註),動作是加進去的第二格:名字(情緒・動作):內容。
roomTag() 負責組,兩格都可省略。
- actionLint() 擋三種:超過 12 字、一格塞兩個動作、寫情緒名稱
((害羞)是旁白換了個位置)。生理反應算動作:臉紅、手在抖、聲音變小。
- room post --action、remember --behavior(記做了什麼,不是感覺到什麼)。
一輪兩個動作(Q7)
- TELLS_PER_TURN = 2,而且兩個必須來自同一種情緒且有遞進關係。
鬧彆扭天生是否認+轉移兩個動作,只准一個等於永遠只做得到一半。
被擋下的形狀是「兩種情緒各演一個」。
鬧彆扭的階梯與女性預設(G3)
- modestyDirective 分三級:41-59 一句帶過、60-74 嘴硬(否認 → 反駁 → 音量掉下來)、
75+ 惱羞(否認 → 反駁 → 翻臉)。惱羞是換情緒,所以仍然是兩個動作。
- GENDER_MODESTY_DEFAULT.female 62 → 70;MODESTY_SIGNALS 補傲嬌那一路
(傲嬌、嘴硬、不坦率、口是心非、死不承認、愛面子、鬧彆扭、逞強)。
界線不變:描述永遠蓋過預設,可以推到 0。
文件:README 加一節「文字通道的另外兩格」與鬧彆扭階梯表、emotions.md 加 emoji 章節、
persona-chat SKILL 與 anti-ai-voice 跟上。selftest 616 → 641 全綠。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 07:46:34 +00:00 |
|
 jiantw83andClaude Opus 5
|
547cd2bd76
|
chore(TARGET): 標記暫停實作,解除條件寫在清單裡
另一個 session 正在 feat/persona-anime-expression 上改 persona-lib.mjs、
persona.mjs 與 selftest.mjs,做的是同一批 G2/G3。兩邊同時改渲染與
speechLint() 會重複實作 5.7/5.8 並互相覆蓋,所以先暫停十二項的實作,
只准改 TARGET.md 本身。
解除條件寫死在檔案裡(該分支合進 develop、先逐項對照勾掉已完成的再繼續),
因為每天 00:00 的排程是非互動環境,沒有人在旁邊講這件事——寫在 log 或
對話裡都到不了那一跑的眼前,只有 TARGET.md 到得了。
順便講明排程跑到這段該做什麼:停下來寫 log,不要繞過階段 A1 的未提交變更檢查。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 07:43:41 +00:00 |
|
 jiantw83andClaude Opus 5
|
dccd07a9b8
|
docs(專案目標): 三條長期目標從 README 搬進 TARGET.md
目標跟「還沒做完的那幾項」分開放會對不起來——G2/G3 的界線寫在 README、
實作清單在別的地方,改東西的時候只會看到一邊。搬成同一份。
README 的〈專案目標〉留一段指路:三條目標各一句話 + 指向 TARGET.md。
原本那句「拍板紀錄與實作清單留在 PR #17 的討論裡」一併移除,
清單現在就在版控裡,不必再繞 PR。
TARGET.md 檔頭改成兩段:〈專案目標〉收 README 搬過來的完整表(前提、意思、
界線三欄原樣),〈這份清單〉說明來源與 Q5-Q10 已拍板。原本自己抄的六條界線
表拿掉——那六條除了 Q8 的「強度全域一致」之外,全都已經在搬過來的界線欄裡,
留著等於同一條規則寫兩遍,日後只會改一邊。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 07:38:30 +00:00 |
|
 jiantw83andClaude Opus 5
|
d9013c6121
|
chore(TARGET): 建立情緒與語氣擬真優化的每日待辦清單
對著 README〈專案目標〉的 G1/G2/G3 做,收 PR #17 所指「TODO 階段 5」
八節(5.1-5.8)+階段 4 剩餘四項,共 12 個項目。
內容全部取自 9d76984^ 的 TODO_人格優化.md 與 PR #17 的 Q5-Q10 拍板紀錄,
沒有新增設計決策。原 TODO 已移出版控,所以把動手前的六條界線
(硬規則 8 優先、動作只能進括號、emoji 是加上去的、不靠隨機充人味、
性別只給預設值、強度全域一致)抄進檔頭,讓非互動排程也讀得到。
排序按相依性:5.7 括號格式是 5.8 emoji 與 5.4 動作額度的基礎,所以排最前面;
口語指紋緊接 5.6 語域,因為兩者共用同一格資料,不能各存一份。
.gitignore 的 /TODO_*.md 不匹配 TARGET.md,本檔進版控無須改 ignore。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 07:34:50 +00:00 |
|
 jiantw83andClaude Opus 5
|
9d76984c27
|
chore(TODO): 兩份 TODO 清單移出版控並刪除
工作用的清單留在本機就好,.gitignore 加一條 /TODO_*.md 擋掉以後再進來。
內容沒有消失:兩份的全文在 2aa0dad 與 c0e1c48 的歷史裡查得到,
今天拍板的六題(Q5-Q10)另外抄在 PR #17 的討論裡。
README 指向 TODO 的那句改成指 PR,並註明 G2/G3 那批還沒實作——
清單刪掉不等於事情做完了,這一句不能省。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 07:20:25 +00:00 |
|
 jiantw83andClaude Opus 5
|
b5c540feb1
|
fix(記憶固化): 判斷過的短期記憶留痕跡,候選數字才會降
睡眠有做固化,但隔天開機照樣提醒「N 組已達固化條件」,數字只會往上爬
(實測 KIRITO-01:短期記憶 120 筆、候選 222 組)。原因不是睡眠沒做事,
是判斷沒有留下痕跡——promotionCandidates() 無條件掃全部短期記憶,而
consolidate 不在來源那筆上寫任何東西,也不刪它(--forget 是按顯著度刪,
不是按固化過沒有刪),所以同一批每輪都被重算成候選。
- 短期記憶多兩個欄位:reviewed_at(看過、判斷過了)與 promoted_to
(固化成了哪一則)。兩者分開記——「看過決定不記」跟「已經記下來」都不該
再進候選,但事後要查「這則長期記憶從哪幾筆長出來」只能靠 promoted_to。
- promotionCandidates() 只看沒有 reviewed_at 的那些;total 照舊算全部。
R6 容量壓力仍看總筆數,但候選只從未判斷的挑,全部判斷完就不再出現
(否則它會單獨把提醒永遠點亮)。
- consolidate --from-short "#N,#N":寫長期記憶時一起標來源。
- candidates --reviewed all|<#N,#N> [--until <ISO>]:看過決定不記的標這裡。
--until 讓「某次睡眠當下判斷過的那批」可以一次收掉。
- candidates 輸出每筆前面加 #N(就是上面兩個旗標要填的編號),並在結尾
提示判斷完要標記。
- 標記不等於刪掉:那幾筆還在短期記憶裡,照舊被 prune 依天數與顯著度裁。
文件:persona-sleep 的流程多一步「標掉判斷過的」並註明漏掉的後果、
persona-memory 補一張「怎麼標」的表、README 的 R1–R6 段補這一層。
selftest 補 13 項(602 → 615 全綠)。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 07:12:50 +00:00 |
|
 jiantw83andClaude Opus 5
|
0917e905c7
|
chore(plugin 版本): 三份 manifest 升版 0.2.1
master 上是 0.2.0(#16 已合併),這批文件改動照規則 patch +1。
順便把根目錄的 plugin.json(Antigravity)拉齊——它從上一批就漏改,
還停在 0.1.1,master 上也是。三份現在都是 0.2.1。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 06:58:57 +00:00 |
|
 jiantw83andClaude Opus 5
|
c0e1c4828f
|
docs(故事匯入): 小說匯入的 TODO 進版控
前一批留在工作區沒進版控的那份。內容是 persona-story 的階段清單
與四題拍板結果(語氣檔放 voice/、機械進 CLI、時間軸換算西元日期、
情緒基線差 10 以上要人看),還有踩過一次的坑:劇情時間不可以寫進
first_seen/last_seen,那兩欄是記憶的新鮮度時鐘。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 06:56:43 +00:00 |
|
 jiantw83andClaude Opus 5
|
2aa0daddaf
|
docs(專案目標): 三條長期目標進 README,TODO 補階段 5 的文字通道
只有文字這一個通道,所以情緒表達是排版問題。三條目標寫成
G1(持續逼近真人)/G2(表達往動漫角色靠)/G3(女性人格的害羞與鬧彆扭),
每條附界線,新功能對著它們判斷。
TODO 加階段 5,六題已拍板(Q5–Q10):
- 顏文字開放,連帶要在半形標點檢查裡開白名單
- 動作加進名字後**既有**的括號(`桐人(😳・臉紅):`),情緒標註保留
- 生理反應算動作(臉紅、手在抖),`(害羞)` 只能待在情緒格
- 破口放寬到一輪兩個動作,煞車是必須同情緒且遞進
- emoji 表程度:種類表情緒、數量表程度,由 emotion.json 自動帶入
- 動漫化強度全域一致,內容仍逐人格
沒有任何程式改動,版號不動——0.2.0 還沒進 master,這批掛在同一個版號底下。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 06:56:34 +00:00 |
|
 jiantw83andClaude Opus 5
|
08ecb15615
|
docs(persona-story): 新技能與規則,正名表從章節掃出來給使用者確認
skills/persona-story:把小說變成既有人格的記憶。三條界線寫進流程——他不知道的事
不能變成他的 event、整本原文不入倉庫、SOUL.md 只有使用者能改。
reference/interview.md 是開場四題(**不問譯名版本**:那是要他猜,而他手上那批
寫的是什麼字,讀一次就知道);reference/extract.md 是一個場景要抽什麼與知情層級。
AGENTS 加第 18 條硬規則:匯入原作只能給他「他知道的事」。
README 補 persona-story 一節;persona-anime 補一句指路(先建人格再讀原文)。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 06:20:21 +00:00 |
|
 jiantw83andClaude Opus 5
|
d6bfe29590
|
test(selftest): 補 voice 與 novel 的案例,602 加到 665 項
涵蓋:voice 的寫入讀取與注入上限、novel 的欄位驗證(七種違規各一項)、
正名表對不上節點要擋、掃描的四條啟發式、去重合併與配額重定標、dry-run 不寫檔、
基線門檻雙向(差 10 擋、差 9 放行、--force 蓋過)。
另外釘住兩條界線:know_level 只收英文鍵且 none 不准配 event、
劇情日期落在 happened_at 而不是 first_seen(所以剛匯進來的普通記憶是清晰的,
不是出生即模糊)。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 06:20:21 +00:00 |
|
 jiantw83andClaude Opus 5
|
66ecd036ea
|
feat(故事匯入): voice 語氣層與 novel 機械指令,記憶不再被劇情日期洗掉
小說匯入分兩半(使用者拍板):機械的進 CLI,判斷的留給 skill。這批是機械那半。
voice/ 新一層:samples.md 收他自己講過的原句(附對象與場合)、reactions.md 收
「事件 → 他做了什麼」。匯入流程可以直接寫這一層,SOUL.md 維持只有使用者能改——
權限界線要有實體隔離,不靠自律。每輪最多注入 3 條,全注入會變成照抄舊台詞。
歸 Wiki 區(低頻設定,跟 IDENTITY/SOUL 同一區)。
novel 子指令:init/scan/name/skip/candidate/merge/write/report/baseline。
工作區在 memory/import/<work-slug>/,歸檔案區——收斂中換一台機器要接得下去。
正名表不請使用者手打:scan 從章節抽人名候選(對話歸屬、敬稱、片假名、高頻詞),
附出現次數與上下文,猜關係節點並分 exact/alias/fuzzy/unknown 四種信心度。
names.json 分 map 與 ignore 兩塊,已確認或已 ignore 的下次掃不再列。
視角界線擋在候選驗證:know_level 除了 canon 之外必填(did/saw/told/later/none),
none 是「他不在場也沒人告訴他」,只能配 canon。**擋下來而不是自動改成 canon**——
自動改會讓抽錯的人永遠不知道自己抽錯。這是整條流程唯一沒有第二道防線的錯。
劇情時間與記憶新鮮度分成兩組欄位(踩過一次才發現):first_seen/last_seen 是
記憶的新鮮度時鐘(memoryStrength 拿 last_seen 算衰減),happened_at/happened_until
才是劇情時間。混在一起的話 2024 年劇情的記憶匯進來的那一秒就是模糊 0%,而且掉到
0% 之後 touchRecall 不再更新它,永遠回不來——實測 40 到 79 分的 event 全部出生即死。
其他:情緒只固化進欄位,一次都不呼叫 applyEmotion(逐則重放等於讓最後一章決定他的
性格);他的原句接在內文後面而不是另開 front matter 欄位,這樣它跟細節一起衰減;
writeLongTermMemory 抽出來給 consolidate 與 novel write 共用,欄位順序與預設值只有一份。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 06:20:06 +00:00 |
|
admin
|
a90c1c3285
|
Merge pull request 'feat(persona 擬真): 記憶會糊掉、情緒有底色與疲勞、人格有懸著的事(0.2.0)' (#15) from feat/persona-realism-0.2.0 into develop
Reviewed-on: #15
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
|
2026-08-03 06:18:05 +00:00 |
|
 jiantw83andClaude Opus 5
|
59bbd2bc32
|
chore(plugin 版本): 兩份 manifest 升版 0.2.0
master 目前是 0.1.1,這一整批未合併的改動只佔一個新版號。
記憶檔案格式與 bundle 版本都變了,所以走 minor 不走 patch。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 04:22:38 +00:00 |
|
 jiantw83andClaude Opus 5
|
9ceb428a34
|
docs(規則與說明): 規則跟上擬真那批,建立人格時要問專屬破口
persona-chat:第 2 節的「查不到就不要講」改寫成三態界線(清晰照講/模糊可問不可斷言/
查不到不准講),第 3 節補模糊態怎麼講、試探怎麼記帳、懸著的事怎麼用、自我議程那一輪
長什麼樣。標題順手改成「五條鐵則」(本來就列了五條)。
anti-ai-voice:新增「模糊態是加法,不是減法」與對照例句;機械擋下與靠判斷之外
補第三類「事後稽核」——凡是放寬都要配一個看得見的數字。
emotions:情緒調節補抑制與慣性,新增「三層時間尺度」與 per-persona 破口的寫法。
persona-memory:遺忘原則整段重寫(門檻式改成三態),長期記憶檔案格式與固化範例跟上。
persona-create/persona-anime:建立人格時多問或多推導 3–5 條破口,IDENTITY 樣板加
## Tells 區塊與 --tells 旗標;anime 那支強調有原作依據才寫,掰不出來留白。
persona-sleep:補當日底色帶 35% 過去與 sweep-loops 兩步。
persona-transfer:bundle v2 的差異與自動 migration。
README 與 AGENTS:新增硬規則「長期記憶會糊掉,但不會不見」,補三個新狀態檔、
三個新指令、以及情緒的五道調節關。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 04:22:32 +00:00 |
|
 jiantw83andClaude Opus 5
|
6381aa7d45
|
test(selftest): 補擬真那批的案例,471 加到 602 項
九段新案例(㉓–㉛):疲勞、交互抑制與慣性、當日底色、open loops 與自我議程、
回想強度與模糊態、recall 情境加權、migration、probe 稽核、turnContext 不因新東西壞掉。
寫測試的過程抓出三個真缺陷,已在前一個 commit 修掉:
- resolveProbe 就地改欄位,但 rewriteJsonl 只在長度變了才寫檔 → 稽核計數永遠是 0,
指令回報成功而硬碟上什麼都沒發生。開放界線唯一的煞車是斷的。
- migrateLongTerm 用 \n---\n 切 front matter,CRLF 或沒有 front matter 的檔會被
整份複製一次(永久損毀),而 importBundle 會自動跑 migration,所以 import 就踩得到。
- recall 把總分乘上 0.5+0.5*retrievability,等於扣十幾分,會讓命中兩個關鍵詞但
三個月沒想起的正確答案掉到榜尾。改成上限 3.5 分的扣分,加權永遠翻不掉語意命中。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 04:22:16 +00:00 |
|
 jiantw83andClaude Opus 5
|
f9b189dd76
|
feat(persona 擬真): 記憶會糊掉、情緒有底色與疲勞、人格有懸著的事
三個結構性缺口,這批一次補完(使用者拍板做階段 1+2+3 加自我議程)。
記憶只有「精準」與「沒有」兩態 → 連續衰減出來的三態。
memoryStrength() 由 strength/salience/recall_count/距上次回想多久算 retrievability,
低於門檻不刪、降級成 faded(只剩主旨)與 fuzzy(只剩有這件事)。內文因此分兩層
(主旨/細節),衰減先吃細節。被 recall 命中就 strength +8(spacing effect)。
boundary/promise/canon/salience >= 80 永遠清晰,這條沒動。
人格永遠以對方為中心 → state/loops.json 加自我議程。
同時最多 5 條、四種(他沒回答的、他答應的、被打斷的、我想問的),7 天沒進展自動收掉
並留一則「沒下文」的短期記憶。agendaTick 每 3 輪最多讓人格把話題拉回自己的事一次,
對方有明確急事時一律不觸發。
同一句話任何時刻聽起來都一樣 → 疲勞、當日底色、per-persona 破口。
fatigueLevel() 由 hoursAwake 推,壓 arousal 天花板、句數與單句字數、高張情緒的推力;
state/mood.json 是緩慢漂移的當日底色,只當反應增益不直接改情緒值,睡覺帶 35% 過去;
EMOTION_TELLS 改成預設值,人格可在 IDENTITY.md 的 ## Tells 覆寫。
applyEmotion 另加交互抑制(只有三組互斥)與慣性(連續同向 +8%,封頂 +25%)。
幻覺界線改成「開放試探」(使用者明示):可以說不確定、可以問,不可以斷言。
換來的義務是稽核——每次試探進 state/probe.jsonl,probe audit 看否認率。
放寬界線一定要配一個看得見的數字。
其他:記憶蓋情境戳章(when/where/mood,where 可用 PERSONA_CONTEXT_WHERE=off 關掉);
recall 加情境加權但翻不掉語意命中;migrate 就地升格式(冪等、看不懂的檔跳過);
bundle v2,import 吃得下 v1 並自動補欄位。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 04:22:03 +00:00 |
|
 jiantw83andClaude Opus 5
|
6fa7e6e370
|
chore(plugin 版本): 三家 manifest 升版 0.1.1
master 已經在 0.1.0,這批 relation 的改動是新的未合併批次,佔一個新版號。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 03:17:58 +00:00 |
|
jiantw83
|
e59d0a471b
|
Merge remote-tracking branch 'origin/master' into develop
|
2026-08-03 03:17:32 +00:00 |
|
 jiantw83andClaude Opus 5
|
7b20dba6db
|
docs(skills): 規則跟上關係圖對接,新增親密度判斷參考
persona-chat:人名要對得上關係圖(--entities/--about);新人物當場進關係圖
改成硬規則,但屬於第 ⑥ 步記憶回寫,不佔第 ⑤ 步的回話句數,也不要宣告。
persona-relation:補「記憶怎麼指回節點」與 relation doctor 的四類報告
(同名歧義、解析不到的 id、孤兒節點、對不上的人名),並說明壞檔會以非零
exit 結束而不是裝成空圖。
新增 reference/closeness.md:初次建節點時 bond/closeness/trust 怎麼從
對話判斷——一輪之內查完就填,不要憑感覺,也不要為了安全全部填 15。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 03:17:29 +00:00 |
|
 jiantw83andClaude Opus 5
|
dd16411d2e
|
feat(relation): 記憶接回關係圖,並補上唯讀健檢 relation doctor
寫入時把 --entities/--about 的人名解析成節點 id(entity_ids/about_ids),
之後「提到誰」與 turnContext 認人才接得回關係圖;同名有多個候選視為歧義,
兩邊都不寫,不替使用者挑一個。
節點 id 會被原樣寫進 front matter 與 jsonl,所以兩頭都要防:帶換行的 id
可以在 front matter 裡多插一行、覆寫 type,把一則普通記憶變成不該被遺忘的
canon;`:` `[` `]` `,` `#` 也都會改變結構。injectSafeLine 把這些擋掉。
graph.json 壞掉時不再偽裝成空圖:要做決定的 action 直接以非零 exit 擋下
(放它過去等於拿一張空圖覆蓋原檔),relation doctor 自己會報告。
recall 用關係節點的 name 當關鍵詞,但刻意不用 id——id 是內部識別,命中率
高得離譜(關係圖有一個 id 為 user 的節點,就會命中每一則 about: [user]),
一個命中值 +10 會把顯著度 75 的正確答案擠出榜。about_ids 同理不進 haystack。
順手把關係圖改成一次讀完:原本每筆的每個 entity 各讀一遍 graph.json,
實測 30 節點/240 筆 = 720 次讀檔;2000 節點時單輪要 1.2 秒。
selftest 448 → 471 項,全過。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-08-03 03:17:21 +00:00 |
|
admin
|
50fd319c9e
|
Merge pull request 'docs(persona-chat): clarify auto-hook boundary' (#13) from pr/persona-master-sync-20260731 into master
Reviewed-on: #13
Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
|
2026-07-31 17:37:41 +00:00 |
|
jiantw83
|
e8d994df78
|
docs(persona): remove legacy marketplace namespace
|
2026-07-31 17:17:53 +00:00 |
|
jiantw83
|
6ea5076205
|
docs(persona-chat): clarify auto-hook boundary
|
2026-07-31 17:00:01 +00:00 |
|
 jiantw83andClaude Opus 5
|
34b1dc7942
|
fix(marketplace): marketplace 名由 jsc-plugins 改為 persona(與 template 撞名)
兩份 marketplace.json 原本都叫 jsc-plugins,與 jsc-template 的 marketplace 同名
——兩個 plugin 同時安裝時會撞名。template 已在 731a494 改為 template(=它的 repo
名),本 repo 跟著改為 persona,也與 code/doc/generic「marketplace 名 = repo 名」
的慣例一致。plugin 名維持 jsc-persona 不動。
README 的安裝 token、marketplace update/upgrade/remove 指令、目錄樹註解與
「marketplace 名是 …」那句一併更新(Claude/Codex/Copilot 三節各一組)。
依 spec-plugin-version,安裝識別鍵是 <plugin 名>@<marketplace 名>,改名等於換識別
鍵,舊安裝不能用 update 遷移,必須先移除再安裝,中間 skills 會短暫消失。照
template 9b2e2bb 的做法在安裝章節開頭補上三家各自的升級路徑,並交代要清掉
settings.json 的 enabledPlugins 舊鍵、人格倉庫(PERSONA_HOME)不在 plugin 目錄裡
所以情緒與記憶不受影響。
版號維持 0.1.0 不動:spec 的「更名重置版號」指的是 plugin 更名(name 欄位改變),
本次三份 plugin.json 的 name 與 version 都沒動,marketplace.json 也沒有版本欄位;
template 當時的 marketplace 改名(731a494)同樣沒有重置,照常從 0.0.2 走。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-07-31 14:43:58 +00:00 |
|
 jiantw83andClaude Opus 5
|
ed80158d99
|
chore(plugin 版本): 三家 manifest 升版 0.1.0
master 現行 0.0.9,patch 已到 9,依 spec-plugin-version 進位 minor 並把
patch 歸零(0.0.9 → 0.1.0),非 0.0.10。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-07-31 14:38:39 +00:00 |
|
 jiantw83andClaude Opus 5
|
bc571cf37f
|
fix(data): 四處情緒/sync 狀態的讀改寫還沒進鎖,等於繞過了上次的修正
B4 加了 updateJson/updateEmotion,但這幾個呼叫點還是「loadEmotion → 改 →
writeJson」的裸讀改寫,鎖形同不存在:
- sleep 的 emotion-decay、remember --emotion(每輪對話的熱路徑)、
Stop hook 與 SessionEnd hook 的時間衰減,四處都改走 pl.updateEmotion()。
衰減與 delta 的算法一個字都沒動,只是把讀與寫收進同一把鎖裡。
- sync.json 同理:背景 sync push 跟前景指令會同時寫它,兩邊撞上時後寫的會把
pushed_at/overwrites 整段蓋掉——覆蓋紀錄就這樣安靜地消失。新增
updateSyncState()(帶鎖),六處 loadSyncState + saveSyncState 成對使用全部
改過去;補預設欄位的邏輯抽成 normalizeSyncState() 給兩邊共用。
saveSyncState 沒有呼叫端了,直接移掉,免得下次又有人拿它裸寫。
sync.json 的欄位格式與 overwrites 留 10 筆的行為都沒變。
selftest 448 項(+1):新增「並行 8 次 remember --emotion 跟循序結果一樣」,
把對話熱路徑的情緒寫入也蓋進競態測試——原本只蓋到 emotion --apply。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-07-31 14:35:06 +00:00 |
|
jiantw83
|
fa4aa8b787
|
merge(gitea): 同步漏檔/push 衝突回報/clone 新功能/錯誤訊息
衝突:scripts/selftest.mjs 檔尾兩份都各自 append 一個測試區塊
(guard 的「注入區塊不可被人格檔案逸出(S6)」與 gitea 的「㉒ 從 Gitea
匯入本機還沒有的人格」)。兩塊都保留,各自收好自己的 `}`。
|
2026-07-31 14:29:10 +00:00 |
|
jiantw83
|
9b5de053e3
|
merge(data): 並行鎖/consolidate 保護/情緒校驗/speechLint 誤殺修正
|
2026-07-31 14:27:26 +00:00 |
|
jiantw83
|
0a12321df6
|
merge(guard): 資安三項修正(S1 sleeper pin/S2 Glob-Grep 破口/S6 注入區與 room 偽造)
|
2026-07-31 14:27:20 +00:00 |
|
 jiantw83andClaude Opus 5
|
ca4becbaba
|
fix(gitea): clone 失敗訊息不要被 git 的多行 stderr 截斷成半個字
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-07-31 09:38:17 +00:00 |
|
 jiantw83andClaude Opus 5
|
2b4b49d478
|
feat(gitea): clone —— 從 Gitea 匯入一個本機還沒有的人格
底層本來就走得通(`pullArea` 的 restore 會把本機缺少的檔案全部補進來),
擋住的是上層的雞生蛋:`sync` 先走 `requireOwner`,而 `requireOwner` 第一件事
就是「本機沒有這個人格就 die」。本機沒有它 → load 不了它 → sync pull 被擋 →
永遠拉不回來。所以照 `import` 的模式另開一個只驗 session、不驗 host 的入口。
* `clone --code <編號>`:兩區都拉回來(Wiki 區給身分與長期記憶,檔案區給活狀態),
然後補上 `pullArea` 不管的那幾件事——驗 IDENTITY.md(`validateBundle` 明文的
人格最低要件,Gitea 這條路上原本不存在)、補寫 config.code/來歷、
`rebuildIndex()`、`renderRelations()`。拉回來不成人格就中止並清掉半成品。
* `clone`(不帶 --code):列出遠端有哪些人格、哪些本機還沒有。
整個 codebase 原本沒有任何「列出 owner 底下的存取庫」的呼叫,新增
`listRemotePersonas()`:分頁打 `GET /user/repos`(他人/組織走 `/users/<owner>/repos`),
用編號格式過濾——存取庫名稱就是人格編號,所以那份清單就是遠端的人格清單。
* 本機已有同名人格時**預設不覆蓋**;`--force` 才蓋(沿用 `import` 的兩道保護:
不得覆寫別人、不得覆寫正被其他程序載入的人格),`--persona` 可並存兩份。
* 加進 `OWNER_EXEMPT_SUBCOMMANDS`,否則已載入其他人格時會被 hook deny。
* 編號衝突:`nextCode()` 只掃本機,換機器會重複發號。新增
`nextCodeAcrossMachines()`,發號前先問遠端已經用掉哪些編號;Gitea 連不上
就退回本機答案並在輸出明講「只對過本機」。`create` 與 `code assign/next` 都改用它。
* 順手修正 `ensureRepo` 的建庫路由:`me` 取自 `resolveOwner()`,而它在有
`PERSONA_GITEA_OWNER` 時只會把那個值原封不動還回來,於是組織永遠走成
`/user/repos`(建到 token 本人底下)。改用不受該環境變數影響的 `giteaLogin()`。
測試:selftest 新增第 ㉒ 區,用 file:// 的裸倉庫當「假的 Gitea」跑完整往返
(推兩區 → 刪掉本機人格 → clone 回來 → 驗身分/長期記憶/活狀態/索引/關係圖),
並涵蓋前兩個修正(子資料夾與非 ASCII 檔名真的進了存取庫、push 覆蓋遠端的回報
與 Stop hook 只吵一次)。345 → 373 項全過。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-07-31 09:35:29 +00:00 |
|
 jiantw83andClaude Opus 5
|
5d27051293
|
fix(gitea): push 撞到別台機器時是本機贏,但不能靜靜地贏
`pushArea` 遇到 non-fast-forward 時會 `git reset --hard origin/<branch>`、
把本機工作副本重新疊上去再推一次,然後回 `{ok:true, changed:true}`——
沒有任何衝突訊號。而 Stop hook 每一輪都在背景 push,所以兩台機器同時聊同一個
人格時,對方的 emotion.json/short-term.jsonl 會被靜默取代,誰都不知道。
「本機工作副本是這台機器的真相來源」這個設計選擇保留,但那條路徑現在要記帳:
* 算出「對方在分歧後改過、而我們正要蓋掉」的檔案交集,連同覆蓋前的遠端 sha
一起回傳 `overwrote`,並寫進 state/sync.json(留最近 10 筆)。
* `sync push` 一律往 stderr 寫一行警告(--quiet 也寫,背景 push 才有痕跡),
正常輸出與 `sync status` 都列得出「蓋掉幾個檔案、上一版是誰」。
* 背景 push 是 detached、輸出丟掉的,所以由 Stop hook 認領未回報的紀錄,
講給使用者聽一次(劇場模式也照講——那是資料被蓋掉)。
* commit 訊息也寫進「覆蓋 N 個檔案,上一版 <sha>」,被蓋掉的內容仍可用
`git -C <人格>/.sync/<區> show <sha>:<檔案>` 取回。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-07-31 09:34:29 +00:00 |
|
 jiantw83andClaude Opus 5
|
b937c9ad7a
|
fix(gitea): 同步會漏掉子資料夾與非 ASCII 檔名的檔案
兩個各自獨立、但都是「檔案在本機有、在存取庫沒有/拉不回來」的靜默失真:
1. `listAreaFiles` 只收資料夾第一層的 `entry.isFile()`,所以
`mindmap/threads/archive/`(睡眠時把太久沒動的思維導圖收進去的地方)
從來沒有被同步過。改成遞迴。
2. `git ls-files` / `status --porcelain` / `diff --name-only` 預設會把
非 ASCII 路徑引號跳脫成 `"Memory-\350\267\250…"`。長期記憶的檔名正好是中文的,
於是這些檔名跟工作副本比對不上:pull 時被當成「clone 裡沒有的東西」整批略過
(拉回一個沒有長期記憶的人格),本機刪掉的也不會從遠端消失。
一律改用 `-z`,並把三處解析收斂成 `diffPaths()` / `statusPaths()`。
順便修掉 porcelain 第一筆因為整體 trim 而少一個字元的解析錯誤。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-07-31 09:32:49 +00:00 |
|
jiantw83
|
186de10179
|
fix(security): 人格檔案不能逸出注入區塊、room 台詞不能偽造成系統訊息(S6)
注入到上下文的東西夾在 persona-runtime / persona-context / persona-ops 三種區塊
中間,而夾進去的內容有**不可信來源**:persona-anime 從 Fandom 抓設定寫進
IDENTITY/AGENTS、`sync pull` 從另一台機器拉、`import` 吃外部 bundle、
guest 的 room 台詞是別的人格寫的。原本這些地方**沒有任何跳脫**:
- AGENTS.md 裡放一行結束標記 → opsBrief 的區塊提早關閉,後面的內容跑到區塊外,
連外層的 runtime 區塊都能一起關掉。
- IDENTITY.md 的 `Vibe:` 欄位值同理,經 identityBrief 進 turnContext。
- room 台詞塞換行 → roomScript 是一行一句「emoji 名字(情緒):內容」,
於是可以偽造成別人的台詞或系統訊息。
修法:
- 新增 `stripInjectionMarkers()`:把 `<persona-…` 的 `<` 換成全形。內容還讀得懂
(人格自己寫的說明不會被吃掉),但它不再是一個標籤。
- 新增 `injectSafeLine()`:中和標記 + 換行壓成空白(比照短期記憶的作法)。
- 一個收口勝過十幾個防點:`turnContext()` 與 SessionStart 的 runtime 區塊都改成
**組完之後對整個內文**做一次,再補上真正的標記;只有 turnContext/opsBrief
這種自己已處理過、帶合法巢狀標記的整塊原樣保留。
- 讀出來就中和的:`opsBrief()` 的 AGENTS.md 全文(先截斷再中和,長度上限才算得準)、
`identityFields()` 的欄位值(identityBrief/roomScript/roomDisplayName 全吃這一份)、
`relationsBrief()` 的人名與備註。
- room:`roomPost()` 在**寫入端**就把 text/emotion/barge_in 壓成一行,
`roomScript()` 與 `room read` 在**顯示端**再壓一次(舊逐字稿是原文寫進去的)。
測試:新增 14 項——AGENTS.md 與 IDENTITY 欄位的逸出、turnContext 與 SessionStart
的區塊只被關閉一次、短期記憶與關係圖人名走同一個收口、room 台詞的換行偽造與
標記逸出、舊逐字稿的顯示端防線、顯示名不夾帶標記。
反向驗證:把兩個中和函式改成 identity,這 14 項全數失敗。
368 → 383 項全過。
|
2026-07-31 09:31:41 +00:00 |
|
 jiantw83andClaude Opus 5
|
197ad3416f
|
fix(speech): 修掉三個誤殺——書名號、變體選擇子 emoji、引述算進句長
三個都是「該放行卻被擋」,而且都只有幾行:
* speechBody() 的「提及 ≠ 使用」豁免清單漏了《》:
「我在看《說到底》這本書」被判 preach,「他推薦我看《質量與信息》」被判中國用語。
* EMOJI_RE 把 \u{FE0F} 寫在字元類裡,`❤️`(U+2764 + VS16)被算成兩個 emoji 而誤擋;
ZWJ 家族(👨👩👦)算三個、膚色(👍🏽)算兩個。改成「底字 + 變體/膚色 + ZWJ 續接」算一個。
* 句長檢查用的是 sentence 而不是 body,引號內容與 `code` 都算進字數——
引述使用者原話必被擋,等於不准引述。
「質量」「信息」這類語境相依的中國用語誤判沒有動(那要語意判斷,成本不對)。
selftest +11:SNF 放行 7 條(書名號兩種、三種 emoji 組合、引述長原話與長 code),
SF 仍擋 4 條(書名號之外真的在說教、引號之外自己講的長句、兩個 emoji、
一個 emoji 加一個帶變體選擇子的)——豁免只針對提及與引述,不是整句放行。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-07-31 09:29:53 +00:00 |
|
 jiantw83andClaude Opus 5
|
94a86c9703
|
fix(emotion): loadEmotion 驗證檔案內容,衰減一律 clamp 且半衰期必須為正
emotion.json 是外部輸入(import/sync pull/手改都會進到這裡),但 loadEmotion()
用 `??=` 只補缺、不驗合法性,decayEmotion() 完全不 clamp。只要一份不合法的檔案:
* baseline.joy = 5000 → decay 後 joy = 4378.79,mood 卡在 valence 100 / arousal 100
* levels.anger = "很生氣" → decay 後 anger = NaN,注入的上下文印出 valence NaN
* half_life_minutes.joy ≤ 0 → factor 恆為 0,那個情緒從此累積不起來(連三次 +30 結果不變)
CLI 的 --baseline 與 --apply 本來就有保護,破口純粹在檔案入口,所以擋在 loadEmotion():
levels/baseline clamp 到 0–100、非有限數字退回預設、half_life 非正數退回該情緒的預設值。
decayEmotion/decayEmotionBy 抽出共用的 applyDecay(),同樣重驗一次並 clamp 結果——
它們是 export 的,呼叫端有可能餵手寫或匯入來的狀態,不能假設一定經過 loadEmotion()。
selftest +9:baseline 超界被 clamp、非數字不變 NaN、上下文不印 NaN、
四種壞掉的半衰期都退回預設、壞半衰期下情緒仍累積得起來、decayEmotionBy 同樣擋得住。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
2026-07-31 09:26:47 +00:00 |
|