import { getContext } from '../../../../../../extensions.js'; import { lwbResolveVarPath, lwbAssignVarPath, lwbAddVarPath, lwbPushVarPath, lwbDeleteVarPath, lwbRemoveArrayItemByValue, } from '../var-commands.js'; import { lwbSplitPathWithBrackets } from '../../../core/variable-path.js'; import { extractStateBlocks, computeStateSignature, parseStateBlock } from './parser.js'; import { generateSemantic } from './semantic.js'; const LWB_STATE_APPLIED_KEY = 'LWB_STATE_APPLIED_KEY'; /** * chatMetadata 内记录每楼层 signature,防止重复执行 */ function getAppliedMap() { const meta = getContext()?.chatMetadata || {}; meta[LWB_STATE_APPLIED_KEY] ||= {}; return meta[LWB_STATE_APPLIED_KEY]; } export function clearStateAppliedFor(floor) { try { const map = getAppliedMap(); delete map[floor]; getContext()?.saveMetadataDebounced?.(); } catch {} } export function clearStateAppliedFrom(floorInclusive) { try { const map = getAppliedMap(); for (const k of Object.keys(map)) { const id = Number(k); if (!Number.isNaN(id) && id >= floorInclusive) delete map[k]; } getContext()?.saveMetadataDebounced?.(); } catch {} } function safeParseAny(str) { if (str == null || str === '') return undefined; if (typeof str !== 'string') return str; const t = str.trim(); if (!t) return undefined; if (t[0] === '{' || t[0] === '[') { try { return JSON.parse(t); } catch { return str; } } if (/^-?\d+(?:\.\d+)?$/.test(t)) return Number(t); if (t === 'true') return true; if (t === 'false') return false; return str; } function isIndexDeleteOp(opItem) { if (!opItem || opItem.op !== 'del') return false; const segs = lwbSplitPathWithBrackets(opItem.path); if (!segs.length) return false; const last = segs[segs.length - 1]; return typeof last === 'number' && Number.isFinite(last); } function buildParentPathFromSegs(segs) { return segs.reduce((acc, s) => { if (typeof s === 'number') return `${acc}[${s}]`; return acc ? `${acc}.${s}` : String(s); }, ''); } function buildExecOpsWithIndexDeleteReorder(ops) { const groups = new Map(); // parentPath -> [{ op, idx }] const groupOrder = new Map(); let orderCounter = 0; const normalOps = []; for (const op of ops) { if (isIndexDeleteOp(op)) { const segs = lwbSplitPathWithBrackets(op.path); const idx = segs[segs.length - 1]; const parentPath = buildParentPathFromSegs(segs.slice(0, -1)); if (!groups.has(parentPath)) { groups.set(parentPath, []); groupOrder.set(parentPath, orderCounter++); } groups.get(parentPath).push({ op, idx }); } else { normalOps.push(op); } } const orderedParents = Array.from(groups.keys()).sort( (a, b) => (groupOrder.get(a) ?? 0) - (groupOrder.get(b) ?? 0) ); const reorderedIndexDeletes = []; for (const parent of orderedParents) { const items = groups.get(parent) || []; items.sort((a, b) => b.idx - a.idx); for (const it of items) reorderedIndexDeletes.push(it.op); } return [...reorderedIndexDeletes, ...normalOps]; } /** * 变量 2.0:执行单条消息里的 ,返回 atoms */ export function applyStateForMessage(messageId, messageContent) { const ctx = getContext(); const chatId = ctx?.chatId || ''; const text = String(messageContent ?? ''); const signature = computeStateSignature(text); // 没有 state:清理旧 signature(避免“删掉 state 后仍然认为执行过”) if (!signature) { clearStateAppliedFor(messageId); return { atoms: [], errors: [], skipped: false }; } // 幂等:signature 没变就跳过 const appliedMap = getAppliedMap(); if (appliedMap[messageId] === signature) { return { atoms: [], errors: [], skipped: true }; } const blocks = extractStateBlocks(text); const atoms = []; const errors = []; let idx = 0; for (const block of blocks) { const ops = parseStateBlock(block); const execOps = buildExecOpsWithIndexDeleteReorder(ops); for (const opItem of execOps) { const { path, op, value, delta, warning } = opItem; if (!path) continue; if (warning) errors.push(`[${path}] ${warning}`); const oldValue = safeParseAny(lwbResolveVarPath(path)); try { switch (op) { case 'set': lwbAssignVarPath(path, value); break; case 'inc': lwbAddVarPath(path, delta); break; case 'push': lwbPushVarPath(path, value); break; case 'pop': lwbRemoveArrayItemByValue(path, value); break; case 'del': lwbDeleteVarPath(path); break; default: errors.push(`[${path}] 未知 op=${op}`); continue; } } catch (e) { errors.push(`[${path}] 执行失败: ${e?.message || e}`); continue; } const newValue = safeParseAny(lwbResolveVarPath(path)); atoms.push({ atomId: `sa-${messageId}-${idx}`, chatId, floor: messageId, idx, path, op, oldValue, newValue, delta: op === 'inc' ? delta : undefined, semantic: generateSemantic(path, op, oldValue, newValue, delta, value), timestamp: Date.now(), }); idx++; } } appliedMap[messageId] = signature; getContext()?.saveMetadataDebounced?.(); return { atoms, errors, skipped: false }; }