#!/usr/bin/env node // Stop:情緒隨時間衰減、續租、記錄逐字,並在達到「短期→長期」條件時提醒固化。 // 劇場模式進行中不發任何提醒(那會破壞「只顯示人格對話」)。 import path from "node:path"; import { fileURLToPath } from "node:url"; import { readEvent, respond } from "./_hook.mjs"; import * as pl from "../scripts/persona-lib.mjs"; import * as gt from "../scripts/persona-gitea.mjs"; const CLI = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "scripts", "persona.mjs"); const event = readEvent(); if (!event) process.exit(0); const sessionId = event.session_id || "unknown"; const data = pl.loadSession(sessionId); const host = data.host; if (!host || !pl.personaExists(host)) process.exit(0); pl.heartbeatLock(host, sessionId); const state = pl.decayEmotion(pl.loadEmotion(host)); pl.writeJson(pl.emotionPath(host), state); const theater = Boolean(data.theater) && (data.rooms || []).length > 0; const message = event.last_assistant_message || ""; if (message) { pl.appendJsonl(pl.journalPath(host), { ts: pl.nowIso(), kind: "utterance", role: "persona", text: message.slice(0, 4000), mood: pl.mood(state), }); // 說出口的話存進 said.jsonl,下一輪的 會提醒「這些別再說一次」。 // recordSaid 內建 10 分鐘去重,所以劇場模式裡 room post 已記過的台詞不會重覆記。 for (const line of pl.spokenLines(message, { theater })) { pl.recordSaid(host, line, { kind: theater ? "room" : "reply" }); } } // 檔案區=高頻活狀態:每輪結束後背景推上 Gitea(有最小間隔、失敗不阻斷這一輪) if (!gt.giteaProblem() && gt.personaCode(host) && gt.pushDue(host, "files")) { gt.pushInBackground(host, "files", sessionId, CLI); } const out = { suppressOutput: true }; if (!theater) { const { total, candidates } = pl.promotionCandidates(host); if (candidates.length) { const rules = [...new Set(candidates.flatMap((c) => c.rules))].sort().join("/"); out.systemMessage = `[jsc-persona] \`${host}\` 短期記憶 ${total} 筆,${candidates.length} 組已達固化條件(${rules})` + "→ 建議執行 /jsc-persona:persona-memory。"; } } respond(out);