feat(role): 新增點名載入,對話中叫名字即可切換角色
- `UserPromptSubmit` hook(`role_call.sh`)比對訊息開頭的角色名稱/ID/`aliases` 別名, 命中即注入該角色人格與記憶並接手本輪;未點名時不輸出內容也不寫檔案。 - 抽出 `role_context.sh` 共用 context 組裝,SessionStart 與點名載入共用同一份人格與操作規則, 避免兩種載入方式漂移;實測 SessionStart 注入內容與改動前 byte-identical。 - 新增階段角色狀態(`~/.roles/.sessions/<工作階段>.role`),`Stop` hook 改以「本階段實際角色」 寫記憶,避免點名換人後把跟 A 的對話記進 B 的記憶。 - `SessionEnd` 改為釋放本階段名下所有角色鎖並清掉階段狀態檔。 - 切換時先確認取得目標角色鎖才釋放原角色鎖,避免出現兩個角色都沒有的空窗。 - 睡眠時段、目標角色已被其他活躍階段佔用、點名的是已在場的角色時,一律不切換。 - 新增 `ROLE_CALL_ENABLED`(總開關)與 `ROLE_CALL_MARKER_ONLY`(只認 `@名字`)。 - 順帶修正 `role_capture.sh` 的 `--postcompact` 分支在 `PROJECT` 賦值前就使用它, 導致壓縮摘要記憶的 `sources` 一直為空。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
# 等待睡眠時段做完整 NREM/REM 整理。睡眠時段雖不載入角色,對話仍照常記錄。
|
||||
# 另支援 --precompact/--postcompact:對話壓縮會讓尚未寫入記憶的內容蒸發,
|
||||
# 壓縮前強制記錄一次(跳過長度門檻),壓縮後把系統產生的摘要也存成記憶。
|
||||
# 更新時間:2026/07/28 16:18:00
|
||||
# 更新時間:2026/07/29 13:25:00
|
||||
# 相依:bash、node、任一 headless CLI、同目錄的 role_lib.sh/memory.js/transcript.js。
|
||||
# 機密:濃縮提示詞明令不得輸出憑證與個資,寫檔前再以 transcript.js redact 遮蔽一次。
|
||||
# 退出碼:一律 0 —— hook 絕不可阻斷使用者流程。
|
||||
@@ -25,13 +25,14 @@ case "${1:-}" in
|
||||
esac
|
||||
|
||||
role_is_child && exit 0
|
||||
role_enabled || exit 0
|
||||
# 角色不在此判斷:本階段實際是誰要等解析出 session_id 才知道(可能已被點名換人),
|
||||
# 因此這裡只做「總開關關閉」與「完全沒有角色目錄」的廉價退出
|
||||
case "${ROLE_ENABLED:-}" in
|
||||
0|false|no) exit 0 ;;
|
||||
esac
|
||||
[ -d "$(role_home)" ] || exit 0
|
||||
command -v node >/dev/null 2>&1 || role_quit "找不到 node,略過記憶記錄" "WRN"
|
||||
|
||||
ROLE="$(role_resolve_name)"
|
||||
[ -n "$ROLE" ] || role_quit "未指定角色,略過記憶記錄"
|
||||
[ -f "$(role_file "$ROLE")" ] || role_quit "找不到角色定義檔,略過記憶記錄" "WRN"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 讀取 hook 傳入的 JSON(session_id/transcript_path/cwd/stop_hook_active)
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -57,6 +58,25 @@ process.stdin.on("end", () => {
|
||||
')
|
||||
EOF_HOOK
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 記憶要記給誰:以本階段實際角色為準
|
||||
#
|
||||
# 使用者可能在對話中點名換人(role_call.sh),此時 ROLE_NAME/.active 指的還是原角色,
|
||||
# 照它寫就會把跟 A 的對話記進 B 的記憶。狀態檔取不到時(未點名過、或 SessionStart
|
||||
# 因睡眠/佔用而未載入人格)才退回靜態解析,維持「不載入人格但仍記錄記憶」的既有行為。
|
||||
# ------------------------------------------------------------------------------
|
||||
ROLE="$(role_session_get "$(role_session_key "$SESSION_ID" "$TRANSCRIPT_PATH")" 2>/dev/null || printf '')"
|
||||
if [ -n "$ROLE" ]; then
|
||||
role_log "DBG" "本階段實際角色為 ${ROLE}(依階段狀態檔)"
|
||||
else
|
||||
ROLE="$(role_resolve_name)"
|
||||
fi
|
||||
[ -n "$ROLE" ] || role_quit "未指定角色,略過記憶記錄"
|
||||
[ -f "$(role_file "$ROLE")" ] || role_quit "找不到角色定義檔,略過記憶記錄" "WRN"
|
||||
|
||||
# 專案判定要在壓縮分支之前算好:壓縮摘要也要標記專案,之後才有辦法回溯它屬於哪份工作
|
||||
PROJECT="$(role_project_name "$HOOK_CWD")"
|
||||
|
||||
if [ "$CAPTURE_MODE" = "postcompact" ]; then
|
||||
# 壓縮後:系統已產生一份摘要,直接保存比自己再濃縮一次划算且免費。
|
||||
# 欄位名以容錯方式取用(實測 binary 內出現 compactSummary/isCompactSummary),
|
||||
@@ -99,7 +119,6 @@ fi
|
||||
|
||||
[ "$STOP_ACTIVE" = "1" ] && [ "$CAPTURE_MODE" = "turn" ] && role_quit "stop_hook_active 為 true,避免迴圈不重複記錄"
|
||||
role_in_scope "$HOOK_CWD" || role_quit "cwd 不在 ROLE_SCOPE 範圍內:${HOOK_CWD}"
|
||||
PROJECT="$(role_project_name "$HOOK_CWD")"
|
||||
node "${SCRIPT_DIR}/memory.js" mark-activity --role "$ROLE" --project "$PROJECT" >/dev/null 2>&1 || true
|
||||
|
||||
if [ ! -f "$TRANSCRIPT_PATH" ] && [ -n "${CODEX_THREAD_ID:-}" ]; then
|
||||
|
||||
Reference in New Issue
Block a user