主动唤醒 SOP。由定时任务触发,判断是否向用户发送主动关心消息。 包含发送条件检查、消息类型选择、角色声音生成、飞书发送。
CRITICAL:选择静默时,回复内容必须完全为空。 静默路径(exit 0)执行后 Claude 不得输出任何文字。 发送"当前不需要发信息"之类的说明会直接泄露给用户,严重破坏沉浸感。
WORKSPACE_DIR="${WORKSPACE_DIR:-$(pwd)}"
EVENTS_FILE="$WORKSPACE_DIR/memory/events.md"
PROFILE_FILE="$WORKSPACE_DIR/memory/user_profile.md"
LIFE_LOG="$WORKSPACE_DIR/memory/life_log.md"
RECENT_HISTORY="$WORKSPACE_DIR/memory/RECENT_HISTORY.md"
MEMORY_LOCK="$WORKSPACE_DIR/.memory.lock"
MEMORY_FILE="$WORKSPACE_DIR/memory/MEMORY.md"
PROACTIVE_STATE="$WORKSPACE_DIR/.proactive_state"
# 前置条件:initialization_status 必须为 done
INIT_STATUS=$(grep 'initialization_status:' "$MEMORY_FILE" 2>/dev/null | grep -oP '(pending|phase1_done|phase2_done|done)' | head -1)
if [[ "$INIT_STATUS" != "done" ]]; then
exit 0 # 初始化未完成,静默退出
fi
# 从最近的 SESSION_CONTEXT.md 读取 DB_PATH、CHANNEL_KEY、SESSION_ID(供后续步骤复用)
LATEST_CTX=$(find "$WORKSPACE_DIR/sessions" -name "SESSION_CONTEXT.md" \
-exec stat -c '%Y %n' {} \; 2>/dev/null | sort -rn | head -1 | awk '{print $2}')
SESSION_ID=$(grep "^- Session ID:" "$LATEST_CTX" 2>/dev/null | awk '{print $NF}')
DB_PATH=$(grep "^- DB path:" "$LATEST_CTX" 2>/dev/null | sed 's/^- DB path: //')
CHANNEL_KEY=$(grep "^- Channel key:" "$LATEST_CTX" 2>/dev/null | sed 's/^- Channel key: //')
# 从 SQLite 查询用户最近一条消息时间(真值源,不依赖 LLM 写入的文本标记)
# 注意:此处严禁改为读取 [E000]——[E000] 仅由 memory_write skill 写入,proactive 不拥有该信号
# 使用 python3 内置 sqlite3 模块(系统 sqlite3 CLI 可能未安装),通过 env 传参避免 shell 注入
LAST_ACTIVE=""
if [[ -n "$DB_PATH" && -n "$CHANNEL_KEY" ]]; then
LAST_ACTIVE=$(_DB="$DB_PATH" _CH="$CHANNEL_KEY" python3 -c "
import sqlite3, os