feat(role): SessionEnd 立即釋放角色鎖,不必等閒置逾時 #21

Merged
admin merged 2 commits from develop into master 2026-07-29 04:49:18 +00:00
5 changed files with 108 additions and 8 deletions
Showing only changes of commit 3c0214a927 - Show all commits
+2 -1
View File
@@ -36,7 +36,7 @@ generic/
│ └── marketplace.json # Codex marketplacename: "generic"url source 指向本 repo │ └── marketplace.json # Codex marketplacename: "generic"url source 指向本 repo
├── plugin.json # Antigravity 外掛定義(name: "jsc-generic"skills: "./skills/" ├── plugin.json # Antigravity 外掛定義(name: "jsc-generic"skills: "./skills/"
├── hooks/ ├── hooks/
│ └── hooks.json # hook 定義(SessionStart 載入角色並提示問候、Stop 記錄記憶) │ └── hooks.json # hook 定義(SessionStart 載入角色並提示問候、Stop 記錄記憶、SessionEnd 釋放角色鎖
├── scripts/ ├── scripts/
│ └── role/ # role skill 的可執行元件(腳本一律不放進 skills/) │ └── role/ # role skill 的可執行元件(腳本一律不放進 skills/)
├── skills/ # ★ 唯一真實來源:所有 skills ├── skills/ # ★ 唯一真實來源:所有 skills
@@ -232,6 +232,7 @@ copilot plugin marketplace remove generic
| `skills/role` 的手動模式 | ✅ | ⚠️ 需保留 `scripts/` | ⚠️ 同左 | ❌ 只複製 `skills/` | ⚠️ 同左 | | `skills/role` 的手動模式 | ✅ | ⚠️ 需保留 `scripts/` | ⚠️ 同左 | ❌ 只複製 `skills/` | ⚠️ 同左 |
| `hooks/hooks.json``SessionStart` 載入角色 | ✅ | ⚠️ 需該版本支援 | ❌ | ❌ | ❌ | | `hooks/hooks.json``SessionStart` 載入角色 | ✅ | ⚠️ 需該版本支援 | ❌ | ❌ | ❌ |
| `hooks/hooks.json``Stop` 記錄記憶 | ✅ | ✅ | ❌ | ❌ | ❌ | | `hooks/hooks.json``Stop` 記錄記憶 | ✅ | ✅ | ❌ | ❌ | ❌ |
| `hooks/hooks.json``SessionEnd` 釋放角色鎖 | ✅ | ⚠️ 需該版本支援 | ❌ | ❌ | ❌ |
| cron 睡眠整理(系統排程) | ✅ | ✅ | ✅ | ✅ | ✅ | | cron 睡眠整理(系統排程) | ✅ | ✅ | ✅ | ✅ | ✅ |
> **OpenCode 以複製 `skills/` 目錄安裝**,不會帶入 `scripts/` 與 `hooks/`,凡依賴腳本的 skill 一律不可用。 > **OpenCode 以複製 `skills/` 目錄安裝**,不會帶入 `scripts/` 與 `hooks/`,凡依賴腳本的 skill 一律不可用。
+11
View File
@@ -11,6 +11,17 @@
] ]
} }
], ],
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "rel='scripts/role/role_unload.sh'; own='generic'; plug='jsc-generic'; root=\"${CLAUDE_PLUGIN_ROOT:-}\"; if [ -n \"$root\" ] && [ -f \"$root/$rel\" ]; then exec \"$root/$rel\"; fi; for base in \"$HOME/.claude/plugins/cache\" \"$HOME/.codex/plugins/cache\"; do for dir in \"$base/$own/$plug\" \"$base\"; do s=$(find \"$dir\" -path \"*/$plug/*/$rel\" -type f 2>/dev/null | sort -V | tail -n 1); if [ -n \"$s\" ]; then exec \"$s\"; fi; done; done; exit 0",
"timeout": 10
}
]
}
],
"Stop": [ "Stop": [
{ {
"hooks": [ "hooks": [
+13 -4
View File
@@ -3,7 +3,7 @@
# 用途:角色(role)系統的共用函式庫。提供統一 log、啟用判斷、角色解析、 # 用途:角色(role)系統的共用函式庫。提供統一 log、啟用判斷、角色解析、
# 睡眠時段判斷、AI 行程偵測、摘要 CLI 選擇與呼叫、記憶目錄鎖。 # 睡眠時段判斷、AI 行程偵測、摘要 CLI 選擇與呼叫、記憶目錄鎖。
# 本檔僅供 source,不可直接執行。 # 本檔僅供 source,不可直接執行。
# 更新時間:2026/07/28 12:21:11 # 更新時間:2026/07/29 12:55:00
# 相依:bash;摘要路徑需 README 定義的任一 headless CLI。 # 相依:bash;摘要路徑需 README 定義的任一 headless CLI。
# 機密:不 echo 任何 token;角色與記憶內容僅在程序記憶體與檔案間傳遞。 # 機密:不 echo 任何 token;角色與記憶內容僅在程序記憶體與檔案間傳遞。
# ============================================================================== # ==============================================================================
@@ -274,9 +274,18 @@ role_lock_release() {
# #
# 目的:同一角色同時只被一個工作階段載入,避免使用者同時與兩個相同人格對話。 # 目的:同一角色同時只被一個工作階段載入,避免使用者同時與兩個相同人格對話。
# #
# 為什麼以 transcript 檔的 mtime 判斷而非 pidSessionStart hook 無法可靠取得 CLI 主行程 # 釋放分兩條路,兩者缺一不可:
# 的 pid,且沒有保證會觸發的 SessionEnd hook 可用來釋放鎖。活躍的工作階段會持續寫入 # 1. 快速路徑:SessionEnd hookrole_unload.sh)在工作階段結束時刪掉自己的鎖,讓使用者
# transcript,因此「該檔多久沒被寫入」是最貼近真實狀態、也不需要清理程序的判斷依據 # 關掉 CLI 後可以立刻重開新階段叫回角色
# 2. 後援:以下的 mtime 閒置逾時接手。SessionEnd **不保證觸發**kill -9、直接關終端機
# 視窗、WSL 關機、當機都不會跑),少了它會在異常結束時把角色鎖死到下次手動解鎖。
#
# 為什麼後援以 transcript 檔的 mtime 判斷而非 pidSessionStart hook 無法可靠取得 CLI 主
# 行程的 pid。活躍的工作階段會持續寫入 transcript,因此「該檔多久沒被寫入」是最貼近真實
# 狀態、也不需要清理程序的判斷依據。
#
# 已知取捨:正常關閉才有快速路徑;異常結束仍需等 ROLE_INSTANCE_IDLE_MINUTES(預設 30 分鐘)
# 過期,或手動 role_sleep.sh --unlock。
# #
# 設計原則:**寧可誤放行也不要誤鎖** —— 誤鎖的後果是使用者叫不出角色,比偶爾重複載入嚴重。 # 設計原則:**寧可誤放行也不要誤鎖** —— 誤鎖的後果是使用者叫不出角色,比偶爾重複載入嚴重。
# 因此無法識別工作階段(例如 hook 未提供 transcript 路徑)時一律放行。 # 因此無法識別工作階段(例如 hook 未提供 transcript 路徑)時一律放行。
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
# ==============================================================================
# 用途:SessionEnd hook 主程式。工作階段結束時**盡力**釋放角色單一載入鎖,讓使用者
# 關掉 CLI 後可以立刻在新階段叫回同一個角色,不必等閒置逾時自然過期。
# 更新時間:2026/07/29 12:55:00
# 相依:bash、node(解析 hook 輸入)、同目錄的 role_lib.sh。
# 退出碼:一律 0 —— hook 絕不可阻斷 CLI 結束。
#
# 為什麼這只是「快速路徑」而非唯一解法:SessionEnd 不保證觸發(kill -9、直接關掉終端機
# 視窗、WSL 關機、當機都不會跑),因此 role_instance_acquire 的 mtime 閒置逾時接手仍是
# 最終保障,兩者缺一不可 —— 只留 SessionEnd 會在異常結束時把角色鎖死到下次手動解鎖。
#
# 為什麼一定要比對 transcript 才釋放:被鎖擋下的第二個工作階段也會觸發 SessionEnd,
# 若無條件刪鎖,它關閉時就會把「仍在使用中」的第一個階段的鎖一起刪掉,等於讓整個
# 單一實例限制形同虛設。只有鎖確實登記在自己名下時才釋放。
# ==============================================================================
ROLE_STAGE="role-unload"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./role_lib.sh
. "${SCRIPT_DIR}/role_lib.sh"
role_is_child && exit 0
role_enabled || exit 0
role_single_instance_enabled || exit 0
# sub agent 等非對話情境本來就不寫鎖,也就沒有鎖要釋放
role_skip_instance_lock && exit 0
command -v node >/dev/null 2>&1 || role_quit "找不到 node,略過角色鎖釋放" "WRN"
ROLE="$(role_resolve_name)"
[ -n "$ROLE" ] || role_quit "未指定角色,略過角色鎖釋放"
# ------------------------------------------------------------------------------
# 讀取 hook 輸入(transcript_pathreason
# ------------------------------------------------------------------------------
HOOK_INPUT="$(cat 2>/dev/null)"
[ -n "$HOOK_INPUT" ] || role_quit "hook 輸入為空,略過角色鎖釋放"
HOOK_FIELDS="$(printf '%s' "$HOOK_INPUT" | node -e '
let raw = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", (chunk) => { raw += chunk; });
process.stdin.on("end", () => {
let data = {};
try { data = JSON.parse(raw); } catch {}
process.stdout.write([
data.transcript_path || data.session_path || data.conversation_path || data.path || "",
data.reason || "",
].join("\n"));
});
' 2>/dev/null)"
HOOK_TRANSCRIPT="$(printf '%s' "$HOOK_FIELDS" | sed -n '1p')"
HOOK_REASON="$(printf '%s' "$HOOK_FIELDS" | sed -n '2p')"
# 無法識別工作階段就不動鎖:寧可讓它照原本的閒置逾時過期,也不要誤刪別人的鎖
[ -n "$HOOK_TRANSCRIPT" ] || role_quit "hook 未提供 transcript 路徑,略過角色鎖釋放"
LOCK_FILE="$(role_instance_lock_path "$ROLE")"
[ -f "$LOCK_FILE" ] || role_quit "角色 ${ROLE} 目前無鎖,無須釋放"
HOLDER="$(role_instance_lock_field "$LOCK_FILE" transcript)"
if [ "$HOLDER" != "$HOOK_TRANSCRIPT" ]; then
role_quit "角色鎖屬於其他工作階段,不釋放(持有者 ${HOLDER:-未知}"
fi
role_instance_release "$ROLE"
role_log "INF" "工作階段結束(原因 ${HOOK_REASON:-未提供}),已釋放角色鎖:${ROLE}"
exit 0
+14 -3
View File
@@ -1,6 +1,6 @@
--- ---
name: role name: role
description: 角色人格與長期記憶系統的建立與維護 skill。讓 CLI 工具以固定角色(namenature/vibe/簽名 emoji)回覆,並把每輪對話累積成長期記憶:搭配相容的 SessionStart hook 於啟動時依字元預算載入高價值記憶、Stop hook 先本地過濾再輕量記錄對話,睡眠時段(預設 22:00 至隔天 06:00)由排程整理記憶(NREM 鞏固:分類/去噪/去重/合併/優先度;REM 整合:跨記憶連結/抽象化/提取線索;再依 semanticepisodicproceduralemotionalpreferencerule 與 explicitimplicit 標記長期記憶型態,壓縮歸檔並適當遺忘)。提供 --new(新建或更新角色;可只給角色名稱,必要時詢問來源/作品並推斷 name/naturevibeemoji 四欄)、--use(以角色 ID 切換啟用角色)、--list(列出角色與 ID)、--export(匯出角色壓縮檔)、--sleep(立即整理)、--status--diagnose、--install-cron--remove-cron、--forget-preview、--brief(晨間狀態檢查)、--agent(匯出成 sub agent 供多角色協作)、--migrate(舊格式角色檔拆成身分與人格兩檔)等模式。當使用者說建立角色、新增人格、切換角色、匯出角色、備份角色、讓回覆更有特色、角色記憶、記憶整理、睡覺整理記憶、忘記舊記憶、角色沒有載入、hook 沒載入角色、晨間狀態檢查、早上主動回報狀態,或提到 .roles.memoryROLE_NAMEROLE_ENABLEDROLE_SLEEP_STARTROLE_MEMORY_HOMEROLE_LOAD_LIMITROLE_LOAD_INBOX_LIMITROLE_LOAD_DIALOG_TURNSROLE_CAPTURE_ENABLED 時觸發。不適用於:工作紀錄寫入 Gitea wiki(用 /jsc-doc:worklog)、專案文件化(用 /jsc-doc:funcs)。 description: 角色人格與長期記憶系統的建立與維護 skill。讓 CLI 工具以固定角色(namenature/vibe/簽名 emoji)回覆,並把每輪對話累積成長期記憶:搭配相容的 SessionStart hook 於啟動時依字元預算載入高價值記憶、Stop hook 先本地過濾再輕量記錄對話,睡眠時段(預設 22:00 至隔天 06:00)由排程整理記憶(NREM 鞏固:分類/去噪/去重/合併/優先度;REM 整合:跨記憶連結/抽象化/提取線索;再依 semanticepisodicproceduralemotionalpreferencerule 與 explicitimplicit 標記長期記憶型態,壓縮歸檔並適當遺忘)。提供 --new(新建或更新角色;可只給角色名稱,必要時詢問來源/作品並推斷 name/naturevibeemoji 四欄)、--use(以角色 ID 切換啟用角色)、--list(列出角色與 ID)、--export(匯出角色壓縮檔)、--sleep(立即整理)、--status--diagnose、--install-cron--remove-cron、--forget-preview、--brief(晨間狀態檢查)、--agent(匯出成 sub agent 供多角色協作)、--migrate(舊格式角色檔拆成身分與人格兩檔)等模式。當使用者說建立角色、新增人格、切換角色、匯出角色、備份角色、讓回覆更有特色、角色記憶、記憶整理、睡覺整理記憶、忘記舊記憶、角色沒有載入、hook 沒載入角色、角色被鎖住、角色鎖沒有自動解除、關掉 CLI 後角色叫不回來、角色說已在另一個工作階段、晨間狀態檢查、早上主動回報狀態,或提到 .roles.memoryROLE_NAMEROLE_ENABLEDROLE_SLEEP_STARTROLE_MEMORY_HOMEROLE_LOAD_LIMITROLE_LOAD_INBOX_LIMITROLE_LOAD_DIALOG_TURNSROLE_CAPTURE_ENABLEDROLE_SINGLE_INSTANCEROLE_INSTANCE_IDLE_MINUTES 時觸發。不適用於:工作紀錄寫入 Gitea wiki(用 /jsc-doc:worklog)、專案文件化(用 /jsc-doc:funcs)。
--- ---
# role — 角色人格與長期記憶 # role — 角色人格與長期記憶
@@ -14,10 +14,12 @@ description: 角色人格與長期記憶系統的建立與維護 skill。讓 CLI
| `hooks/hooks.json``Stop` hook | harness 自動 | 每輪結束先記錄最後互動時間 → 用本地規則過濾低價值短回合 → 值得保存時才濃縮成一則輕量 inbox 記憶 → 遮蔽 → 寫入 `inbox/` | | `hooks/hooks.json``Stop` hook | harness 自動 | 每輪結束先記錄最後互動時間 → 用本地規則過濾低價值短回合 → 值得保存時才濃縮成一則輕量 inbox 記憶 → 遮蔽 → 寫入 `inbox/` |
| `hooks/hooks.json``PreCompact` hook | harness 自動 | 對話壓縮**前**強制記錄一次(**跳過長度門檻**):壓縮會讓尚未寫入的內容永久蒸發,此時寧可多記 | | `hooks/hooks.json``PreCompact` hook | harness 自動 | 對話壓縮**前**強制記錄一次(**跳過長度門檻**):壓縮會讓尚未寫入的內容永久蒸發,此時寧可多記 |
| `hooks/hooks.json``PostCompact` hook | harness 自動 | 壓縮**後**把 harness 產生的摘要存成一則 `daily` 記憶,作為該段落的濃縮備份 | | `hooks/hooks.json``PostCompact` hook | harness 自動 | 壓縮**後**把 harness 產生的摘要存成一則 `daily` 記憶,作為該段落的濃縮備份 |
| `hooks/hooks.json``SessionEnd` hook | harness 自動 | 工作階段結束時釋放本階段持有的角色單一載入鎖,讓關掉 CLI 後可立刻重開叫回同一角色;鎖不屬於自己時不動作 |
| cron 排程(本 skill 安裝) | 系統排程 | 睡眠時段每小時檢查一次:**有 AI 在運行就不睡**;另可依 CLI 閒置時間自動小睡整理 | | cron 排程(本 skill 安裝) | 系統排程 | 睡眠時段每小時檢查一次:**有 AI 在運行就不睡**;另可依 CLI 閒置時間自動小睡整理 |
| 本 skill `/jsc-generic:role` | 使用者/助理手動 | `--new``--use``--list``--export``--agent``--migrate``--sleep``--brief``--status``--install-cron``--forget-preview` | | 本 skill `/jsc-generic:role` | 使用者/助理手動 | `--new``--use``--list``--export``--agent``--migrate``--sleep``--brief``--status``--install-cron``--forget-preview` |
| `scripts/role/role_load.sh` | SessionStart hook | 角色與記憶載入;參考 OpenClaw 的 SOULAGENTSUSERMEMORY 分層,把人格、操作邊界、使用者記憶分開注入,並提供第一則回覆問候提示(單一實作,避免漂移) | | `scripts/role/role_load.sh` | SessionStart hook | 角色與記憶載入;參考 OpenClaw 的 SOULAGENTSUSERMEMORY 分層,把人格、操作邊界、使用者記憶分開注入,並提供第一則回覆問候提示(單一實作,避免漂移) |
| `scripts/role/role_capture.sh` | Stop hook | 對話 → 記憶(固定欄位格式) | | `scripts/role/role_capture.sh` | Stop hook | 對話 → 記憶(固定欄位格式) |
| `scripts/role/role_unload.sh` | SessionEnd hook | 釋放本階段的角色單一載入鎖(比對 transcript 確認鎖屬於自己才釋放) |
| `scripts/role/role_sleep.sh` | cron/小睡/補跑/手動 | 睡眠與小睡判斷、記憶整理、角色匯出、sub agent 定義匯出、晨間狀態檢查、排程安裝、狀態輸出 | | `scripts/role/role_sleep.sh` | cron/小睡/補跑/手動 | 睡眠與小睡判斷、記憶整理、角色匯出、sub agent 定義匯出、晨間狀態檢查、排程安裝、狀態輸出 |
| `scripts/role/memory.js` | 上述共用 | 記憶檔讀寫、分類、去重合併、優先度、心理學記憶型態與關聯 metadata、壓縮歸檔、遺忘、載入組裝 | | `scripts/role/memory.js` | 上述共用 | 記憶檔讀寫、分類、去重合併、優先度、心理學記憶型態與關聯 metadata、壓縮歸檔、遺忘、載入組裝 |
| `scripts/role/transcript.js` | 上述共用 | 抽本輪對話片段、抽最近數輪純對話供工作階段交接、機密與個資遮蔽 | | `scripts/role/transcript.js` | 上述共用 | 抽本輪對話片段、抽最近數輪純對話供工作階段交接、機密與個資遮蔽 |
@@ -30,6 +32,7 @@ description: 角色人格與長期記憶系統的建立與維護 skill。讓 CLI
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| `SessionStart` 載入角色 | ✅ | ⚠️ 需該版本支援 SessionStart hook | ❌ | ❌ | ❌ | | `SessionStart` 載入角色 | ✅ | ⚠️ 需該版本支援 SessionStart hook | ❌ | ❌ | ❌ |
| `Stop` 記錄記憶 | ✅ | ✅ 需可讀 Codex session JSONL | ❌ | ❌ | ❌ | | `Stop` 記錄記憶 | ✅ | ✅ 需可讀 Codex session JSONL | ❌ | ❌ | ❌ |
| `SessionEnd` 釋放角色鎖 | ✅ | ⚠️ 需該版本支援 SessionEnd hook | ❌ | ❌ | ❌ |
| cron 睡眠整理 | ✅ 與助理無關(系統排程) | ✅ | ✅ | ✅ | ✅ | | cron 睡眠整理 | ✅ 與助理無關(系統排程) | ✅ | ✅ | ✅ | ✅ |
| `--new``--use``--sleep` 等模式 | ✅ | ⚠️ 需 plugin 目錄保留 `scripts/` | ⚠️ 同左 | ❌ 只複製 `skills/`,無腳本 | ⚠️ 同左 | | `--new``--use``--sleep` 等模式 | ✅ | ⚠️ 需 plugin 目錄保留 `scripts/` | ⚠️ 同左 | ❌ 只複製 `skills/`,無腳本 | ⚠️ 同左 |
| 濃縮/整理 CLI | `claude -p` | `codex exec` | `agy -p` | `opencode run` | `copilot -p` | | 濃縮/整理 CLI | `claude -p` | `codex exec` | `agy -p` | `opencode run` | `copilot -p` |
@@ -40,7 +43,7 @@ description: 角色人格與長期記憶系統的建立與維護 skill。讓 CLI
| plugin | hooks.json 內容 | 擁有的腳本 | | plugin | hooks.json 內容 | 擁有的腳本 |
| --- | --- | --- | | --- | --- | --- |
| `jsc-generic` | `SessionStart`role_load)+ `Stop`role_capture | `scripts/role/` | | `jsc-generic` | `SessionStart`role_load)+ `Stop``PreCompact``PostCompact`role_capture)+ `SessionEnd`role_unload | `scripts/role/` |
| `jsc-doc` | `Stop`worklog | `scripts/worklog/` | | `jsc-doc` | `Stop`worklog | `scripts/worklog/` |
| `jsc-code` | 無 `hooks/hooks.json` | 無 hook 腳本 | | `jsc-code` | 無 `hooks/hooks.json` | 無 hook 腳本 |
@@ -345,12 +348,20 @@ Stop hook 只做「編碼前處理」,輸出粗分類、summary、tags、prior
| --- | --- | | --- | --- |
| 同一個工作階段重新載入(含 `resume` | 允許,更新鎖 | | 同一個工作階段重新載入(含 `resume` | 允許,更新鎖 |
| 另一個工作階段仍活躍 | 拒絕載入人格,並在 context 說明解除方式 | | 另一個工作階段仍活躍 | 拒絕載入人格,並在 context 說明解除方式 |
| 持有者正常結束工作階段(`SessionEnd`) | **立即釋放**,下一個階段可馬上載入 |
| 持有者的 transcript 已刪除 | 自動接手 | | 持有者的 transcript 已刪除 | 自動接手 |
| 持有者閒置超過 `ROLE_INSTANCE_IDLE_MINUTES` | 自動接手 | | 持有者閒置超過 `ROLE_INSTANCE_IDLE_MINUTES` | 自動接手 |
| hook 未提供 transcript 路徑 | **一律放行且不寫鎖** | | hook 未提供 transcript 路徑 | **一律放行且不寫鎖** |
| `ROLE_SKIP_INSTANCE_LOCK=1` | **一律放行且不寫鎖**(sub agent 等非對話情境) | | `ROLE_SKIP_INSTANCE_LOCK=1` | **一律放行且不寫鎖**(sub agent 等非對話情境) |
判斷依據是**持有者 transcript 檔的 mtime**,而非 pid —— SessionStart hook 無法可靠取得 CLI 主行程 pid,也沒有保證會觸發的 SessionEnd hook 可用來釋放鎖;活躍的工作階段會持續寫入 transcript,因此「多久沒被寫入」最貼近真實狀態且不需要清理程序。 釋放分兩條路,**兩者缺一不可**
1. **快速路徑**`SessionEnd` hook`role_unload.sh`)刪掉自己的鎖。少了它,關掉 CLI 後立刻重開會被自己上一個階段的殘留鎖擋住,得等閒置逾時。
2. **後援**:持有者 transcript 的 mtime 閒置逾時接手。少了它,`kill -9`、直接關掉終端機視窗、WSL 關機、當機這些**不會觸發 `SessionEnd`** 的情況會把角色鎖死到下次手動解鎖。
`role_unload.sh` 只在**鎖檔登記的 transcript 等於自己**時才釋放:被鎖擋下的第二個階段結束時同樣會觸發 `SessionEnd`,若無條件刪鎖,它會把仍在使用中的第一個階段的鎖一起刪掉,等於讓整個限制形同虛設。
後援之所以看 transcript mtime 而非 pidSessionStart hook 無法可靠取得 CLI 主行程 pid;活躍的工作階段會持續寫入 transcript,因此「多久沒被寫入」最貼近真實狀態且不需要清理程序。
**設計原則是寧可誤放行也不要誤鎖** —— 誤鎖會讓使用者叫不出角色,比偶爾重複載入嚴重得多。因此無法識別工作階段時一律放行。 **設計原則是寧可誤放行也不要誤鎖** —— 誤鎖會讓使用者叫不出角色,比偶爾重複載入嚴重得多。因此無法識別工作階段時一律放行。