feat(role): 情感判準改依角色設定、加入輕微忌妒與單一載入實例限制
一、情感表現判準從性別改為角色設定 使用者原本希望「女性角色預設具備愛/喜歡/害羞/撒嬌/鬧彆扭/忌妒」, 討論後改採角色的 nature/vibe 為判準,理由是以性別預設會與剛寫入的 「鬧彆扭需氛圍適合」規則矛盾,也會讓同性別角色表現趨同而失去差異。 - 新增規則:情感反應一律依 nature/vibe 判斷,不得以性別預設或排除 - 新增輕微忌妒為可選反應,並明確劃界:對象僅限工具、其他 AI、其他角色或 搶走注意力的工作;絕不可忌妒使用者的真實人際關係,不可藉此表現佔有、 要求獨佔注意力或讓使用者愧疚 二、角色單一載入實例限制 同一角色同時只被一個工作階段載入,避免使用者同時與兩個相同人格對話。 - role_lib.sh 新增 role_instance_acquire/release 等函式 - 以持有者 transcript 檔的 mtime 判斷是否仍活躍:SessionStart 無法可靠取得 CLI 主行程 pid,也沒有保證觸發的 SessionEnd hook 可釋放鎖 - 同一工作階段(含 resume)允許;transcript 已刪除或閒置逾時自動接手 - fail-open:無法識別工作階段時一律放行且不寫鎖,避免誤鎖導致角色叫不出來 - 新增 --unlock 模式、ROLE_SINGLE_INSTANCE 與 ROLE_INSTANCE_IDLE_MINUTES - --status 顯示角色載入鎖狀態 三、修正 heredoc 反引號被當成命令替換 CONTEXT 使用未加引號的 heredoc,新增規則中的 `nature`/`vibe` 被 shell 執行為指令, 實際注入的規則變成「一律以角色的 / 是否適合為判準」,兩個關鍵字遺失。 已轉義並複查全部 heredoc(CONTEXT/BUSY/SLEEP/DIALOG)確認無未轉義反引號。 版號 0.0.6 → 0.0.7 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -242,6 +242,87 @@ role_lock_release() {
|
||||
rm -rf "$(role_memory_home)/$1/.lock" 2>/dev/null
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 角色單一載入實例
|
||||
#
|
||||
# 目的:同一角色同時只被一個工作階段載入,避免使用者同時與兩個相同人格對話。
|
||||
#
|
||||
# 為什麼以 transcript 檔的 mtime 判斷而非 pid:SessionStart hook 無法可靠取得 CLI 主行程
|
||||
# 的 pid,且沒有保證會觸發的 SessionEnd hook 可用來釋放鎖。活躍的工作階段會持續寫入
|
||||
# transcript,因此「該檔多久沒被寫入」是最貼近真實狀態、也不需要清理程序的判斷依據。
|
||||
#
|
||||
# 設計原則:**寧可誤放行也不要誤鎖** —— 誤鎖的後果是使用者叫不出角色,比偶爾重複載入嚴重。
|
||||
# 因此無法識別工作階段(例如 hook 未提供 transcript 路徑)時一律放行。
|
||||
# ------------------------------------------------------------------------------
|
||||
role_single_instance_enabled() {
|
||||
case "${ROLE_SINGLE_INSTANCE:-1}" in
|
||||
0|false|no|off) return 1 ;;
|
||||
*) return 0 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
role_instance_idle_minutes() { printf '%s' "${ROLE_INSTANCE_IDLE_MINUTES:-30}"; }
|
||||
|
||||
role_instance_lock_path() { printf '%s/%s.lock' "$(role_home)" "$1"; }
|
||||
|
||||
role_instance_lock_field() {
|
||||
# 從鎖檔取出指定欄位
|
||||
local lock="$1" key="$2"
|
||||
[ -f "$lock" ] || return 1
|
||||
sed -n "s/^${key}=//p" "$lock" 2>/dev/null | head -n 1
|
||||
}
|
||||
|
||||
role_instance_write_lock() {
|
||||
local role="$1" transcript="$2" cwd="$3" lock
|
||||
lock="$(role_instance_lock_path "$role")"
|
||||
mkdir -p "$(dirname "$lock")" 2>/dev/null
|
||||
{
|
||||
printf 'transcript=%s\n' "$transcript"
|
||||
printf 'loaded=%s\n' "$(role_now)"
|
||||
printf 'cwd=%s\n' "$cwd"
|
||||
} > "$lock" 2>/dev/null
|
||||
}
|
||||
|
||||
# 回傳 0=可載入(已取得或接手鎖);1=已被其他仍活躍的工作階段持有
|
||||
role_instance_acquire() {
|
||||
local role="$1" transcript="$2" cwd="$3" lock holder idle
|
||||
role_single_instance_enabled || return 0
|
||||
# 無法識別工作階段就放行,不寫鎖:寧可重複也不要把角色鎖死
|
||||
[ -n "$transcript" ] || return 0
|
||||
|
||||
lock="$(role_instance_lock_path "$role")"
|
||||
if [ ! -f "$lock" ]; then
|
||||
role_instance_write_lock "$role" "$transcript" "$cwd"
|
||||
return 0
|
||||
fi
|
||||
|
||||
holder="$(role_instance_lock_field "$lock" transcript)"
|
||||
if [ -z "$holder" ] || [ "$holder" = "$transcript" ]; then
|
||||
# 同一個工作階段(含 resume 後重新載入)或鎖檔損壞:更新後放行
|
||||
role_instance_write_lock "$role" "$transcript" "$cwd"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ ! -f "$holder" ]; then
|
||||
role_log "INF" "前一個工作階段的 transcript 已不存在,接手角色鎖"
|
||||
role_instance_write_lock "$role" "$transcript" "$cwd"
|
||||
return 0
|
||||
fi
|
||||
|
||||
idle="$(find "$holder" -maxdepth 0 -mmin "+$(role_instance_idle_minutes)" 2>/dev/null)"
|
||||
if [ -n "$idle" ]; then
|
||||
role_log "INF" "前一個工作階段已閒置超過 $(role_instance_idle_minutes) 分鐘,接手角色鎖"
|
||||
role_instance_write_lock "$role" "$transcript" "$cwd"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
role_instance_release() {
|
||||
rm -f "$(role_instance_lock_path "$1")" 2>/dev/null
|
||||
}
|
||||
|
||||
role_project_name() {
|
||||
# 專案判定:git remote 的 <owner>/<repo> 優先,其次目錄名
|
||||
local cwd="$1" origin cleaned owner_repo
|
||||
|
||||
Reference in New Issue
Block a user