{"version":3,"sources":["../src/lib/turn-log.ts"],"names":["get","set"],"mappings":";;;;;AAoBA,IAAM,YAAA,GAAe,mBAAA;AACrB,IAAM,SAAA,GAAY,GAAA;AAClB,IAAM,YAAA,GAAe,GAAA;AAYrB,IAAI,KAAA,GAA4B,IAAA;AAChC,IAAI,SAAA,GAA2B,QAAQ,OAAA,EAAQ;AAE/C,eAAe,IAAA,GAA6B;AAC1C,EAAA,IAAI,OAAO,OAAO,KAAA;AAClB,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAMA,aAAA,CAAiB,YAAY,CAAA;AAC/C,IAAA,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAM,EAAC;AAAA,EACtC,CAAA,CAAA,MAAQ;AACN,IAAA,KAAA,GAAQ,EAAC;AAAA,EACX;AACA,EAAA,OAAO,KAAA;AACT;AAGA,SAAS,UAAa,EAAA,EAAkC;AACtD,EAAA,MAAM,IAAA,GAAO,SAAA,CAAU,IAAA,CAAK,EAAA,EAAI,EAAE,CAAA;AAClC,EAAA,SAAA,GAAY,IAAA,CAAK,IAAA,CAAK,MAAM,MAAA,EAAW,MAAM,MAAS,CAAA;AACtD,EAAA,OAAO,IAAA;AACT;AAGA,eAAsB,WAAW,KAAA,EAA4F;AAC3H,EAAA,OAAO,UAAU,YAAY;AAC3B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,EAAK;AACvB,IAAA,MAAM,IAAA,GAAkB;AAAA,MACtB,IAAI,KAAA,CAAM,EAAA,IAAM,CAAA,EAAA,EAAK,IAAA,CAAK,KAAK,CAAA,CAAA,EAAI,IAAA,CAAK,MAAA,GAAS,QAAA,CAAS,EAAE,EAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA;AAAA,MACzE,EAAA,EAAI,KAAA,CAAM,EAAA,IAAM,IAAA,CAAK,GAAA,EAAI;AAAA,MACzB,UAAU,KAAA,CAAM,QAAA;AAAA,MAChB,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,OAAO,KAAA,CAAM,IAAA,IAAQ,EAAA,EAAI,KAAA,CAAM,GAAG,YAAY,CAAA;AAAA,MAC9C,GAAI,KAAA,CAAM,KAAA,GAAQ,EAAE,KAAA,EAAO,KAAA,CAAM,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,EAAE,GAAI;AAAC,KAC5D;AAEA,IAAA,IAAI,IAAI,IAAA,CAAK,CAAA,CAAA,KAAK,EAAE,EAAA,KAAO,IAAA,CAAK,EAAE,CAAA,EAAG;AACrC,IAAA,GAAA,CAAI,KAAK,IAAI,CAAA;AAEb,IAAA,MAAM,OAAA,GAAU,IAAI,MAAA,GAAS,SAAA,GAAY,IAAI,KAAA,CAAM,CAAC,SAAS,CAAA,GAAI,GAAA;AACjE,IAAA,KAAA,GAAQ,OAAA;AACR,IAAA,IAAI;AAAE,MAAA,MAAMC,aAAA,CAAI,cAAc,OAAO,CAAA;AAAA,IAAE,SAAS,CAAA,EAAG;AAAE,MAAA,OAAA,CAAQ,IAAA,CAAK,6BAA6B,CAAC,CAAA;AAAA,IAAE;AAAA,EACpG,CAAC,CAAA;AACH;AAGA,eAAsB,SAAA,CAAU,IAAA,GAAqE,EAAC,EAAyB;AAC7H,EAAA,MAAM,GAAA,GAAM,MAAM,IAAA,EAAK;AACvB,EAAA,IAAI,MAAA,GAAS,GAAA;AACb,EAAA,IAAI,IAAA,CAAK,QAAA,KAAa,MAAA,EAAW,MAAA,GAAS,MAAA,CAAO,OAAO,CAAA,CAAA,KAAK,CAAA,CAAE,QAAA,KAAa,IAAA,CAAK,QAAQ,CAAA;AACzF,EAAA,IAAI,IAAA,CAAK,OAAO,MAAA,GAAS,MAAA,CAAO,OAAO,CAAA,CAAA,KAAK,CAAA,CAAE,EAAA,IAAM,IAAA,CAAK,KAAM,CAAA;AAC/D,EAAA,IAAI,KAAK,KAAA,EAAO,MAAA,GAAS,OAAO,KAAA,CAAM,CAAC,KAAK,KAAK,CAAA;AACjD,EAAA,OAAO,MAAA;AACT;AAGO,SAAS,aAAA,CAAc,QAAQ,SAAA,EAAwB;AAC5D,EAAA,IAAI,CAAC,KAAA,EAAO,OAAO,EAAC;AACpB,EAAA,OAAO,KAAA,CAAM,KAAA,CAAM,CAAC,KAAK,CAAA;AAC3B;AAGA,eAAsB,YAAA,GAAgC;AACpD,EAAA,MAAM,GAAA,GAAM,MAAM,IAAA,EAAK;AACvB,EAAA,OAAO,GAAA,CAAI,MAAA;AACb;AAGO,SAAS,oBAAA,CAAqB,KAAA,EAAoB,IAAA,GAA8D,EAAC,EAAW;AACjI,EAAA,IAAI,CAAC,KAAA,CAAM,MAAA,EAAQ,OAAO,EAAA;AAC1B,EAAA,MAAM,QAAA,GAAW,KAAK,QAAA,IAAY,IAAA;AAElC,EAAA,IAAI,KAAA,GAAQ,CAAA;AAEZ,EAAA,MAAM,QAAA,GAAW,CAAC,GAAG,KAAK,EAAE,OAAA,EAAQ;AACpC,EAAA,MAAM,OAAiB,EAAC;AACxB,EAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,IAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,CAAA,CAAE,EAAE,CAAA,CAAE,kBAAA,CAAmB,OAAA,EAAS,EAAE,MAAA,EAAQ,KAAA,EAAO,CAAA;AACzE,IAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,CAAA,CAAE,EAAE,CAAA,CAAE,kBAAA,CAAmB,OAAA,EAAS,EAAE,KAAA,EAAO,OAAA,EAAS,GAAA,EAAK,WAAW,CAAA;AAC1F,IAAA,MAAM,SAAA,GAAY,CAAA,CAAE,QAAA,IAAY,CAAA,CAAE,QAAA,KAAa,IAAA,CAAK,cAAA,GAAiB,CAAA,EAAA,EAAK,CAAA,CAAE,QAAA,CAAS,KAAA,CAAM,EAAE,CAAC,CAAA,CAAA,CAAA,GAAM,EAAA;AACpG,IAAA,MAAM,QAAA,GAAW,CAAA,CAAE,IAAA,KAAS,MAAA,GAAS,WAAA,GAAO,WAAA;AAC5C,IAAA,MAAM,aAAa,CAAA,CAAE,KAAA,GAAQ,CAAA,SAAA,EAAO,CAAA,CAAE,KAAK,CAAA,CAAA,GAAK,EAAA;AAChD,IAAA,MAAM,OAAO,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,EAAI,IAAI,IAAI,SAAS,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA,EAAI,EAAE,IAAI,CAAA,EAAA,EAAK,CAAA,CAAE,IAAI,GAAG,UAAU,CAAA,CAAA;AACxF,IAAA,IAAI,KAAA,GAAQ,IAAA,CAAK,MAAA,GAAS,QAAA,EAAU;AACpC,IAAA,IAAA,CAAK,KAAK,IAAI,CAAA;AACd,IAAA,KAAA,IAAS,KAAK,MAAA,GAAS,CAAA;AAAA,EACzB;AACA,EAAA,IAAA,CAAK,OAAA,EAAQ;AACb,EAAA,OAAO,CAAA,gCAAA,EAA4B,KAAK,MAAM,CAAA;AAAA,EAAsB,IAAA,CAAK,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AACrF;AAGA,eAAsB,YAAA,GAA8B;AAClD,EAAA,OAAO,UAAU,YAAY;AAC3B,IAAA,KAAA,GAAQ,EAAC;AACT,IAAA,IAAI;AAAE,MAAA,MAAMA,aAAA,CAAI,YAAA,EAAc,EAAE,CAAA;AAAA,IAAE,CAAA,CAAA,MAAQ;AAAA,IAAC;AAAA,EAC7C,CAAC,CAAA;AACH;AAGA,eAAsB,YAAA,GAAgH;AACpI,EAAA,MAAM,GAAA,GAAM,MAAM,IAAA,EAAK;AACvB,EAAA,IAAI,CAAC,GAAA,CAAI,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,CAAA,EAAG,QAAA,EAAU,IAAA,EAAM,QAAA,EAAU,IAAA,EAAM,OAAA,EAAS,EAAC,EAAE;AAChF,EAAA,OAAO;AAAA,IACL,OAAO,GAAA,CAAI,MAAA;AAAA,IACX,QAAA,EAAU,GAAA,CAAI,CAAC,CAAA,CAAE,EAAA;AAAA,IACjB,QAAA,EAAU,GAAA,CAAI,GAAA,CAAI,MAAA,GAAS,CAAC,CAAA,CAAE,EAAA;AAAA,IAC9B,OAAA,EAAS,KAAA,CAAM,IAAA,CAAK,IAAI,IAAI,GAAA,CAAI,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,QAAQ,CAAA,CAAE,MAAA,CAAO,OAAO,CAAa,CAAC;AAAA,GACnF;AACF","file":"chunk-QTAJQB6R.cjs","sourcesContent":["/**\n * turn-log — IDB-persisted continuous conversation transcript.\n *\n * Inspired by devduck's `get_last_messages()` which injects the last ~200\n * turns into the system prompt as a timestamped flow. This gives the agent\n * continuous context awareness across reloads, thread switches, and even\n * context-window trims (since the transcript survives message drops).\n *\n * Storage model:\n *   - Single IDB key `careless-turn-log` holds a global ring (last 200 turns\n *     across ALL threads, tagged with threadId).\n *   - Each turn: { id, ts, threadId, role, text, toolSummary? }\n *   - Ring is capped; oldest entries evicted on append.\n *\n * Why global not per-thread? The agent benefits from seeing past thread\n * activity (\"I asked the same thing yesterday in another thread\"). Per-thread\n * filtering is done at render time if desired.\n */\nimport { get, set } from 'idb-keyval'\n\nconst TURN_LOG_KEY = 'careless-turn-log'\nconst MAX_TURNS = 200\nconst MAX_TEXT_LEN = 500  // truncate long turns to keep prompt size bounded\n\nexport interface TurnEntry {\n  id: string\n  ts: number\n  threadId: string | null\n  role: 'user' | 'assistant'\n  text: string\n  /** Short summary of tools fired during this turn (assistant only) */\n  tools?: string\n}\n\nlet cache: TurnEntry[] | null = null\nlet writeLock: Promise<void> = Promise.resolve()\n\nasync function load(): Promise<TurnEntry[]> {\n  if (cache) return cache\n  try {\n    const raw = await get<TurnEntry[]>(TURN_LOG_KEY)\n    cache = Array.isArray(raw) ? raw : []\n  } catch {\n    cache = []\n  }\n  return cache\n}\n\n/** Serialize writes to avoid lost-update race under parallel streams. */\nfunction serialize<T>(fn: () => Promise<T>): Promise<T> {\n  const next = writeLock.then(fn, fn)\n  writeLock = next.then(() => undefined, () => undefined)\n  return next\n}\n\n/** Append a turn to the log. Fire-and-forget safe; idempotent on duplicate id. */\nexport async function appendTurn(entry: Omit<TurnEntry, 'id' | 'ts'> & Partial<Pick<TurnEntry, 'id' | 'ts'>>): Promise<void> {\n  return serialize(async () => {\n    const log = await load()\n    const full: TurnEntry = {\n      id: entry.id || `t-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,\n      ts: entry.ts ?? Date.now(),\n      threadId: entry.threadId,\n      role: entry.role,\n      text: (entry.text || '').slice(0, MAX_TEXT_LEN),\n      ...(entry.tools ? { tools: entry.tools.slice(0, 200) } : {}),\n    }\n    // Dedupe on id (in case of retries / regenerate)\n    if (log.some(t => t.id === full.id)) return\n    log.push(full)\n    // Evict oldest\n    const trimmed = log.length > MAX_TURNS ? log.slice(-MAX_TURNS) : log\n    cache = trimmed\n    try { await set(TURN_LOG_KEY, trimmed) } catch (e) { console.warn('[turn-log] persist failed', e) }\n  })\n}\n\n/** Read recent turns, optionally filtered by thread or limit. */\nexport async function readTurns(opts: { limit?: number; threadId?: string | null; since?: number } = {}): Promise<TurnEntry[]> {\n  const log = await load()\n  let result = log\n  if (opts.threadId !== undefined) result = result.filter(t => t.threadId === opts.threadId)\n  if (opts.since) result = result.filter(t => t.ts >= opts.since!)\n  if (opts.limit) result = result.slice(-opts.limit)\n  return result\n}\n\n/** Synchronous read from cache (for system-prompt hot path). Returns [] if not yet loaded. */\nexport function readTurnsSync(limit = MAX_TURNS): TurnEntry[] {\n  if (!cache) return []\n  return cache.slice(-limit)\n}\n\n/** Force-load cache (call once on app boot so readTurnsSync works on first render). */\nexport async function primeTurnLog(): Promise<number> {\n  const log = await load()\n  return log.length\n}\n\n/** Format turns as a continuous flow for system-prompt injection. */\nexport function formatTurnsForPrompt(turns: TurnEntry[], opts: { maxChars?: number; activeThreadId?: string | null } = {}): string {\n  if (!turns.length) return ''\n  const maxChars = opts.maxChars ?? 12000\n  const lines: string[] = []\n  let chars = 0\n  // Walk newest-to-oldest so if we truncate, we keep the freshest\n  const reversed = [...turns].reverse()\n  const kept: string[] = []\n  for (const t of reversed) {\n    const time = new Date(t.ts).toLocaleTimeString('en-US', { hour12: false })\n    const date = new Date(t.ts).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })\n    const threadTag = t.threadId && t.threadId !== opts.activeThreadId ? ` {${t.threadId.slice(-4)}}` : ''\n    const roleIcon = t.role === 'user' ? '👤' : '🤖'\n    const toolSuffix = t.tools ? `  ◦ ${t.tools}` : ''\n    const line = `[${date} ${time}]${threadTag} ${roleIcon} ${t.role}: ${t.text}${toolSuffix}`\n    if (chars + line.length > maxChars) break\n    kept.push(line)\n    chars += line.length + 1\n  }\n  kept.reverse()  // back to chronological\n  return `## 📜 Recent Turns (last ${kept.length}, across threads)\\n${kept.join('\\n')}`\n}\n\n/** Clear the log (exposed for /clear slash-command integration). */\nexport async function clearTurnLog(): Promise<void> {\n  return serialize(async () => {\n    cache = []\n    try { await set(TURN_LOG_KEY, []) } catch {}\n  })\n}\n\n/** Stats for debugging. */\nexport async function turnLogStats(): Promise<{ count: number; oldestTs: number | null; newestTs: number | null; threads: string[] }> {\n  const log = await load()\n  if (!log.length) return { count: 0, oldestTs: null, newestTs: null, threads: [] }\n  return {\n    count: log.length,\n    oldestTs: log[0].ts,\n    newestTs: log[log.length - 1].ts,\n    threads: Array.from(new Set(log.map(t => t.threadId).filter(Boolean) as string[])),\n  }\n}\n"]}