[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-tanstack-ai-persistencebuild-custom-adapter":3,"mdc--uvlrhz-key":60,"related-org-tanstack-ai-persistencebuild-custom-adapter":4195,"related-repo-tanstack-ai-persistencebuild-custom-adapter":4339},{"slug":4,"name":5,"fn":6,"description":7,"org":8,"tags":12,"stars":30,"repoUrl":31,"updatedAt":32,"license":33,"forks":34,"topics":35,"repo":55,"sourceUrl":58,"mdContent":59},"ai-persistencebuild-custom-adapter","ai-persistence\u002Fbuild-custom-adapter","build custom database adapters for TanStack AI","Use when an app needs TanStack AI chat persistence on a database with no dedicated recipe — raw Postgres (pg\u002Fpostgres.js), Kysely, node:sqlite, MongoDB, Supabase, Redis. Writes a chat-persistence.ts against the app's existing client, covering the four stores, the idempotency invariants, and the conformance gate. Route to the Drizzle, Prisma, or Cloudflare skills instead when one of those matches.",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},"tanstack","TanStack","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Ftanstack.png",[13,17,20,23,26,29],{"name":14,"slug":15,"type":16},"MongoDB","mongodb","tag",{"name":18,"slug":19,"type":16},"Database","database",{"name":21,"slug":22,"type":16},"Redis","redis",{"name":24,"slug":25,"type":16},"PostgreSQL","postgresql",{"name":27,"slug":28,"type":16},"Persistence","persistence",{"name":10,"slug":9,"type":16},2884,"https:\u002F\u002Fgithub.com\u002FTanStack\u002Fai","2026-07-30T05:53:36.51292",null,269,[36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,9,51,52,53,54],"ai","ai-agents","ai-sdk","anthropic","chatbot","function-calling","gemini","generative-ai","llm","multimodal","openai","react","solidjs","streaming","svelte","tool-calling","typescript","typescript-sdk","vue",{"repoUrl":31,"stars":30,"forks":34,"topics":56,"description":57},[36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,9,51,52,53,54],"🤖 Type-safe, provider-agnostic TypeScript AI SDK for streaming chat, tool calling, agents, and multimodal apps across OpenAI, Anthropic, Gemini, React, Vue, Svelte, and Solid.","https:\u002F\u002Fgithub.com\u002FTanStack\u002Fai\u002Ftree\u002FHEAD\u002Fpackages\u002Fai-persistence\u002Fskills\u002Fai-persistence\u002Fbuild-custom-adapter","---\nname: ai-persistence\u002Fbuild-custom-adapter\ndescription: Use when an app needs TanStack AI chat persistence on a database with no dedicated recipe — raw Postgres (pg\u002Fpostgres.js), Kysely, node:sqlite, MongoDB, Supabase, Redis. Writes a chat-persistence.ts against the app's existing client, covering the four stores, the idempotency invariants, and the conformance gate. Route to the Drizzle, Prisma, or Cloudflare skills instead when one of those matches.\n---\n\n# Custom Chat Persistence\n\nThe deliverable is **one file in the app** — `src\u002Flib\u002Fchat-persistence.ts` —\nexporting a `ChatPersistence` built from the database client the app already\nhas. Plus whatever DDL that database needs, added through the app's existing\nmigration flow.\n\nDo not create a package, a second client, or a migration runner.\n\n**Route first.** If the app already runs one of these, stop and use that skill —\nit has the driver-specific code:\n\n| App runs                  | Use                                     |\n| ------------------------- | --------------------------------------- |\n| Drizzle ORM (any dialect) | ai-persistence\u002Fbuild-drizzle-adapter    |\n| Prisma                    | ai-persistence\u002Fbuild-prisma-adapter     |\n| Cloudflare Workers + D1   | ai-persistence\u002Fbuild-cloudflare-adapter |\n\nEverything else lands here. The full contracts and their invariants are in\n**ai-persistence\u002Fstores**; the complete worked `node:sqlite` walkthrough\nis `docs\u002Fpersistence\u002Fbuild-your-own-adapter.md` and\n`examples\u002Fts-react-chat\u002Fsrc\u002Flib\u002Fsqlite-persistence.ts`.\n\n## 1. Read the app before writing anything\n\n| Find               | Where to look                                                 | What it decides                                        |\n| ------------------ | ------------------------------------------------------------- | ------------------------------------------------------ |\n| The client         | `src\u002Fdb.ts`, `src\u002Flib\u002Fdb.ts`, `src\u002Fserver\u002Fdb.ts`              | What the file imports — never construct a second pool  |\n| Client lifetime    | module singleton vs per-request factory (`getDb()`, bindings) | `export const chatPersistence` vs `export function`    |\n| Migration flow     | `migrations\u002F`, `drizzle\u002F`, `supabase\u002Fmigrations\u002F`, an ORM CLI | How the DDL gets applied — use theirs, add nothing new |\n| Naming conventions | existing tables\u002Fcollections                                   | Prefix (`chat_*`) so nothing collides                  |\n| JSON support       | `jsonb` (Postgres), `json` (MySQL 5.7+), text (SQLite)        | Whether mappers stringify\u002Fparse                        |\n| Import alias       | `tsconfig.json` `paths`                                       | `@\u002Fdb`, `~\u002Fdb`, `#\u002Fdb`, or a relative path             |\n\n## 2. Shape the storage\n\nFour logical records. Whatever the engine, keep these keys — the store methods\nlook records up by exactly these:\n\n| Record    | Key                | Fields                                                                              |\n| --------- | ------------------ | ----------------------------------------------------------------------------------- |\n| thread    | `threadId`         | `messages` (array, full transcript)                                                 |\n| run       | `runId`            | `threadId`, `status`, `startedAt`, `finishedAt?`, `error?`, `usage?`                |\n| interrupt | `interruptId`      | `runId`, `threadId`, `status`, `requestedAt`, `resolvedAt?`, `payload`, `response?` |\n| metadata  | `(namespace, key)` | `value`                                                                             |\n\n- Timestamps are **epoch milliseconds** (`number`) in records. Store them\n  however the engine prefers and convert in the mapper.\n- `(namespace, key)` is a **composite** key. Never join with a separator —\n  `('a:b','c')` and `('a','b:c')` must stay distinct records, and the\n  conformance suite checks it.\n- Index `runs(threadId, status)` and `interrupts(threadId, requestedAt)` — those\n  are the two listing paths.\n- Extra app-owned columns are fine (a `userId`, audit columns) as long as they\n  are nullable or defaulted. The stores never read columns they do not know\n  about.\n\n## 3. The five invariants\n\nGetting one of these wrong is the usual source of stuck approvals and wiped\nhistory. They are engine-independent:\n\n1. **`saveThread` is a full overwrite**, never an append. The argument is the\n   complete authoritative transcript.\n2. **`loadThread` returns `[]`** for an unknown thread, never `null`.\n3. **`createOrResume` is insert-if-absent** — an existing `runId` comes back\n   _unchanged_, ignoring the new field values. Resume and double-submit depend\n   on it. After a racy insert, re-read rather than trusting your own write.\n4. **`runs.update` on an unknown id is a silent no-op** — it must not throw and\n   must not insert. (Drivers that throw on zero rows affected need the\n   `updateMany`-style call, not the `update`-one-or-throw call.)\n5. **`interrupts.create` is insert-if-absent** — never clobber a resolved\n   interrupt back to pending. Every `list*` is ordered by `requestedAt`\n   ascending.\n\nRow mappers omit absent optionals\n(`...(row.error != null ? { error: row.error } : {})`) so records compare\ncleanly against the reference in-memory backend.\n\n## 4. Write `src\u002Flib\u002Fchat-persistence.ts`\n\nFour factories and one assembly. Postgres via `pg` shown here; the shape is the\nsame for any driver.\n\n```ts ignore\nimport { defineAIPersistence } from '@tanstack\u002Fai-persistence'\nimport type { Pool } from 'pg'\nimport type {\n  ChatPersistence,\n  MessageStore,\n  RunStore,\n} from '@tanstack\u002Fai-persistence'\n\nimport { pool } from '@\u002Fdb'\n\nfunction createMessageStore(db: Pool): MessageStore {\n  return {\n    async loadThread(threadId) {\n      const { rows } = await db.query(\n        'SELECT messages_json FROM chat_threads WHERE thread_id = $1',\n        [threadId],\n      )\n      return rows[0]?.messages_json ?? []\n    },\n    \u002F\u002F Full overwrite — `messages` is the complete authoritative transcript.\n    async saveThread(threadId, messages) {\n      await db.query(\n        `INSERT INTO chat_threads (thread_id, messages_json, updated_at)\n         VALUES ($1, $2, $3)\n         ON CONFLICT (thread_id)\n         DO UPDATE SET messages_json = EXCLUDED.messages_json,\n                       updated_at = EXCLUDED.updated_at`,\n        [threadId, JSON.stringify(messages), Date.now()],\n      )\n    },\n  }\n}\n\nfunction createRunStore(db: Pool): RunStore {\n  async function get(runId: string) {\n    const { rows } = await db.query(\n      'SELECT * FROM chat_runs WHERE run_id = $1',\n      [runId],\n    )\n    return rows[0] ? mapRun(rows[0]) : null\n  }\n\n  return {\n    get,\n    \u002F\u002F Idempotent: an existing runId is returned untouched.\n    async createOrResume({ runId, threadId, startedAt, status }) {\n      const existing = await get(runId)\n      if (existing) return existing\n\n      await db.query(\n        `INSERT INTO chat_runs (run_id, thread_id, status, started_at)\n         VALUES ($1, $2, $3, $4)\n         ON CONFLICT (run_id) DO NOTHING`,\n        [runId, threadId, status ?? 'running', startedAt],\n      )\n      \u002F\u002F Re-read: a concurrent createOrResume may have won the race, and that\n      \u002F\u002F row is the authoritative one.\n      const stored = await get(runId)\n      return (\n        stored ?? { runId, threadId, status: status ?? 'running', startedAt }\n      )\n    },\n    \u002F\u002F ... update (no-op on unknown id), findActiveRun (latest 'running')\n  }\n}\n\n\u002F** The four chat state stores backed by the app's database. *\u002F\nexport const chatPersistence: ChatPersistence = defineAIPersistence({\n  stores: {\n    messages: createMessageStore(pool),\n    runs: createRunStore(pool),\n    interrupts: createInterruptStore(pool),\n    metadata: createMetadataStore(pool),\n  },\n})\n```\n\nAnnotate `ChatPersistence` — bare `AIPersistence` is the all-optional bag and\n`withPersistence` rejects it. There is no `locks` store: `stores` accepts only\n`messages`, `runs`, `interrupts`, `metadata`, and anything else throws\n`Unknown AIPersistence store key`. Coordination is wired separately with\n`withLocks` (see **ai-core\u002Flocks**).\n\nIf the client is per-request (Workers bindings, request-scoped transactions),\nexport a `chatPersistence()` factory instead of a const and call it inside the\nhandler.\n\n## Engine notes\n\n**Postgres (`pg`, `postgres.js`, Neon, Supabase)** — `jsonb` columns round-trip\nobjects, so skip the `JSON.stringify` on read paths (`pg` parses `jsonb` for\nyou; check what the driver returns before assuming). `bigint` columns come back\nas strings in `pg` — use `bigint` with an explicit `Number()` in the mapper, or\nstore epoch ms in a `double precision`\u002F`bigint` and convert once. Composite key\nis `PRIMARY KEY (namespace, key)`.\n\n**Kysely** — define the four tables in the app's `Database` interface, then the\nstores are `db.insertInto('chat_runs').values(...).onConflict((oc) => oc.column('run_id').doNothing())`\nand `.executeTakeFirst()`. `updateTable(...).execute()` is already a no-op on\nzero matches, so invariant 4 comes free.\n\n**node:sqlite \u002F better-sqlite3** — the complete implementation is in the guide\nand in `examples\u002Fts-react-chat\u002Fsrc\u002Flib\u002Fsqlite-persistence.ts`. Prepared\nstatements at factory scope, `INSERT ... ON CONFLICT`, JSON as `text`, epoch ms\nas `integer`. Wrap sync calls in `async` methods; the contracts are promise-based.\n\n**MongoDB** — one collection per record type, `_id` set to the natural key\n(`threadId`, `runId`, `interruptId`, and `` `${namespace}�${key}` `` or a\ncompound unique index on `{ namespace, key }` — never a `:`-joined string).\n`createOrResume` is `updateOne({ _id }, { $setOnInsert: doc }, { upsert: true })`\nthen a `findOne` — `$setOnInsert` is the insert-if-absent primitive. Guard the\n`E11000` duplicate-key race and re-read. `list*` need `.sort({ requestedAt: 1 })`.\n\n**Redis \u002F Upstash** — workable for `metadata` and excellent for `LockStore`, but\nthink before putting `interrupts` there: the listings need ordered secondary\nindexes you have to maintain by hand (a sorted set per thread and per run,\nscored by `requestedAt`). A common split is Postgres for `messages`\u002F`runs`\u002F\n`interrupts` and Redis for locks; compose them with `composePersistence`.\n\n**Anything else** — you only need the five invariants above. The core never\ninspects your storage.\n\n## Adopt part of it\n\nYou rarely need all four stores at once. Implement what you own and fill the\nrest from another base:\n\n```ts ignore\nimport { composePersistence, memoryPersistence } from '@tanstack\u002Fai-persistence'\nimport { messages, runs } from '.\u002Fmy-stores'\n\nexport const chatPersistence = composePersistence(memoryPersistence(), {\n  overrides: { messages, runs },\n})\n```\n\nOnly listed keys move. There is **no cross-store transaction** — if `messages`\nlives in Postgres and `interrupts` in Redis, a write touching both is two\nwrites. The idempotency invariants are exactly what make those retries safe.\n\n## Wire it into the chat route\n\n```ts ignore\nimport {\n  chat,\n  chatParamsFromRequest,\n  toServerSentEventsResponse,\n} from '@tanstack\u002Fai'\nimport { openaiText } from '@tanstack\u002Fai-openai'\nimport { withPersistence } from '@tanstack\u002Fai-persistence'\nimport { chatPersistence } from '@\u002Flib\u002Fchat-persistence'\n\nexport async function POST(request: Request) {\n  const params = await chatParamsFromRequest(request)\n  const stream = chat({\n    adapter: openaiText('gpt-5.5'),\n    messages: params.messages,\n    threadId: params.threadId,\n    runId: params.runId,\n    ...(params.resume ? { resume: params.resume } : {}),\n    middleware: [withPersistence(chatPersistence)],\n  })\n  return toServerSentEventsResponse(stream)\n}\n```\n\n`threadId` is a bare string to the stores. **Authorize thread access at the\nroute** — derive the user from the session, never trust a client-supplied id.\n\n## Verify (required)\n\nThis matters more here than anywhere else: there is no reference driver to\ncompare against, so the testkit is the only thing standing between a subtle\nidempotency bug and stuck approvals in production.\n\n```ts ignore\nimport { runPersistenceConformance } from '@tanstack\u002Fai-persistence\u002Ftestkit'\nimport { chatPersistence } from '..\u002Fsrc\u002Flib\u002Fchat-persistence'\n\nrunPersistenceConformance('app-custom', () => chatPersistence)\n```\n\nPoint it at a throwaway database and reset between runs. Declare intentional\nomissions with `skip: ['metadata']` — it accepts only\n`'messages' | 'runs' | 'interrupts' | 'metadata'`, never `'locks'`, which is not\na state store.\n",{"data":61,"body":62},{"name":5,"description":7},{"type":63,"children":64},"root",[65,74,105,110,120,187,223,230,478,484,489,695,786,792,797,939,952,963,976,2676,2770,2783,2789,2893,2934,2982,3093,3159,3169,3175,3180,3376,3402,3408,4001,4018,4024,4029,4160,4189],{"type":66,"tag":67,"props":68,"children":70},"element","h1",{"id":69},"custom-chat-persistence",[71],{"type":72,"value":73},"text","Custom Chat Persistence",{"type":66,"tag":75,"props":76,"children":77},"p",{},[78,80,86,88,95,97,103],{"type":72,"value":79},"The deliverable is ",{"type":66,"tag":81,"props":82,"children":83},"strong",{},[84],{"type":72,"value":85},"one file in the app",{"type":72,"value":87}," — ",{"type":66,"tag":89,"props":90,"children":92},"code",{"className":91},[],[93],{"type":72,"value":94},"src\u002Flib\u002Fchat-persistence.ts",{"type":72,"value":96}," —\nexporting a ",{"type":66,"tag":89,"props":98,"children":100},{"className":99},[],[101],{"type":72,"value":102},"ChatPersistence",{"type":72,"value":104}," built from the database client the app already\nhas. Plus whatever DDL that database needs, added through the app's existing\nmigration flow.",{"type":66,"tag":75,"props":106,"children":107},{},[108],{"type":72,"value":109},"Do not create a package, a second client, or a migration runner.",{"type":66,"tag":75,"props":111,"children":112},{},[113,118],{"type":66,"tag":81,"props":114,"children":115},{},[116],{"type":72,"value":117},"Route first.",{"type":72,"value":119}," If the app already runs one of these, stop and use that skill —\nit has the driver-specific code:",{"type":66,"tag":121,"props":122,"children":123},"table",{},[124,143],{"type":66,"tag":125,"props":126,"children":127},"thead",{},[128],{"type":66,"tag":129,"props":130,"children":131},"tr",{},[132,138],{"type":66,"tag":133,"props":134,"children":135},"th",{},[136],{"type":72,"value":137},"App runs",{"type":66,"tag":133,"props":139,"children":140},{},[141],{"type":72,"value":142},"Use",{"type":66,"tag":144,"props":145,"children":146},"tbody",{},[147,161,174],{"type":66,"tag":129,"props":148,"children":149},{},[150,156],{"type":66,"tag":151,"props":152,"children":153},"td",{},[154],{"type":72,"value":155},"Drizzle ORM (any dialect)",{"type":66,"tag":151,"props":157,"children":158},{},[159],{"type":72,"value":160},"ai-persistence\u002Fbuild-drizzle-adapter",{"type":66,"tag":129,"props":162,"children":163},{},[164,169],{"type":66,"tag":151,"props":165,"children":166},{},[167],{"type":72,"value":168},"Prisma",{"type":66,"tag":151,"props":170,"children":171},{},[172],{"type":72,"value":173},"ai-persistence\u002Fbuild-prisma-adapter",{"type":66,"tag":129,"props":175,"children":176},{},[177,182],{"type":66,"tag":151,"props":178,"children":179},{},[180],{"type":72,"value":181},"Cloudflare Workers + D1",{"type":66,"tag":151,"props":183,"children":184},{},[185],{"type":72,"value":186},"ai-persistence\u002Fbuild-cloudflare-adapter",{"type":66,"tag":75,"props":188,"children":189},{},[190,192,197,199,205,207,213,215,221],{"type":72,"value":191},"Everything else lands here. The full contracts and their invariants are in\n",{"type":66,"tag":81,"props":193,"children":194},{},[195],{"type":72,"value":196},"ai-persistence\u002Fstores",{"type":72,"value":198},"; the complete worked ",{"type":66,"tag":89,"props":200,"children":202},{"className":201},[],[203],{"type":72,"value":204},"node:sqlite",{"type":72,"value":206}," walkthrough\nis ",{"type":66,"tag":89,"props":208,"children":210},{"className":209},[],[211],{"type":72,"value":212},"docs\u002Fpersistence\u002Fbuild-your-own-adapter.md",{"type":72,"value":214}," and\n",{"type":66,"tag":89,"props":216,"children":218},{"className":217},[],[219],{"type":72,"value":220},"examples\u002Fts-react-chat\u002Fsrc\u002Flib\u002Fsqlite-persistence.ts",{"type":72,"value":222},".",{"type":66,"tag":224,"props":225,"children":227},"h2",{"id":226},"_1-read-the-app-before-writing-anything",[228],{"type":72,"value":229},"1. Read the app before writing anything",{"type":66,"tag":121,"props":231,"children":232},{},[233,254],{"type":66,"tag":125,"props":234,"children":235},{},[236],{"type":66,"tag":129,"props":237,"children":238},{},[239,244,249],{"type":66,"tag":133,"props":240,"children":241},{},[242],{"type":72,"value":243},"Find",{"type":66,"tag":133,"props":245,"children":246},{},[247],{"type":72,"value":248},"Where to look",{"type":66,"tag":133,"props":250,"children":251},{},[252],{"type":72,"value":253},"What it decides",{"type":66,"tag":144,"props":255,"children":256},{},[257,294,332,370,396,428],{"type":66,"tag":129,"props":258,"children":259},{},[260,265,289],{"type":66,"tag":151,"props":261,"children":262},{},[263],{"type":72,"value":264},"The client",{"type":66,"tag":151,"props":266,"children":267},{},[268,274,276,282,283],{"type":66,"tag":89,"props":269,"children":271},{"className":270},[],[272],{"type":72,"value":273},"src\u002Fdb.ts",{"type":72,"value":275},", ",{"type":66,"tag":89,"props":277,"children":279},{"className":278},[],[280],{"type":72,"value":281},"src\u002Flib\u002Fdb.ts",{"type":72,"value":275},{"type":66,"tag":89,"props":284,"children":286},{"className":285},[],[287],{"type":72,"value":288},"src\u002Fserver\u002Fdb.ts",{"type":66,"tag":151,"props":290,"children":291},{},[292],{"type":72,"value":293},"What the file imports — never construct a second pool",{"type":66,"tag":129,"props":295,"children":296},{},[297,302,315],{"type":66,"tag":151,"props":298,"children":299},{},[300],{"type":72,"value":301},"Client lifetime",{"type":66,"tag":151,"props":303,"children":304},{},[305,307,313],{"type":72,"value":306},"module singleton vs per-request factory (",{"type":66,"tag":89,"props":308,"children":310},{"className":309},[],[311],{"type":72,"value":312},"getDb()",{"type":72,"value":314},", bindings)",{"type":66,"tag":151,"props":316,"children":317},{},[318,324,326],{"type":66,"tag":89,"props":319,"children":321},{"className":320},[],[322],{"type":72,"value":323},"export const chatPersistence",{"type":72,"value":325}," vs ",{"type":66,"tag":89,"props":327,"children":329},{"className":328},[],[330],{"type":72,"value":331},"export function",{"type":66,"tag":129,"props":333,"children":334},{},[335,340,365],{"type":66,"tag":151,"props":336,"children":337},{},[338],{"type":72,"value":339},"Migration flow",{"type":66,"tag":151,"props":341,"children":342},{},[343,349,350,356,357,363],{"type":66,"tag":89,"props":344,"children":346},{"className":345},[],[347],{"type":72,"value":348},"migrations\u002F",{"type":72,"value":275},{"type":66,"tag":89,"props":351,"children":353},{"className":352},[],[354],{"type":72,"value":355},"drizzle\u002F",{"type":72,"value":275},{"type":66,"tag":89,"props":358,"children":360},{"className":359},[],[361],{"type":72,"value":362},"supabase\u002Fmigrations\u002F",{"type":72,"value":364},", an ORM CLI",{"type":66,"tag":151,"props":366,"children":367},{},[368],{"type":72,"value":369},"How the DDL gets applied — use theirs, add nothing new",{"type":66,"tag":129,"props":371,"children":372},{},[373,378,383],{"type":66,"tag":151,"props":374,"children":375},{},[376],{"type":72,"value":377},"Naming conventions",{"type":66,"tag":151,"props":379,"children":380},{},[381],{"type":72,"value":382},"existing tables\u002Fcollections",{"type":66,"tag":151,"props":384,"children":385},{},[386,388,394],{"type":72,"value":387},"Prefix (",{"type":66,"tag":89,"props":389,"children":391},{"className":390},[],[392],{"type":72,"value":393},"chat_*",{"type":72,"value":395},") so nothing collides",{"type":66,"tag":129,"props":397,"children":398},{},[399,404,423],{"type":66,"tag":151,"props":400,"children":401},{},[402],{"type":72,"value":403},"JSON support",{"type":66,"tag":151,"props":405,"children":406},{},[407,413,415,421],{"type":66,"tag":89,"props":408,"children":410},{"className":409},[],[411],{"type":72,"value":412},"jsonb",{"type":72,"value":414}," (Postgres), ",{"type":66,"tag":89,"props":416,"children":418},{"className":417},[],[419],{"type":72,"value":420},"json",{"type":72,"value":422}," (MySQL 5.7+), text (SQLite)",{"type":66,"tag":151,"props":424,"children":425},{},[426],{"type":72,"value":427},"Whether mappers stringify\u002Fparse",{"type":66,"tag":129,"props":429,"children":430},{},[431,436,453],{"type":66,"tag":151,"props":432,"children":433},{},[434],{"type":72,"value":435},"Import alias",{"type":66,"tag":151,"props":437,"children":438},{},[439,445,447],{"type":66,"tag":89,"props":440,"children":442},{"className":441},[],[443],{"type":72,"value":444},"tsconfig.json",{"type":72,"value":446}," ",{"type":66,"tag":89,"props":448,"children":450},{"className":449},[],[451],{"type":72,"value":452},"paths",{"type":66,"tag":151,"props":454,"children":455},{},[456,462,463,469,470,476],{"type":66,"tag":89,"props":457,"children":459},{"className":458},[],[460],{"type":72,"value":461},"@\u002Fdb",{"type":72,"value":275},{"type":66,"tag":89,"props":464,"children":466},{"className":465},[],[467],{"type":72,"value":468},"~\u002Fdb",{"type":72,"value":275},{"type":66,"tag":89,"props":471,"children":473},{"className":472},[],[474],{"type":72,"value":475},"#\u002Fdb",{"type":72,"value":477},", or a relative path",{"type":66,"tag":224,"props":479,"children":481},{"id":480},"_2-shape-the-storage",[482],{"type":72,"value":483},"2. Shape the storage",{"type":66,"tag":75,"props":485,"children":486},{},[487],{"type":72,"value":488},"Four logical records. Whatever the engine, keep these keys — the store methods\nlook records up by exactly these:",{"type":66,"tag":121,"props":490,"children":491},{},[492,513],{"type":66,"tag":125,"props":493,"children":494},{},[495],{"type":66,"tag":129,"props":496,"children":497},{},[498,503,508],{"type":66,"tag":133,"props":499,"children":500},{},[501],{"type":72,"value":502},"Record",{"type":66,"tag":133,"props":504,"children":505},{},[506],{"type":72,"value":507},"Key",{"type":66,"tag":133,"props":509,"children":510},{},[511],{"type":72,"value":512},"Fields",{"type":66,"tag":144,"props":514,"children":515},{},[516,544,604,669],{"type":66,"tag":129,"props":517,"children":518},{},[519,524,533],{"type":66,"tag":151,"props":520,"children":521},{},[522],{"type":72,"value":523},"thread",{"type":66,"tag":151,"props":525,"children":526},{},[527],{"type":66,"tag":89,"props":528,"children":530},{"className":529},[],[531],{"type":72,"value":532},"threadId",{"type":66,"tag":151,"props":534,"children":535},{},[536,542],{"type":66,"tag":89,"props":537,"children":539},{"className":538},[],[540],{"type":72,"value":541},"messages",{"type":72,"value":543}," (array, full transcript)",{"type":66,"tag":129,"props":545,"children":546},{},[547,552,561],{"type":66,"tag":151,"props":548,"children":549},{},[550],{"type":72,"value":551},"run",{"type":66,"tag":151,"props":553,"children":554},{},[555],{"type":66,"tag":89,"props":556,"children":558},{"className":557},[],[559],{"type":72,"value":560},"runId",{"type":66,"tag":151,"props":562,"children":563},{},[564,569,570,576,577,583,584,590,591,597,598],{"type":66,"tag":89,"props":565,"children":567},{"className":566},[],[568],{"type":72,"value":532},{"type":72,"value":275},{"type":66,"tag":89,"props":571,"children":573},{"className":572},[],[574],{"type":72,"value":575},"status",{"type":72,"value":275},{"type":66,"tag":89,"props":578,"children":580},{"className":579},[],[581],{"type":72,"value":582},"startedAt",{"type":72,"value":275},{"type":66,"tag":89,"props":585,"children":587},{"className":586},[],[588],{"type":72,"value":589},"finishedAt?",{"type":72,"value":275},{"type":66,"tag":89,"props":592,"children":594},{"className":593},[],[595],{"type":72,"value":596},"error?",{"type":72,"value":275},{"type":66,"tag":89,"props":599,"children":601},{"className":600},[],[602],{"type":72,"value":603},"usage?",{"type":66,"tag":129,"props":605,"children":606},{},[607,612,621],{"type":66,"tag":151,"props":608,"children":609},{},[610],{"type":72,"value":611},"interrupt",{"type":66,"tag":151,"props":613,"children":614},{},[615],{"type":66,"tag":89,"props":616,"children":618},{"className":617},[],[619],{"type":72,"value":620},"interruptId",{"type":66,"tag":151,"props":622,"children":623},{},[624,629,630,635,636,641,642,648,649,655,656,662,663],{"type":66,"tag":89,"props":625,"children":627},{"className":626},[],[628],{"type":72,"value":560},{"type":72,"value":275},{"type":66,"tag":89,"props":631,"children":633},{"className":632},[],[634],{"type":72,"value":532},{"type":72,"value":275},{"type":66,"tag":89,"props":637,"children":639},{"className":638},[],[640],{"type":72,"value":575},{"type":72,"value":275},{"type":66,"tag":89,"props":643,"children":645},{"className":644},[],[646],{"type":72,"value":647},"requestedAt",{"type":72,"value":275},{"type":66,"tag":89,"props":650,"children":652},{"className":651},[],[653],{"type":72,"value":654},"resolvedAt?",{"type":72,"value":275},{"type":66,"tag":89,"props":657,"children":659},{"className":658},[],[660],{"type":72,"value":661},"payload",{"type":72,"value":275},{"type":66,"tag":89,"props":664,"children":666},{"className":665},[],[667],{"type":72,"value":668},"response?",{"type":66,"tag":129,"props":670,"children":671},{},[672,677,686],{"type":66,"tag":151,"props":673,"children":674},{},[675],{"type":72,"value":676},"metadata",{"type":66,"tag":151,"props":678,"children":679},{},[680],{"type":66,"tag":89,"props":681,"children":683},{"className":682},[],[684],{"type":72,"value":685},"(namespace, key)",{"type":66,"tag":151,"props":687,"children":688},{},[689],{"type":66,"tag":89,"props":690,"children":692},{"className":691},[],[693],{"type":72,"value":694},"value",{"type":66,"tag":696,"props":697,"children":698},"ul",{},[699,720,753,773],{"type":66,"tag":700,"props":701,"children":702},"li",{},[703,705,710,712,718],{"type":72,"value":704},"Timestamps are ",{"type":66,"tag":81,"props":706,"children":707},{},[708],{"type":72,"value":709},"epoch milliseconds",{"type":72,"value":711}," (",{"type":66,"tag":89,"props":713,"children":715},{"className":714},[],[716],{"type":72,"value":717},"number",{"type":72,"value":719},") in records. Store them\nhowever the engine prefers and convert in the mapper.",{"type":66,"tag":700,"props":721,"children":722},{},[723,728,730,735,737,743,745,751],{"type":66,"tag":89,"props":724,"children":726},{"className":725},[],[727],{"type":72,"value":685},{"type":72,"value":729}," is a ",{"type":66,"tag":81,"props":731,"children":732},{},[733],{"type":72,"value":734},"composite",{"type":72,"value":736}," key. Never join with a separator —\n",{"type":66,"tag":89,"props":738,"children":740},{"className":739},[],[741],{"type":72,"value":742},"('a:b','c')",{"type":72,"value":744}," and ",{"type":66,"tag":89,"props":746,"children":748},{"className":747},[],[749],{"type":72,"value":750},"('a','b:c')",{"type":72,"value":752}," must stay distinct records, and the\nconformance suite checks it.",{"type":66,"tag":700,"props":754,"children":755},{},[756,758,764,765,771],{"type":72,"value":757},"Index ",{"type":66,"tag":89,"props":759,"children":761},{"className":760},[],[762],{"type":72,"value":763},"runs(threadId, status)",{"type":72,"value":744},{"type":66,"tag":89,"props":766,"children":768},{"className":767},[],[769],{"type":72,"value":770},"interrupts(threadId, requestedAt)",{"type":72,"value":772}," — those\nare the two listing paths.",{"type":66,"tag":700,"props":774,"children":775},{},[776,778,784],{"type":72,"value":777},"Extra app-owned columns are fine (a ",{"type":66,"tag":89,"props":779,"children":781},{"className":780},[],[782],{"type":72,"value":783},"userId",{"type":72,"value":785},", audit columns) as long as they\nare nullable or defaulted. The stores never read columns they do not know\nabout.",{"type":66,"tag":224,"props":787,"children":789},{"id":788},"_3-the-five-invariants",[790],{"type":72,"value":791},"3. The five invariants",{"type":66,"tag":75,"props":793,"children":794},{},[795],{"type":72,"value":796},"Getting one of these wrong is the usual source of stuck approvals and wiped\nhistory. They are engine-independent:",{"type":66,"tag":798,"props":799,"children":800},"ol",{},[801,817,846,877,909],{"type":66,"tag":700,"props":802,"children":803},{},[804,815],{"type":66,"tag":81,"props":805,"children":806},{},[807,813],{"type":66,"tag":89,"props":808,"children":810},{"className":809},[],[811],{"type":72,"value":812},"saveThread",{"type":72,"value":814}," is a full overwrite",{"type":72,"value":816},", never an append. The argument is the\ncomplete authoritative transcript.",{"type":66,"tag":700,"props":818,"children":819},{},[820,837,839,845],{"type":66,"tag":81,"props":821,"children":822},{},[823,829,831],{"type":66,"tag":89,"props":824,"children":826},{"className":825},[],[827],{"type":72,"value":828},"loadThread",{"type":72,"value":830}," returns ",{"type":66,"tag":89,"props":832,"children":834},{"className":833},[],[835],{"type":72,"value":836},"[]",{"type":72,"value":838}," for an unknown thread, never ",{"type":66,"tag":89,"props":840,"children":842},{"className":841},[],[843],{"type":72,"value":844},"null",{"type":72,"value":222},{"type":66,"tag":700,"props":847,"children":848},{},[849,860,862,867,869,875],{"type":66,"tag":81,"props":850,"children":851},{},[852,858],{"type":66,"tag":89,"props":853,"children":855},{"className":854},[],[856],{"type":72,"value":857},"createOrResume",{"type":72,"value":859}," is insert-if-absent",{"type":72,"value":861}," — an existing ",{"type":66,"tag":89,"props":863,"children":865},{"className":864},[],[866],{"type":72,"value":560},{"type":72,"value":868}," comes back\n",{"type":66,"tag":870,"props":871,"children":872},"em",{},[873],{"type":72,"value":874},"unchanged",{"type":72,"value":876},", ignoring the new field values. Resume and double-submit depend\non it. After a racy insert, re-read rather than trusting your own write.",{"type":66,"tag":700,"props":878,"children":879},{},[880,891,893,899,901,907],{"type":66,"tag":81,"props":881,"children":882},{},[883,889],{"type":66,"tag":89,"props":884,"children":886},{"className":885},[],[887],{"type":72,"value":888},"runs.update",{"type":72,"value":890}," on an unknown id is a silent no-op",{"type":72,"value":892}," — it must not throw and\nmust not insert. (Drivers that throw on zero rows affected need the\n",{"type":66,"tag":89,"props":894,"children":896},{"className":895},[],[897],{"type":72,"value":898},"updateMany",{"type":72,"value":900},"-style call, not the ",{"type":66,"tag":89,"props":902,"children":904},{"className":903},[],[905],{"type":72,"value":906},"update",{"type":72,"value":908},"-one-or-throw call.)",{"type":66,"tag":700,"props":910,"children":911},{},[912,922,924,930,932,937],{"type":66,"tag":81,"props":913,"children":914},{},[915,921],{"type":66,"tag":89,"props":916,"children":918},{"className":917},[],[919],{"type":72,"value":920},"interrupts.create",{"type":72,"value":859},{"type":72,"value":923}," — never clobber a resolved\ninterrupt back to pending. Every ",{"type":66,"tag":89,"props":925,"children":927},{"className":926},[],[928],{"type":72,"value":929},"list*",{"type":72,"value":931}," is ordered by ",{"type":66,"tag":89,"props":933,"children":935},{"className":934},[],[936],{"type":72,"value":647},{"type":72,"value":938},"\nascending.",{"type":66,"tag":75,"props":940,"children":941},{},[942,944,950],{"type":72,"value":943},"Row mappers omit absent optionals\n(",{"type":66,"tag":89,"props":945,"children":947},{"className":946},[],[948],{"type":72,"value":949},"...(row.error != null ? { error: row.error } : {})",{"type":72,"value":951},") so records compare\ncleanly against the reference in-memory backend.",{"type":66,"tag":224,"props":953,"children":955},{"id":954},"_4-write-srclibchat-persistencets",[956,958],{"type":72,"value":957},"4. Write ",{"type":66,"tag":89,"props":959,"children":961},{"className":960},[],[962],{"type":72,"value":94},{"type":66,"tag":75,"props":964,"children":965},{},[966,968,974],{"type":72,"value":967},"Four factories and one assembly. Postgres via ",{"type":66,"tag":89,"props":969,"children":971},{"className":970},[],[972],{"type":72,"value":973},"pg",{"type":72,"value":975}," shown here; the shape is the\nsame for any driver.",{"type":66,"tag":977,"props":978,"children":984},"pre",{"className":979,"code":980,"language":981,"meta":982,"style":983},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { defineAIPersistence } from '@tanstack\u002Fai-persistence'\nimport type { Pool } from 'pg'\nimport type {\n  ChatPersistence,\n  MessageStore,\n  RunStore,\n} from '@tanstack\u002Fai-persistence'\n\nimport { pool } from '@\u002Fdb'\n\nfunction createMessageStore(db: Pool): MessageStore {\n  return {\n    async loadThread(threadId) {\n      const { rows } = await db.query(\n        'SELECT messages_json FROM chat_threads WHERE thread_id = $1',\n        [threadId],\n      )\n      return rows[0]?.messages_json ?? []\n    },\n    \u002F\u002F Full overwrite — `messages` is the complete authoritative transcript.\n    async saveThread(threadId, messages) {\n      await db.query(\n        `INSERT INTO chat_threads (thread_id, messages_json, updated_at)\n         VALUES ($1, $2, $3)\n         ON CONFLICT (thread_id)\n         DO UPDATE SET messages_json = EXCLUDED.messages_json,\n                       updated_at = EXCLUDED.updated_at`,\n        [threadId, JSON.stringify(messages), Date.now()],\n      )\n    },\n  }\n}\n\nfunction createRunStore(db: Pool): RunStore {\n  async function get(runId: string) {\n    const { rows } = await db.query(\n      'SELECT * FROM chat_runs WHERE run_id = $1',\n      [runId],\n    )\n    return rows[0] ? mapRun(rows[0]) : null\n  }\n\n  return {\n    get,\n    \u002F\u002F Idempotent: an existing runId is returned untouched.\n    async createOrResume({ runId, threadId, startedAt, status }) {\n      const existing = await get(runId)\n      if (existing) return existing\n\n      await db.query(\n        `INSERT INTO chat_runs (run_id, thread_id, status, started_at)\n         VALUES ($1, $2, $3, $4)\n         ON CONFLICT (run_id) DO NOTHING`,\n        [runId, threadId, status ?? 'running', startedAt],\n      )\n      \u002F\u002F Re-read: a concurrent createOrResume may have won the race, and that\n      \u002F\u002F row is the authoritative one.\n      const stored = await get(runId)\n      return (\n        stored ?? { runId, threadId, status: status ?? 'running', startedAt }\n      )\n    },\n    \u002F\u002F ... update (no-op on unknown id), findActiveRun (latest 'running')\n  }\n}\n\n\u002F** The four chat state stores backed by the app's database. *\u002F\nexport const chatPersistence: ChatPersistence = defineAIPersistence({\n  stores: {\n    messages: createMessageStore(pool),\n    runs: createRunStore(pool),\n    interrupts: createInterruptStore(pool),\n    metadata: createMetadataStore(pool),\n  },\n})\n","ts","ignore","",[985],{"type":66,"tag":89,"props":986,"children":987},{"__ignoreMap":983},[988,1038,1080,1097,1111,1124,1137,1162,1172,1209,1217,1268,1281,1313,1364,1387,1409,1418,1466,1475,1485,1524,1549,1563,1572,1581,1590,1608,1677,1685,1693,1702,1711,1719,1761,1805,1850,1872,1893,1902,1969,1977,1985,1997,2010,2019,2078,2116,2149,2157,2181,2194,2203,2220,2281,2289,2298,2307,2344,2357,2432,2440,2448,2457,2465,2473,2481,2490,2535,2552,2578,2603,2629,2655,2664],{"type":66,"tag":989,"props":990,"children":993},"span",{"class":991,"line":992},"line",1,[994,1000,1006,1012,1017,1022,1027,1033],{"type":66,"tag":989,"props":995,"children":997},{"style":996},"--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic",[998],{"type":72,"value":999},"import",{"type":66,"tag":989,"props":1001,"children":1003},{"style":1002},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[1004],{"type":72,"value":1005}," {",{"type":66,"tag":989,"props":1007,"children":1009},{"style":1008},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[1010],{"type":72,"value":1011}," defineAIPersistence",{"type":66,"tag":989,"props":1013,"children":1014},{"style":1002},[1015],{"type":72,"value":1016}," }",{"type":66,"tag":989,"props":1018,"children":1019},{"style":996},[1020],{"type":72,"value":1021}," from",{"type":66,"tag":989,"props":1023,"children":1024},{"style":1002},[1025],{"type":72,"value":1026}," '",{"type":66,"tag":989,"props":1028,"children":1030},{"style":1029},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[1031],{"type":72,"value":1032},"@tanstack\u002Fai-persistence",{"type":66,"tag":989,"props":1034,"children":1035},{"style":1002},[1036],{"type":72,"value":1037},"'\n",{"type":66,"tag":989,"props":1039,"children":1041},{"class":991,"line":1040},2,[1042,1046,1051,1055,1060,1064,1068,1072,1076],{"type":66,"tag":989,"props":1043,"children":1044},{"style":996},[1045],{"type":72,"value":999},{"type":66,"tag":989,"props":1047,"children":1048},{"style":996},[1049],{"type":72,"value":1050}," type",{"type":66,"tag":989,"props":1052,"children":1053},{"style":1002},[1054],{"type":72,"value":1005},{"type":66,"tag":989,"props":1056,"children":1057},{"style":1008},[1058],{"type":72,"value":1059}," Pool",{"type":66,"tag":989,"props":1061,"children":1062},{"style":1002},[1063],{"type":72,"value":1016},{"type":66,"tag":989,"props":1065,"children":1066},{"style":996},[1067],{"type":72,"value":1021},{"type":66,"tag":989,"props":1069,"children":1070},{"style":1002},[1071],{"type":72,"value":1026},{"type":66,"tag":989,"props":1073,"children":1074},{"style":1029},[1075],{"type":72,"value":973},{"type":66,"tag":989,"props":1077,"children":1078},{"style":1002},[1079],{"type":72,"value":1037},{"type":66,"tag":989,"props":1081,"children":1083},{"class":991,"line":1082},3,[1084,1088,1092],{"type":66,"tag":989,"props":1085,"children":1086},{"style":996},[1087],{"type":72,"value":999},{"type":66,"tag":989,"props":1089,"children":1090},{"style":996},[1091],{"type":72,"value":1050},{"type":66,"tag":989,"props":1093,"children":1094},{"style":1002},[1095],{"type":72,"value":1096}," {\n",{"type":66,"tag":989,"props":1098,"children":1100},{"class":991,"line":1099},4,[1101,1106],{"type":66,"tag":989,"props":1102,"children":1103},{"style":1008},[1104],{"type":72,"value":1105},"  ChatPersistence",{"type":66,"tag":989,"props":1107,"children":1108},{"style":1002},[1109],{"type":72,"value":1110},",\n",{"type":66,"tag":989,"props":1112,"children":1114},{"class":991,"line":1113},5,[1115,1120],{"type":66,"tag":989,"props":1116,"children":1117},{"style":1008},[1118],{"type":72,"value":1119},"  MessageStore",{"type":66,"tag":989,"props":1121,"children":1122},{"style":1002},[1123],{"type":72,"value":1110},{"type":66,"tag":989,"props":1125,"children":1127},{"class":991,"line":1126},6,[1128,1133],{"type":66,"tag":989,"props":1129,"children":1130},{"style":1008},[1131],{"type":72,"value":1132},"  RunStore",{"type":66,"tag":989,"props":1134,"children":1135},{"style":1002},[1136],{"type":72,"value":1110},{"type":66,"tag":989,"props":1138,"children":1140},{"class":991,"line":1139},7,[1141,1146,1150,1154,1158],{"type":66,"tag":989,"props":1142,"children":1143},{"style":1002},[1144],{"type":72,"value":1145},"}",{"type":66,"tag":989,"props":1147,"children":1148},{"style":996},[1149],{"type":72,"value":1021},{"type":66,"tag":989,"props":1151,"children":1152},{"style":1002},[1153],{"type":72,"value":1026},{"type":66,"tag":989,"props":1155,"children":1156},{"style":1029},[1157],{"type":72,"value":1032},{"type":66,"tag":989,"props":1159,"children":1160},{"style":1002},[1161],{"type":72,"value":1037},{"type":66,"tag":989,"props":1163,"children":1165},{"class":991,"line":1164},8,[1166],{"type":66,"tag":989,"props":1167,"children":1169},{"emptyLinePlaceholder":1168},true,[1170],{"type":72,"value":1171},"\n",{"type":66,"tag":989,"props":1173,"children":1175},{"class":991,"line":1174},9,[1176,1180,1184,1189,1193,1197,1201,1205],{"type":66,"tag":989,"props":1177,"children":1178},{"style":996},[1179],{"type":72,"value":999},{"type":66,"tag":989,"props":1181,"children":1182},{"style":1002},[1183],{"type":72,"value":1005},{"type":66,"tag":989,"props":1185,"children":1186},{"style":1008},[1187],{"type":72,"value":1188}," pool",{"type":66,"tag":989,"props":1190,"children":1191},{"style":1002},[1192],{"type":72,"value":1016},{"type":66,"tag":989,"props":1194,"children":1195},{"style":996},[1196],{"type":72,"value":1021},{"type":66,"tag":989,"props":1198,"children":1199},{"style":1002},[1200],{"type":72,"value":1026},{"type":66,"tag":989,"props":1202,"children":1203},{"style":1029},[1204],{"type":72,"value":461},{"type":66,"tag":989,"props":1206,"children":1207},{"style":1002},[1208],{"type":72,"value":1037},{"type":66,"tag":989,"props":1210,"children":1212},{"class":991,"line":1211},10,[1213],{"type":66,"tag":989,"props":1214,"children":1215},{"emptyLinePlaceholder":1168},[1216],{"type":72,"value":1171},{"type":66,"tag":989,"props":1218,"children":1220},{"class":991,"line":1219},11,[1221,1227,1233,1238,1244,1249,1254,1259,1264],{"type":66,"tag":989,"props":1222,"children":1224},{"style":1223},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[1225],{"type":72,"value":1226},"function",{"type":66,"tag":989,"props":1228,"children":1230},{"style":1229},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[1231],{"type":72,"value":1232}," createMessageStore",{"type":66,"tag":989,"props":1234,"children":1235},{"style":1002},[1236],{"type":72,"value":1237},"(",{"type":66,"tag":989,"props":1239,"children":1241},{"style":1240},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic",[1242],{"type":72,"value":1243},"db",{"type":66,"tag":989,"props":1245,"children":1246},{"style":1002},[1247],{"type":72,"value":1248},":",{"type":66,"tag":989,"props":1250,"children":1252},{"style":1251},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[1253],{"type":72,"value":1059},{"type":66,"tag":989,"props":1255,"children":1256},{"style":1002},[1257],{"type":72,"value":1258},"):",{"type":66,"tag":989,"props":1260,"children":1261},{"style":1251},[1262],{"type":72,"value":1263}," MessageStore",{"type":66,"tag":989,"props":1265,"children":1266},{"style":1002},[1267],{"type":72,"value":1096},{"type":66,"tag":989,"props":1269,"children":1271},{"class":991,"line":1270},12,[1272,1277],{"type":66,"tag":989,"props":1273,"children":1274},{"style":996},[1275],{"type":72,"value":1276},"  return",{"type":66,"tag":989,"props":1278,"children":1279},{"style":1002},[1280],{"type":72,"value":1096},{"type":66,"tag":989,"props":1282,"children":1284},{"class":991,"line":1283},13,[1285,1290,1296,1300,1304,1309],{"type":66,"tag":989,"props":1286,"children":1287},{"style":1223},[1288],{"type":72,"value":1289},"    async",{"type":66,"tag":989,"props":1291,"children":1293},{"style":1292},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[1294],{"type":72,"value":1295}," loadThread",{"type":66,"tag":989,"props":1297,"children":1298},{"style":1002},[1299],{"type":72,"value":1237},{"type":66,"tag":989,"props":1301,"children":1302},{"style":1240},[1303],{"type":72,"value":532},{"type":66,"tag":989,"props":1305,"children":1306},{"style":1002},[1307],{"type":72,"value":1308},")",{"type":66,"tag":989,"props":1310,"children":1311},{"style":1002},[1312],{"type":72,"value":1096},{"type":66,"tag":989,"props":1314,"children":1316},{"class":991,"line":1315},14,[1317,1322,1326,1331,1335,1340,1345,1350,1354,1359],{"type":66,"tag":989,"props":1318,"children":1319},{"style":1223},[1320],{"type":72,"value":1321},"      const",{"type":66,"tag":989,"props":1323,"children":1324},{"style":1002},[1325],{"type":72,"value":1005},{"type":66,"tag":989,"props":1327,"children":1328},{"style":1008},[1329],{"type":72,"value":1330}," rows",{"type":66,"tag":989,"props":1332,"children":1333},{"style":1002},[1334],{"type":72,"value":1016},{"type":66,"tag":989,"props":1336,"children":1337},{"style":1002},[1338],{"type":72,"value":1339}," =",{"type":66,"tag":989,"props":1341,"children":1342},{"style":996},[1343],{"type":72,"value":1344}," await",{"type":66,"tag":989,"props":1346,"children":1347},{"style":1008},[1348],{"type":72,"value":1349}," db",{"type":66,"tag":989,"props":1351,"children":1352},{"style":1002},[1353],{"type":72,"value":222},{"type":66,"tag":989,"props":1355,"children":1356},{"style":1229},[1357],{"type":72,"value":1358},"query",{"type":66,"tag":989,"props":1360,"children":1361},{"style":1292},[1362],{"type":72,"value":1363},"(\n",{"type":66,"tag":989,"props":1365,"children":1367},{"class":991,"line":1366},15,[1368,1373,1378,1383],{"type":66,"tag":989,"props":1369,"children":1370},{"style":1002},[1371],{"type":72,"value":1372},"        '",{"type":66,"tag":989,"props":1374,"children":1375},{"style":1029},[1376],{"type":72,"value":1377},"SELECT messages_json FROM chat_threads WHERE thread_id = $1",{"type":66,"tag":989,"props":1379,"children":1380},{"style":1002},[1381],{"type":72,"value":1382},"'",{"type":66,"tag":989,"props":1384,"children":1385},{"style":1002},[1386],{"type":72,"value":1110},{"type":66,"tag":989,"props":1388,"children":1390},{"class":991,"line":1389},16,[1391,1396,1400,1405],{"type":66,"tag":989,"props":1392,"children":1393},{"style":1292},[1394],{"type":72,"value":1395},"        [",{"type":66,"tag":989,"props":1397,"children":1398},{"style":1008},[1399],{"type":72,"value":532},{"type":66,"tag":989,"props":1401,"children":1402},{"style":1292},[1403],{"type":72,"value":1404},"]",{"type":66,"tag":989,"props":1406,"children":1407},{"style":1002},[1408],{"type":72,"value":1110},{"type":66,"tag":989,"props":1410,"children":1412},{"class":991,"line":1411},17,[1413],{"type":66,"tag":989,"props":1414,"children":1415},{"style":1292},[1416],{"type":72,"value":1417},"      )\n",{"type":66,"tag":989,"props":1419,"children":1421},{"class":991,"line":1420},18,[1422,1427,1431,1436,1442,1446,1451,1456,1461],{"type":66,"tag":989,"props":1423,"children":1424},{"style":996},[1425],{"type":72,"value":1426},"      return",{"type":66,"tag":989,"props":1428,"children":1429},{"style":1008},[1430],{"type":72,"value":1330},{"type":66,"tag":989,"props":1432,"children":1433},{"style":1292},[1434],{"type":72,"value":1435},"[",{"type":66,"tag":989,"props":1437,"children":1439},{"style":1438},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[1440],{"type":72,"value":1441},"0",{"type":66,"tag":989,"props":1443,"children":1444},{"style":1292},[1445],{"type":72,"value":1404},{"type":66,"tag":989,"props":1447,"children":1448},{"style":1002},[1449],{"type":72,"value":1450},"?.",{"type":66,"tag":989,"props":1452,"children":1453},{"style":1008},[1454],{"type":72,"value":1455},"messages_json",{"type":66,"tag":989,"props":1457,"children":1458},{"style":1002},[1459],{"type":72,"value":1460}," ??",{"type":66,"tag":989,"props":1462,"children":1463},{"style":1292},[1464],{"type":72,"value":1465}," []\n",{"type":66,"tag":989,"props":1467,"children":1469},{"class":991,"line":1468},19,[1470],{"type":66,"tag":989,"props":1471,"children":1472},{"style":1002},[1473],{"type":72,"value":1474},"    },\n",{"type":66,"tag":989,"props":1476,"children":1478},{"class":991,"line":1477},20,[1479],{"type":66,"tag":989,"props":1480,"children":1482},{"style":1481},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[1483],{"type":72,"value":1484},"    \u002F\u002F Full overwrite — `messages` is the complete authoritative transcript.\n",{"type":66,"tag":989,"props":1486,"children":1488},{"class":991,"line":1487},21,[1489,1493,1498,1502,1506,1511,1516,1520],{"type":66,"tag":989,"props":1490,"children":1491},{"style":1223},[1492],{"type":72,"value":1289},{"type":66,"tag":989,"props":1494,"children":1495},{"style":1292},[1496],{"type":72,"value":1497}," saveThread",{"type":66,"tag":989,"props":1499,"children":1500},{"style":1002},[1501],{"type":72,"value":1237},{"type":66,"tag":989,"props":1503,"children":1504},{"style":1240},[1505],{"type":72,"value":532},{"type":66,"tag":989,"props":1507,"children":1508},{"style":1002},[1509],{"type":72,"value":1510},",",{"type":66,"tag":989,"props":1512,"children":1513},{"style":1240},[1514],{"type":72,"value":1515}," messages",{"type":66,"tag":989,"props":1517,"children":1518},{"style":1002},[1519],{"type":72,"value":1308},{"type":66,"tag":989,"props":1521,"children":1522},{"style":1002},[1523],{"type":72,"value":1096},{"type":66,"tag":989,"props":1525,"children":1527},{"class":991,"line":1526},22,[1528,1533,1537,1541,1545],{"type":66,"tag":989,"props":1529,"children":1530},{"style":996},[1531],{"type":72,"value":1532},"      await",{"type":66,"tag":989,"props":1534,"children":1535},{"style":1008},[1536],{"type":72,"value":1349},{"type":66,"tag":989,"props":1538,"children":1539},{"style":1002},[1540],{"type":72,"value":222},{"type":66,"tag":989,"props":1542,"children":1543},{"style":1229},[1544],{"type":72,"value":1358},{"type":66,"tag":989,"props":1546,"children":1547},{"style":1292},[1548],{"type":72,"value":1363},{"type":66,"tag":989,"props":1550,"children":1552},{"class":991,"line":1551},23,[1553,1558],{"type":66,"tag":989,"props":1554,"children":1555},{"style":1002},[1556],{"type":72,"value":1557},"        `",{"type":66,"tag":989,"props":1559,"children":1560},{"style":1029},[1561],{"type":72,"value":1562},"INSERT INTO chat_threads (thread_id, messages_json, updated_at)\n",{"type":66,"tag":989,"props":1564,"children":1566},{"class":991,"line":1565},24,[1567],{"type":66,"tag":989,"props":1568,"children":1569},{"style":1029},[1570],{"type":72,"value":1571},"         VALUES ($1, $2, $3)\n",{"type":66,"tag":989,"props":1573,"children":1575},{"class":991,"line":1574},25,[1576],{"type":66,"tag":989,"props":1577,"children":1578},{"style":1029},[1579],{"type":72,"value":1580},"         ON CONFLICT (thread_id)\n",{"type":66,"tag":989,"props":1582,"children":1584},{"class":991,"line":1583},26,[1585],{"type":66,"tag":989,"props":1586,"children":1587},{"style":1029},[1588],{"type":72,"value":1589},"         DO UPDATE SET messages_json = EXCLUDED.messages_json,\n",{"type":66,"tag":989,"props":1591,"children":1593},{"class":991,"line":1592},27,[1594,1599,1604],{"type":66,"tag":989,"props":1595,"children":1596},{"style":1029},[1597],{"type":72,"value":1598},"                       updated_at = EXCLUDED.updated_at",{"type":66,"tag":989,"props":1600,"children":1601},{"style":1002},[1602],{"type":72,"value":1603},"`",{"type":66,"tag":989,"props":1605,"children":1606},{"style":1002},[1607],{"type":72,"value":1110},{"type":66,"tag":989,"props":1609,"children":1611},{"class":991,"line":1610},28,[1612,1616,1620,1624,1629,1633,1638,1642,1646,1650,1654,1659,1663,1668,1673],{"type":66,"tag":989,"props":1613,"children":1614},{"style":1292},[1615],{"type":72,"value":1395},{"type":66,"tag":989,"props":1617,"children":1618},{"style":1008},[1619],{"type":72,"value":532},{"type":66,"tag":989,"props":1621,"children":1622},{"style":1002},[1623],{"type":72,"value":1510},{"type":66,"tag":989,"props":1625,"children":1626},{"style":1008},[1627],{"type":72,"value":1628}," JSON",{"type":66,"tag":989,"props":1630,"children":1631},{"style":1002},[1632],{"type":72,"value":222},{"type":66,"tag":989,"props":1634,"children":1635},{"style":1229},[1636],{"type":72,"value":1637},"stringify",{"type":66,"tag":989,"props":1639,"children":1640},{"style":1292},[1641],{"type":72,"value":1237},{"type":66,"tag":989,"props":1643,"children":1644},{"style":1008},[1645],{"type":72,"value":541},{"type":66,"tag":989,"props":1647,"children":1648},{"style":1292},[1649],{"type":72,"value":1308},{"type":66,"tag":989,"props":1651,"children":1652},{"style":1002},[1653],{"type":72,"value":1510},{"type":66,"tag":989,"props":1655,"children":1656},{"style":1008},[1657],{"type":72,"value":1658}," Date",{"type":66,"tag":989,"props":1660,"children":1661},{"style":1002},[1662],{"type":72,"value":222},{"type":66,"tag":989,"props":1664,"children":1665},{"style":1229},[1666],{"type":72,"value":1667},"now",{"type":66,"tag":989,"props":1669,"children":1670},{"style":1292},[1671],{"type":72,"value":1672},"()]",{"type":66,"tag":989,"props":1674,"children":1675},{"style":1002},[1676],{"type":72,"value":1110},{"type":66,"tag":989,"props":1678,"children":1680},{"class":991,"line":1679},29,[1681],{"type":66,"tag":989,"props":1682,"children":1683},{"style":1292},[1684],{"type":72,"value":1417},{"type":66,"tag":989,"props":1686,"children":1688},{"class":991,"line":1687},30,[1689],{"type":66,"tag":989,"props":1690,"children":1691},{"style":1002},[1692],{"type":72,"value":1474},{"type":66,"tag":989,"props":1694,"children":1696},{"class":991,"line":1695},31,[1697],{"type":66,"tag":989,"props":1698,"children":1699},{"style":1002},[1700],{"type":72,"value":1701},"  }\n",{"type":66,"tag":989,"props":1703,"children":1705},{"class":991,"line":1704},32,[1706],{"type":66,"tag":989,"props":1707,"children":1708},{"style":1002},[1709],{"type":72,"value":1710},"}\n",{"type":66,"tag":989,"props":1712,"children":1714},{"class":991,"line":1713},33,[1715],{"type":66,"tag":989,"props":1716,"children":1717},{"emptyLinePlaceholder":1168},[1718],{"type":72,"value":1171},{"type":66,"tag":989,"props":1720,"children":1722},{"class":991,"line":1721},34,[1723,1727,1732,1736,1740,1744,1748,1752,1757],{"type":66,"tag":989,"props":1724,"children":1725},{"style":1223},[1726],{"type":72,"value":1226},{"type":66,"tag":989,"props":1728,"children":1729},{"style":1229},[1730],{"type":72,"value":1731}," createRunStore",{"type":66,"tag":989,"props":1733,"children":1734},{"style":1002},[1735],{"type":72,"value":1237},{"type":66,"tag":989,"props":1737,"children":1738},{"style":1240},[1739],{"type":72,"value":1243},{"type":66,"tag":989,"props":1741,"children":1742},{"style":1002},[1743],{"type":72,"value":1248},{"type":66,"tag":989,"props":1745,"children":1746},{"style":1251},[1747],{"type":72,"value":1059},{"type":66,"tag":989,"props":1749,"children":1750},{"style":1002},[1751],{"type":72,"value":1258},{"type":66,"tag":989,"props":1753,"children":1754},{"style":1251},[1755],{"type":72,"value":1756}," RunStore",{"type":66,"tag":989,"props":1758,"children":1759},{"style":1002},[1760],{"type":72,"value":1096},{"type":66,"tag":989,"props":1762,"children":1764},{"class":991,"line":1763},35,[1765,1770,1775,1780,1784,1788,1792,1797,1801],{"type":66,"tag":989,"props":1766,"children":1767},{"style":1223},[1768],{"type":72,"value":1769},"  async",{"type":66,"tag":989,"props":1771,"children":1772},{"style":1223},[1773],{"type":72,"value":1774}," function",{"type":66,"tag":989,"props":1776,"children":1777},{"style":1229},[1778],{"type":72,"value":1779}," get",{"type":66,"tag":989,"props":1781,"children":1782},{"style":1002},[1783],{"type":72,"value":1237},{"type":66,"tag":989,"props":1785,"children":1786},{"style":1240},[1787],{"type":72,"value":560},{"type":66,"tag":989,"props":1789,"children":1790},{"style":1002},[1791],{"type":72,"value":1248},{"type":66,"tag":989,"props":1793,"children":1794},{"style":1251},[1795],{"type":72,"value":1796}," string",{"type":66,"tag":989,"props":1798,"children":1799},{"style":1002},[1800],{"type":72,"value":1308},{"type":66,"tag":989,"props":1802,"children":1803},{"style":1002},[1804],{"type":72,"value":1096},{"type":66,"tag":989,"props":1806,"children":1808},{"class":991,"line":1807},36,[1809,1814,1818,1822,1826,1830,1834,1838,1842,1846],{"type":66,"tag":989,"props":1810,"children":1811},{"style":1223},[1812],{"type":72,"value":1813},"    const",{"type":66,"tag":989,"props":1815,"children":1816},{"style":1002},[1817],{"type":72,"value":1005},{"type":66,"tag":989,"props":1819,"children":1820},{"style":1008},[1821],{"type":72,"value":1330},{"type":66,"tag":989,"props":1823,"children":1824},{"style":1002},[1825],{"type":72,"value":1016},{"type":66,"tag":989,"props":1827,"children":1828},{"style":1002},[1829],{"type":72,"value":1339},{"type":66,"tag":989,"props":1831,"children":1832},{"style":996},[1833],{"type":72,"value":1344},{"type":66,"tag":989,"props":1835,"children":1836},{"style":1008},[1837],{"type":72,"value":1349},{"type":66,"tag":989,"props":1839,"children":1840},{"style":1002},[1841],{"type":72,"value":222},{"type":66,"tag":989,"props":1843,"children":1844},{"style":1229},[1845],{"type":72,"value":1358},{"type":66,"tag":989,"props":1847,"children":1848},{"style":1292},[1849],{"type":72,"value":1363},{"type":66,"tag":989,"props":1851,"children":1853},{"class":991,"line":1852},37,[1854,1859,1864,1868],{"type":66,"tag":989,"props":1855,"children":1856},{"style":1002},[1857],{"type":72,"value":1858},"      '",{"type":66,"tag":989,"props":1860,"children":1861},{"style":1029},[1862],{"type":72,"value":1863},"SELECT * FROM chat_runs WHERE run_id = $1",{"type":66,"tag":989,"props":1865,"children":1866},{"style":1002},[1867],{"type":72,"value":1382},{"type":66,"tag":989,"props":1869,"children":1870},{"style":1002},[1871],{"type":72,"value":1110},{"type":66,"tag":989,"props":1873,"children":1875},{"class":991,"line":1874},38,[1876,1881,1885,1889],{"type":66,"tag":989,"props":1877,"children":1878},{"style":1292},[1879],{"type":72,"value":1880},"      [",{"type":66,"tag":989,"props":1882,"children":1883},{"style":1008},[1884],{"type":72,"value":560},{"type":66,"tag":989,"props":1886,"children":1887},{"style":1292},[1888],{"type":72,"value":1404},{"type":66,"tag":989,"props":1890,"children":1891},{"style":1002},[1892],{"type":72,"value":1110},{"type":66,"tag":989,"props":1894,"children":1896},{"class":991,"line":1895},39,[1897],{"type":66,"tag":989,"props":1898,"children":1899},{"style":1292},[1900],{"type":72,"value":1901},"    )\n",{"type":66,"tag":989,"props":1903,"children":1905},{"class":991,"line":1904},40,[1906,1911,1915,1919,1923,1928,1933,1938,1942,1947,1951,1955,1960,1964],{"type":66,"tag":989,"props":1907,"children":1908},{"style":996},[1909],{"type":72,"value":1910},"    return",{"type":66,"tag":989,"props":1912,"children":1913},{"style":1008},[1914],{"type":72,"value":1330},{"type":66,"tag":989,"props":1916,"children":1917},{"style":1292},[1918],{"type":72,"value":1435},{"type":66,"tag":989,"props":1920,"children":1921},{"style":1438},[1922],{"type":72,"value":1441},{"type":66,"tag":989,"props":1924,"children":1925},{"style":1292},[1926],{"type":72,"value":1927},"] ",{"type":66,"tag":989,"props":1929,"children":1930},{"style":1002},[1931],{"type":72,"value":1932},"?",{"type":66,"tag":989,"props":1934,"children":1935},{"style":1229},[1936],{"type":72,"value":1937}," mapRun",{"type":66,"tag":989,"props":1939,"children":1940},{"style":1292},[1941],{"type":72,"value":1237},{"type":66,"tag":989,"props":1943,"children":1944},{"style":1008},[1945],{"type":72,"value":1946},"rows",{"type":66,"tag":989,"props":1948,"children":1949},{"style":1292},[1950],{"type":72,"value":1435},{"type":66,"tag":989,"props":1952,"children":1953},{"style":1438},[1954],{"type":72,"value":1441},{"type":66,"tag":989,"props":1956,"children":1957},{"style":1292},[1958],{"type":72,"value":1959},"]) ",{"type":66,"tag":989,"props":1961,"children":1962},{"style":1002},[1963],{"type":72,"value":1248},{"type":66,"tag":989,"props":1965,"children":1966},{"style":1002},[1967],{"type":72,"value":1968}," null\n",{"type":66,"tag":989,"props":1970,"children":1972},{"class":991,"line":1971},41,[1973],{"type":66,"tag":989,"props":1974,"children":1975},{"style":1002},[1976],{"type":72,"value":1701},{"type":66,"tag":989,"props":1978,"children":1980},{"class":991,"line":1979},42,[1981],{"type":66,"tag":989,"props":1982,"children":1983},{"emptyLinePlaceholder":1168},[1984],{"type":72,"value":1171},{"type":66,"tag":989,"props":1986,"children":1988},{"class":991,"line":1987},43,[1989,1993],{"type":66,"tag":989,"props":1990,"children":1991},{"style":996},[1992],{"type":72,"value":1276},{"type":66,"tag":989,"props":1994,"children":1995},{"style":1002},[1996],{"type":72,"value":1096},{"type":66,"tag":989,"props":1998,"children":2000},{"class":991,"line":1999},44,[2001,2006],{"type":66,"tag":989,"props":2002,"children":2003},{"style":1008},[2004],{"type":72,"value":2005},"    get",{"type":66,"tag":989,"props":2007,"children":2008},{"style":1002},[2009],{"type":72,"value":1110},{"type":66,"tag":989,"props":2011,"children":2013},{"class":991,"line":2012},45,[2014],{"type":66,"tag":989,"props":2015,"children":2016},{"style":1481},[2017],{"type":72,"value":2018},"    \u002F\u002F Idempotent: an existing runId is returned untouched.\n",{"type":66,"tag":989,"props":2020,"children":2022},{"class":991,"line":2021},46,[2023,2027,2032,2037,2042,2046,2051,2055,2060,2064,2069,2074],{"type":66,"tag":989,"props":2024,"children":2025},{"style":1223},[2026],{"type":72,"value":1289},{"type":66,"tag":989,"props":2028,"children":2029},{"style":1292},[2030],{"type":72,"value":2031}," createOrResume",{"type":66,"tag":989,"props":2033,"children":2034},{"style":1002},[2035],{"type":72,"value":2036},"({",{"type":66,"tag":989,"props":2038,"children":2039},{"style":1240},[2040],{"type":72,"value":2041}," runId",{"type":66,"tag":989,"props":2043,"children":2044},{"style":1002},[2045],{"type":72,"value":1510},{"type":66,"tag":989,"props":2047,"children":2048},{"style":1240},[2049],{"type":72,"value":2050}," threadId",{"type":66,"tag":989,"props":2052,"children":2053},{"style":1002},[2054],{"type":72,"value":1510},{"type":66,"tag":989,"props":2056,"children":2057},{"style":1240},[2058],{"type":72,"value":2059}," startedAt",{"type":66,"tag":989,"props":2061,"children":2062},{"style":1002},[2063],{"type":72,"value":1510},{"type":66,"tag":989,"props":2065,"children":2066},{"style":1240},[2067],{"type":72,"value":2068}," status",{"type":66,"tag":989,"props":2070,"children":2071},{"style":1002},[2072],{"type":72,"value":2073}," })",{"type":66,"tag":989,"props":2075,"children":2076},{"style":1002},[2077],{"type":72,"value":1096},{"type":66,"tag":989,"props":2079,"children":2081},{"class":991,"line":2080},47,[2082,2086,2091,2095,2099,2103,2107,2111],{"type":66,"tag":989,"props":2083,"children":2084},{"style":1223},[2085],{"type":72,"value":1321},{"type":66,"tag":989,"props":2087,"children":2088},{"style":1008},[2089],{"type":72,"value":2090}," existing",{"type":66,"tag":989,"props":2092,"children":2093},{"style":1002},[2094],{"type":72,"value":1339},{"type":66,"tag":989,"props":2096,"children":2097},{"style":996},[2098],{"type":72,"value":1344},{"type":66,"tag":989,"props":2100,"children":2101},{"style":1229},[2102],{"type":72,"value":1779},{"type":66,"tag":989,"props":2104,"children":2105},{"style":1292},[2106],{"type":72,"value":1237},{"type":66,"tag":989,"props":2108,"children":2109},{"style":1008},[2110],{"type":72,"value":560},{"type":66,"tag":989,"props":2112,"children":2113},{"style":1292},[2114],{"type":72,"value":2115},")\n",{"type":66,"tag":989,"props":2117,"children":2119},{"class":991,"line":2118},48,[2120,2125,2129,2134,2139,2144],{"type":66,"tag":989,"props":2121,"children":2122},{"style":996},[2123],{"type":72,"value":2124},"      if",{"type":66,"tag":989,"props":2126,"children":2127},{"style":1292},[2128],{"type":72,"value":711},{"type":66,"tag":989,"props":2130,"children":2131},{"style":1008},[2132],{"type":72,"value":2133},"existing",{"type":66,"tag":989,"props":2135,"children":2136},{"style":1292},[2137],{"type":72,"value":2138},") ",{"type":66,"tag":989,"props":2140,"children":2141},{"style":996},[2142],{"type":72,"value":2143},"return",{"type":66,"tag":989,"props":2145,"children":2146},{"style":1008},[2147],{"type":72,"value":2148}," existing\n",{"type":66,"tag":989,"props":2150,"children":2152},{"class":991,"line":2151},49,[2153],{"type":66,"tag":989,"props":2154,"children":2155},{"emptyLinePlaceholder":1168},[2156],{"type":72,"value":1171},{"type":66,"tag":989,"props":2158,"children":2160},{"class":991,"line":2159},50,[2161,2165,2169,2173,2177],{"type":66,"tag":989,"props":2162,"children":2163},{"style":996},[2164],{"type":72,"value":1532},{"type":66,"tag":989,"props":2166,"children":2167},{"style":1008},[2168],{"type":72,"value":1349},{"type":66,"tag":989,"props":2170,"children":2171},{"style":1002},[2172],{"type":72,"value":222},{"type":66,"tag":989,"props":2174,"children":2175},{"style":1229},[2176],{"type":72,"value":1358},{"type":66,"tag":989,"props":2178,"children":2179},{"style":1292},[2180],{"type":72,"value":1363},{"type":66,"tag":989,"props":2182,"children":2184},{"class":991,"line":2183},51,[2185,2189],{"type":66,"tag":989,"props":2186,"children":2187},{"style":1002},[2188],{"type":72,"value":1557},{"type":66,"tag":989,"props":2190,"children":2191},{"style":1029},[2192],{"type":72,"value":2193},"INSERT INTO chat_runs (run_id, thread_id, status, started_at)\n",{"type":66,"tag":989,"props":2195,"children":2197},{"class":991,"line":2196},52,[2198],{"type":66,"tag":989,"props":2199,"children":2200},{"style":1029},[2201],{"type":72,"value":2202},"         VALUES ($1, $2, $3, $4)\n",{"type":66,"tag":989,"props":2204,"children":2206},{"class":991,"line":2205},53,[2207,2212,2216],{"type":66,"tag":989,"props":2208,"children":2209},{"style":1029},[2210],{"type":72,"value":2211},"         ON CONFLICT (run_id) DO NOTHING",{"type":66,"tag":989,"props":2213,"children":2214},{"style":1002},[2215],{"type":72,"value":1603},{"type":66,"tag":989,"props":2217,"children":2218},{"style":1002},[2219],{"type":72,"value":1110},{"type":66,"tag":989,"props":2221,"children":2223},{"class":991,"line":2222},54,[2224,2228,2232,2236,2240,2244,2248,2252,2256,2261,2265,2269,2273,2277],{"type":66,"tag":989,"props":2225,"children":2226},{"style":1292},[2227],{"type":72,"value":1395},{"type":66,"tag":989,"props":2229,"children":2230},{"style":1008},[2231],{"type":72,"value":560},{"type":66,"tag":989,"props":2233,"children":2234},{"style":1002},[2235],{"type":72,"value":1510},{"type":66,"tag":989,"props":2237,"children":2238},{"style":1008},[2239],{"type":72,"value":2050},{"type":66,"tag":989,"props":2241,"children":2242},{"style":1002},[2243],{"type":72,"value":1510},{"type":66,"tag":989,"props":2245,"children":2246},{"style":1008},[2247],{"type":72,"value":2068},{"type":66,"tag":989,"props":2249,"children":2250},{"style":1002},[2251],{"type":72,"value":1460},{"type":66,"tag":989,"props":2253,"children":2254},{"style":1002},[2255],{"type":72,"value":1026},{"type":66,"tag":989,"props":2257,"children":2258},{"style":1029},[2259],{"type":72,"value":2260},"running",{"type":66,"tag":989,"props":2262,"children":2263},{"style":1002},[2264],{"type":72,"value":1382},{"type":66,"tag":989,"props":2266,"children":2267},{"style":1002},[2268],{"type":72,"value":1510},{"type":66,"tag":989,"props":2270,"children":2271},{"style":1008},[2272],{"type":72,"value":2059},{"type":66,"tag":989,"props":2274,"children":2275},{"style":1292},[2276],{"type":72,"value":1404},{"type":66,"tag":989,"props":2278,"children":2279},{"style":1002},[2280],{"type":72,"value":1110},{"type":66,"tag":989,"props":2282,"children":2284},{"class":991,"line":2283},55,[2285],{"type":66,"tag":989,"props":2286,"children":2287},{"style":1292},[2288],{"type":72,"value":1417},{"type":66,"tag":989,"props":2290,"children":2292},{"class":991,"line":2291},56,[2293],{"type":66,"tag":989,"props":2294,"children":2295},{"style":1481},[2296],{"type":72,"value":2297},"      \u002F\u002F Re-read: a concurrent createOrResume may have won the race, and that\n",{"type":66,"tag":989,"props":2299,"children":2301},{"class":991,"line":2300},57,[2302],{"type":66,"tag":989,"props":2303,"children":2304},{"style":1481},[2305],{"type":72,"value":2306},"      \u002F\u002F row is the authoritative one.\n",{"type":66,"tag":989,"props":2308,"children":2310},{"class":991,"line":2309},58,[2311,2315,2320,2324,2328,2332,2336,2340],{"type":66,"tag":989,"props":2312,"children":2313},{"style":1223},[2314],{"type":72,"value":1321},{"type":66,"tag":989,"props":2316,"children":2317},{"style":1008},[2318],{"type":72,"value":2319}," stored",{"type":66,"tag":989,"props":2321,"children":2322},{"style":1002},[2323],{"type":72,"value":1339},{"type":66,"tag":989,"props":2325,"children":2326},{"style":996},[2327],{"type":72,"value":1344},{"type":66,"tag":989,"props":2329,"children":2330},{"style":1229},[2331],{"type":72,"value":1779},{"type":66,"tag":989,"props":2333,"children":2334},{"style":1292},[2335],{"type":72,"value":1237},{"type":66,"tag":989,"props":2337,"children":2338},{"style":1008},[2339],{"type":72,"value":560},{"type":66,"tag":989,"props":2341,"children":2342},{"style":1292},[2343],{"type":72,"value":2115},{"type":66,"tag":989,"props":2345,"children":2347},{"class":991,"line":2346},59,[2348,2352],{"type":66,"tag":989,"props":2349,"children":2350},{"style":996},[2351],{"type":72,"value":1426},{"type":66,"tag":989,"props":2353,"children":2354},{"style":1292},[2355],{"type":72,"value":2356}," (\n",{"type":66,"tag":989,"props":2358,"children":2360},{"class":991,"line":2359},60,[2361,2366,2370,2374,2378,2382,2386,2390,2394,2398,2402,2407,2411,2415,2419,2423,2427],{"type":66,"tag":989,"props":2362,"children":2363},{"style":1008},[2364],{"type":72,"value":2365},"        stored",{"type":66,"tag":989,"props":2367,"children":2368},{"style":1002},[2369],{"type":72,"value":1460},{"type":66,"tag":989,"props":2371,"children":2372},{"style":1002},[2373],{"type":72,"value":1005},{"type":66,"tag":989,"props":2375,"children":2376},{"style":1240},[2377],{"type":72,"value":2041},{"type":66,"tag":989,"props":2379,"children":2380},{"style":1002},[2381],{"type":72,"value":1510},{"type":66,"tag":989,"props":2383,"children":2384},{"style":1240},[2385],{"type":72,"value":2050},{"type":66,"tag":989,"props":2387,"children":2388},{"style":1002},[2389],{"type":72,"value":1510},{"type":66,"tag":989,"props":2391,"children":2392},{"style":1292},[2393],{"type":72,"value":2068},{"type":66,"tag":989,"props":2395,"children":2396},{"style":1002},[2397],{"type":72,"value":1248},{"type":66,"tag":989,"props":2399,"children":2400},{"style":1240},[2401],{"type":72,"value":2068},{"type":66,"tag":989,"props":2403,"children":2404},{"style":1292},[2405],{"type":72,"value":2406}," ?? ",{"type":66,"tag":989,"props":2408,"children":2409},{"style":1002},[2410],{"type":72,"value":1382},{"type":66,"tag":989,"props":2412,"children":2413},{"style":1029},[2414],{"type":72,"value":2260},{"type":66,"tag":989,"props":2416,"children":2417},{"style":1002},[2418],{"type":72,"value":1382},{"type":66,"tag":989,"props":2420,"children":2421},{"style":1002},[2422],{"type":72,"value":1510},{"type":66,"tag":989,"props":2424,"children":2425},{"style":1240},[2426],{"type":72,"value":2059},{"type":66,"tag":989,"props":2428,"children":2429},{"style":1002},[2430],{"type":72,"value":2431}," }\n",{"type":66,"tag":989,"props":2433,"children":2435},{"class":991,"line":2434},61,[2436],{"type":66,"tag":989,"props":2437,"children":2438},{"style":1292},[2439],{"type":72,"value":1417},{"type":66,"tag":989,"props":2441,"children":2443},{"class":991,"line":2442},62,[2444],{"type":66,"tag":989,"props":2445,"children":2446},{"style":1002},[2447],{"type":72,"value":1474},{"type":66,"tag":989,"props":2449,"children":2451},{"class":991,"line":2450},63,[2452],{"type":66,"tag":989,"props":2453,"children":2454},{"style":1481},[2455],{"type":72,"value":2456},"    \u002F\u002F ... update (no-op on unknown id), findActiveRun (latest 'running')\n",{"type":66,"tag":989,"props":2458,"children":2460},{"class":991,"line":2459},64,[2461],{"type":66,"tag":989,"props":2462,"children":2463},{"style":1002},[2464],{"type":72,"value":1701},{"type":66,"tag":989,"props":2466,"children":2468},{"class":991,"line":2467},65,[2469],{"type":66,"tag":989,"props":2470,"children":2471},{"style":1002},[2472],{"type":72,"value":1710},{"type":66,"tag":989,"props":2474,"children":2476},{"class":991,"line":2475},66,[2477],{"type":66,"tag":989,"props":2478,"children":2479},{"emptyLinePlaceholder":1168},[2480],{"type":72,"value":1171},{"type":66,"tag":989,"props":2482,"children":2484},{"class":991,"line":2483},67,[2485],{"type":66,"tag":989,"props":2486,"children":2487},{"style":1481},[2488],{"type":72,"value":2489},"\u002F** The four chat state stores backed by the app's database. *\u002F\n",{"type":66,"tag":989,"props":2491,"children":2493},{"class":991,"line":2492},68,[2494,2499,2504,2509,2513,2518,2522,2526,2530],{"type":66,"tag":989,"props":2495,"children":2496},{"style":996},[2497],{"type":72,"value":2498},"export",{"type":66,"tag":989,"props":2500,"children":2501},{"style":1223},[2502],{"type":72,"value":2503}," const",{"type":66,"tag":989,"props":2505,"children":2506},{"style":1008},[2507],{"type":72,"value":2508}," chatPersistence",{"type":66,"tag":989,"props":2510,"children":2511},{"style":1002},[2512],{"type":72,"value":1248},{"type":66,"tag":989,"props":2514,"children":2515},{"style":1251},[2516],{"type":72,"value":2517}," ChatPersistence",{"type":66,"tag":989,"props":2519,"children":2520},{"style":1002},[2521],{"type":72,"value":1339},{"type":66,"tag":989,"props":2523,"children":2524},{"style":1229},[2525],{"type":72,"value":1011},{"type":66,"tag":989,"props":2527,"children":2528},{"style":1008},[2529],{"type":72,"value":1237},{"type":66,"tag":989,"props":2531,"children":2532},{"style":1002},[2533],{"type":72,"value":2534},"{\n",{"type":66,"tag":989,"props":2536,"children":2538},{"class":991,"line":2537},69,[2539,2544,2548],{"type":66,"tag":989,"props":2540,"children":2541},{"style":1292},[2542],{"type":72,"value":2543},"  stores",{"type":66,"tag":989,"props":2545,"children":2546},{"style":1002},[2547],{"type":72,"value":1248},{"type":66,"tag":989,"props":2549,"children":2550},{"style":1002},[2551],{"type":72,"value":1096},{"type":66,"tag":989,"props":2553,"children":2555},{"class":991,"line":2554},70,[2556,2561,2565,2569,2574],{"type":66,"tag":989,"props":2557,"children":2558},{"style":1292},[2559],{"type":72,"value":2560},"    messages",{"type":66,"tag":989,"props":2562,"children":2563},{"style":1002},[2564],{"type":72,"value":1248},{"type":66,"tag":989,"props":2566,"children":2567},{"style":1229},[2568],{"type":72,"value":1232},{"type":66,"tag":989,"props":2570,"children":2571},{"style":1008},[2572],{"type":72,"value":2573},"(pool)",{"type":66,"tag":989,"props":2575,"children":2576},{"style":1002},[2577],{"type":72,"value":1110},{"type":66,"tag":989,"props":2579,"children":2581},{"class":991,"line":2580},71,[2582,2587,2591,2595,2599],{"type":66,"tag":989,"props":2583,"children":2584},{"style":1292},[2585],{"type":72,"value":2586},"    runs",{"type":66,"tag":989,"props":2588,"children":2589},{"style":1002},[2590],{"type":72,"value":1248},{"type":66,"tag":989,"props":2592,"children":2593},{"style":1229},[2594],{"type":72,"value":1731},{"type":66,"tag":989,"props":2596,"children":2597},{"style":1008},[2598],{"type":72,"value":2573},{"type":66,"tag":989,"props":2600,"children":2601},{"style":1002},[2602],{"type":72,"value":1110},{"type":66,"tag":989,"props":2604,"children":2606},{"class":991,"line":2605},72,[2607,2612,2616,2621,2625],{"type":66,"tag":989,"props":2608,"children":2609},{"style":1292},[2610],{"type":72,"value":2611},"    interrupts",{"type":66,"tag":989,"props":2613,"children":2614},{"style":1002},[2615],{"type":72,"value":1248},{"type":66,"tag":989,"props":2617,"children":2618},{"style":1229},[2619],{"type":72,"value":2620}," createInterruptStore",{"type":66,"tag":989,"props":2622,"children":2623},{"style":1008},[2624],{"type":72,"value":2573},{"type":66,"tag":989,"props":2626,"children":2627},{"style":1002},[2628],{"type":72,"value":1110},{"type":66,"tag":989,"props":2630,"children":2632},{"class":991,"line":2631},73,[2633,2638,2642,2647,2651],{"type":66,"tag":989,"props":2634,"children":2635},{"style":1292},[2636],{"type":72,"value":2637},"    metadata",{"type":66,"tag":989,"props":2639,"children":2640},{"style":1002},[2641],{"type":72,"value":1248},{"type":66,"tag":989,"props":2643,"children":2644},{"style":1229},[2645],{"type":72,"value":2646}," createMetadataStore",{"type":66,"tag":989,"props":2648,"children":2649},{"style":1008},[2650],{"type":72,"value":2573},{"type":66,"tag":989,"props":2652,"children":2653},{"style":1002},[2654],{"type":72,"value":1110},{"type":66,"tag":989,"props":2656,"children":2658},{"class":991,"line":2657},74,[2659],{"type":66,"tag":989,"props":2660,"children":2661},{"style":1002},[2662],{"type":72,"value":2663},"  },\n",{"type":66,"tag":989,"props":2665,"children":2667},{"class":991,"line":2666},75,[2668,2672],{"type":66,"tag":989,"props":2669,"children":2670},{"style":1002},[2671],{"type":72,"value":1145},{"type":66,"tag":989,"props":2673,"children":2674},{"style":1008},[2675],{"type":72,"value":2115},{"type":66,"tag":75,"props":2677,"children":2678},{},[2679,2681,2686,2688,2694,2696,2702,2704,2710,2712,2718,2720,2725,2726,2732,2733,2739,2740,2745,2747,2753,2755,2761,2763,2768],{"type":72,"value":2680},"Annotate ",{"type":66,"tag":89,"props":2682,"children":2684},{"className":2683},[],[2685],{"type":72,"value":102},{"type":72,"value":2687}," — bare ",{"type":66,"tag":89,"props":2689,"children":2691},{"className":2690},[],[2692],{"type":72,"value":2693},"AIPersistence",{"type":72,"value":2695}," is the all-optional bag and\n",{"type":66,"tag":89,"props":2697,"children":2699},{"className":2698},[],[2700],{"type":72,"value":2701},"withPersistence",{"type":72,"value":2703}," rejects it. There is no ",{"type":66,"tag":89,"props":2705,"children":2707},{"className":2706},[],[2708],{"type":72,"value":2709},"locks",{"type":72,"value":2711}," store: ",{"type":66,"tag":89,"props":2713,"children":2715},{"className":2714},[],[2716],{"type":72,"value":2717},"stores",{"type":72,"value":2719}," accepts only\n",{"type":66,"tag":89,"props":2721,"children":2723},{"className":2722},[],[2724],{"type":72,"value":541},{"type":72,"value":275},{"type":66,"tag":89,"props":2727,"children":2729},{"className":2728},[],[2730],{"type":72,"value":2731},"runs",{"type":72,"value":275},{"type":66,"tag":89,"props":2734,"children":2736},{"className":2735},[],[2737],{"type":72,"value":2738},"interrupts",{"type":72,"value":275},{"type":66,"tag":89,"props":2741,"children":2743},{"className":2742},[],[2744],{"type":72,"value":676},{"type":72,"value":2746},", and anything else throws\n",{"type":66,"tag":89,"props":2748,"children":2750},{"className":2749},[],[2751],{"type":72,"value":2752},"Unknown AIPersistence store key",{"type":72,"value":2754},". Coordination is wired separately with\n",{"type":66,"tag":89,"props":2756,"children":2758},{"className":2757},[],[2759],{"type":72,"value":2760},"withLocks",{"type":72,"value":2762}," (see ",{"type":66,"tag":81,"props":2764,"children":2765},{},[2766],{"type":72,"value":2767},"ai-core\u002Flocks",{"type":72,"value":2769},").",{"type":66,"tag":75,"props":2771,"children":2772},{},[2773,2775,2781],{"type":72,"value":2774},"If the client is per-request (Workers bindings, request-scoped transactions),\nexport a ",{"type":66,"tag":89,"props":2776,"children":2778},{"className":2777},[],[2779],{"type":72,"value":2780},"chatPersistence()",{"type":72,"value":2782}," factory instead of a const and call it inside the\nhandler.",{"type":66,"tag":224,"props":2784,"children":2786},{"id":2785},"engine-notes",[2787],{"type":72,"value":2788},"Engine notes",{"type":66,"tag":75,"props":2790,"children":2791},{},[2792,2811,2812,2817,2819,2825,2827,2832,2834,2839,2841,2847,2849,2854,2856,2861,2863,2869,2871,2877,2879,2884,2886,2892],{"type":66,"tag":81,"props":2793,"children":2794},{},[2795,2797,2802,2803,2809],{"type":72,"value":2796},"Postgres (",{"type":66,"tag":89,"props":2798,"children":2800},{"className":2799},[],[2801],{"type":72,"value":973},{"type":72,"value":275},{"type":66,"tag":89,"props":2804,"children":2806},{"className":2805},[],[2807],{"type":72,"value":2808},"postgres.js",{"type":72,"value":2810},", Neon, Supabase)",{"type":72,"value":87},{"type":66,"tag":89,"props":2813,"children":2815},{"className":2814},[],[2816],{"type":72,"value":412},{"type":72,"value":2818}," columns round-trip\nobjects, so skip the ",{"type":66,"tag":89,"props":2820,"children":2822},{"className":2821},[],[2823],{"type":72,"value":2824},"JSON.stringify",{"type":72,"value":2826}," on read paths (",{"type":66,"tag":89,"props":2828,"children":2830},{"className":2829},[],[2831],{"type":72,"value":973},{"type":72,"value":2833}," parses ",{"type":66,"tag":89,"props":2835,"children":2837},{"className":2836},[],[2838],{"type":72,"value":412},{"type":72,"value":2840}," for\nyou; check what the driver returns before assuming). ",{"type":66,"tag":89,"props":2842,"children":2844},{"className":2843},[],[2845],{"type":72,"value":2846},"bigint",{"type":72,"value":2848}," columns come back\nas strings in ",{"type":66,"tag":89,"props":2850,"children":2852},{"className":2851},[],[2853],{"type":72,"value":973},{"type":72,"value":2855}," — use ",{"type":66,"tag":89,"props":2857,"children":2859},{"className":2858},[],[2860],{"type":72,"value":2846},{"type":72,"value":2862}," with an explicit ",{"type":66,"tag":89,"props":2864,"children":2866},{"className":2865},[],[2867],{"type":72,"value":2868},"Number()",{"type":72,"value":2870}," in the mapper, or\nstore epoch ms in a ",{"type":66,"tag":89,"props":2872,"children":2874},{"className":2873},[],[2875],{"type":72,"value":2876},"double precision",{"type":72,"value":2878},"\u002F",{"type":66,"tag":89,"props":2880,"children":2882},{"className":2881},[],[2883],{"type":72,"value":2846},{"type":72,"value":2885}," and convert once. Composite key\nis ",{"type":66,"tag":89,"props":2887,"children":2889},{"className":2888},[],[2890],{"type":72,"value":2891},"PRIMARY KEY (namespace, key)",{"type":72,"value":222},{"type":66,"tag":75,"props":2894,"children":2895},{},[2896,2901,2903,2908,2910,2916,2918,2924,2926,2932],{"type":66,"tag":81,"props":2897,"children":2898},{},[2899],{"type":72,"value":2900},"Kysely",{"type":72,"value":2902}," — define the four tables in the app's ",{"type":66,"tag":89,"props":2904,"children":2906},{"className":2905},[],[2907],{"type":72,"value":18},{"type":72,"value":2909}," interface, then the\nstores are ",{"type":66,"tag":89,"props":2911,"children":2913},{"className":2912},[],[2914],{"type":72,"value":2915},"db.insertInto('chat_runs').values(...).onConflict((oc) => oc.column('run_id').doNothing())",{"type":72,"value":2917},"\nand ",{"type":66,"tag":89,"props":2919,"children":2921},{"className":2920},[],[2922],{"type":72,"value":2923},".executeTakeFirst()",{"type":72,"value":2925},". ",{"type":66,"tag":89,"props":2927,"children":2929},{"className":2928},[],[2930],{"type":72,"value":2931},"updateTable(...).execute()",{"type":72,"value":2933}," is already a no-op on\nzero matches, so invariant 4 comes free.",{"type":66,"tag":75,"props":2935,"children":2936},{},[2937,2942,2944,2949,2951,2957,2959,2964,2966,2972,2974,2980],{"type":66,"tag":81,"props":2938,"children":2939},{},[2940],{"type":72,"value":2941},"node:sqlite \u002F better-sqlite3",{"type":72,"value":2943}," — the complete implementation is in the guide\nand in ",{"type":66,"tag":89,"props":2945,"children":2947},{"className":2946},[],[2948],{"type":72,"value":220},{"type":72,"value":2950},". Prepared\nstatements at factory scope, ",{"type":66,"tag":89,"props":2952,"children":2954},{"className":2953},[],[2955],{"type":72,"value":2956},"INSERT ... ON CONFLICT",{"type":72,"value":2958},", JSON as ",{"type":66,"tag":89,"props":2960,"children":2962},{"className":2961},[],[2963],{"type":72,"value":72},{"type":72,"value":2965},", epoch ms\nas ",{"type":66,"tag":89,"props":2967,"children":2969},{"className":2968},[],[2970],{"type":72,"value":2971},"integer",{"type":72,"value":2973},". Wrap sync calls in ",{"type":66,"tag":89,"props":2975,"children":2977},{"className":2976},[],[2978],{"type":72,"value":2979},"async",{"type":72,"value":2981}," methods; the contracts are promise-based.",{"type":66,"tag":75,"props":2983,"children":2984},{},[2985,2989,2991,2997,2999,3004,3005,3010,3011,3016,3018,3024,3026,3032,3034,3039,3041,3046,3048,3054,3056,3062,3063,3069,3071,3077,3079,3084,3086,3092],{"type":66,"tag":81,"props":2986,"children":2987},{},[2988],{"type":72,"value":14},{"type":72,"value":2990}," — one collection per record type, ",{"type":66,"tag":89,"props":2992,"children":2994},{"className":2993},[],[2995],{"type":72,"value":2996},"_id",{"type":72,"value":2998}," set to the natural key\n(",{"type":66,"tag":89,"props":3000,"children":3002},{"className":3001},[],[3003],{"type":72,"value":532},{"type":72,"value":275},{"type":66,"tag":89,"props":3006,"children":3008},{"className":3007},[],[3009],{"type":72,"value":560},{"type":72,"value":275},{"type":66,"tag":89,"props":3012,"children":3014},{"className":3013},[],[3015],{"type":72,"value":620},{"type":72,"value":3017},", and ",{"type":66,"tag":89,"props":3019,"children":3021},{"className":3020},[],[3022],{"type":72,"value":3023},"`${namespace}�${key}`",{"type":72,"value":3025}," or a\ncompound unique index on ",{"type":66,"tag":89,"props":3027,"children":3029},{"className":3028},[],[3030],{"type":72,"value":3031},"{ namespace, key }",{"type":72,"value":3033}," — never a ",{"type":66,"tag":89,"props":3035,"children":3037},{"className":3036},[],[3038],{"type":72,"value":1248},{"type":72,"value":3040},"-joined string).\n",{"type":66,"tag":89,"props":3042,"children":3044},{"className":3043},[],[3045],{"type":72,"value":857},{"type":72,"value":3047}," is ",{"type":66,"tag":89,"props":3049,"children":3051},{"className":3050},[],[3052],{"type":72,"value":3053},"updateOne({ _id }, { $setOnInsert: doc }, { upsert: true })",{"type":72,"value":3055},"\nthen a ",{"type":66,"tag":89,"props":3057,"children":3059},{"className":3058},[],[3060],{"type":72,"value":3061},"findOne",{"type":72,"value":87},{"type":66,"tag":89,"props":3064,"children":3066},{"className":3065},[],[3067],{"type":72,"value":3068},"$setOnInsert",{"type":72,"value":3070}," is the insert-if-absent primitive. Guard the\n",{"type":66,"tag":89,"props":3072,"children":3074},{"className":3073},[],[3075],{"type":72,"value":3076},"E11000",{"type":72,"value":3078}," duplicate-key race and re-read. ",{"type":66,"tag":89,"props":3080,"children":3082},{"className":3081},[],[3083],{"type":72,"value":929},{"type":72,"value":3085}," need ",{"type":66,"tag":89,"props":3087,"children":3089},{"className":3088},[],[3090],{"type":72,"value":3091},".sort({ requestedAt: 1 })",{"type":72,"value":222},{"type":66,"tag":75,"props":3094,"children":3095},{},[3096,3101,3103,3108,3110,3116,3118,3123,3125,3130,3132,3137,3138,3143,3145,3150,3152,3158],{"type":66,"tag":81,"props":3097,"children":3098},{},[3099],{"type":72,"value":3100},"Redis \u002F Upstash",{"type":72,"value":3102}," — workable for ",{"type":66,"tag":89,"props":3104,"children":3106},{"className":3105},[],[3107],{"type":72,"value":676},{"type":72,"value":3109}," and excellent for ",{"type":66,"tag":89,"props":3111,"children":3113},{"className":3112},[],[3114],{"type":72,"value":3115},"LockStore",{"type":72,"value":3117},", but\nthink before putting ",{"type":66,"tag":89,"props":3119,"children":3121},{"className":3120},[],[3122],{"type":72,"value":2738},{"type":72,"value":3124}," there: the listings need ordered secondary\nindexes you have to maintain by hand (a sorted set per thread and per run,\nscored by ",{"type":66,"tag":89,"props":3126,"children":3128},{"className":3127},[],[3129],{"type":72,"value":647},{"type":72,"value":3131},"). A common split is Postgres for ",{"type":66,"tag":89,"props":3133,"children":3135},{"className":3134},[],[3136],{"type":72,"value":541},{"type":72,"value":2878},{"type":66,"tag":89,"props":3139,"children":3141},{"className":3140},[],[3142],{"type":72,"value":2731},{"type":72,"value":3144},"\u002F\n",{"type":66,"tag":89,"props":3146,"children":3148},{"className":3147},[],[3149],{"type":72,"value":2738},{"type":72,"value":3151}," and Redis for locks; compose them with ",{"type":66,"tag":89,"props":3153,"children":3155},{"className":3154},[],[3156],{"type":72,"value":3157},"composePersistence",{"type":72,"value":222},{"type":66,"tag":75,"props":3160,"children":3161},{},[3162,3167],{"type":66,"tag":81,"props":3163,"children":3164},{},[3165],{"type":72,"value":3166},"Anything else",{"type":72,"value":3168}," — you only need the five invariants above. The core never\ninspects your storage.",{"type":66,"tag":224,"props":3170,"children":3172},{"id":3171},"adopt-part-of-it",[3173],{"type":72,"value":3174},"Adopt part of it",{"type":66,"tag":75,"props":3176,"children":3177},{},[3178],{"type":72,"value":3179},"You rarely need all four stores at once. Implement what you own and fill the\nrest from another base:",{"type":66,"tag":977,"props":3181,"children":3183},{"className":979,"code":3182,"language":981,"meta":982,"style":983},"import { composePersistence, memoryPersistence } from '@tanstack\u002Fai-persistence'\nimport { messages, runs } from '.\u002Fmy-stores'\n\nexport const chatPersistence = composePersistence(memoryPersistence(), {\n  overrides: { messages, runs },\n})\n",[3184],{"type":66,"tag":89,"props":3185,"children":3186},{"__ignoreMap":983},[3187,3232,3277,3284,3331,3365],{"type":66,"tag":989,"props":3188,"children":3189},{"class":991,"line":992},[3190,3194,3198,3203,3207,3212,3216,3220,3224,3228],{"type":66,"tag":989,"props":3191,"children":3192},{"style":996},[3193],{"type":72,"value":999},{"type":66,"tag":989,"props":3195,"children":3196},{"style":1002},[3197],{"type":72,"value":1005},{"type":66,"tag":989,"props":3199,"children":3200},{"style":1008},[3201],{"type":72,"value":3202}," composePersistence",{"type":66,"tag":989,"props":3204,"children":3205},{"style":1002},[3206],{"type":72,"value":1510},{"type":66,"tag":989,"props":3208,"children":3209},{"style":1008},[3210],{"type":72,"value":3211}," memoryPersistence",{"type":66,"tag":989,"props":3213,"children":3214},{"style":1002},[3215],{"type":72,"value":1016},{"type":66,"tag":989,"props":3217,"children":3218},{"style":996},[3219],{"type":72,"value":1021},{"type":66,"tag":989,"props":3221,"children":3222},{"style":1002},[3223],{"type":72,"value":1026},{"type":66,"tag":989,"props":3225,"children":3226},{"style":1029},[3227],{"type":72,"value":1032},{"type":66,"tag":989,"props":3229,"children":3230},{"style":1002},[3231],{"type":72,"value":1037},{"type":66,"tag":989,"props":3233,"children":3234},{"class":991,"line":1040},[3235,3239,3243,3247,3251,3256,3260,3264,3268,3273],{"type":66,"tag":989,"props":3236,"children":3237},{"style":996},[3238],{"type":72,"value":999},{"type":66,"tag":989,"props":3240,"children":3241},{"style":1002},[3242],{"type":72,"value":1005},{"type":66,"tag":989,"props":3244,"children":3245},{"style":1008},[3246],{"type":72,"value":1515},{"type":66,"tag":989,"props":3248,"children":3249},{"style":1002},[3250],{"type":72,"value":1510},{"type":66,"tag":989,"props":3252,"children":3253},{"style":1008},[3254],{"type":72,"value":3255}," runs",{"type":66,"tag":989,"props":3257,"children":3258},{"style":1002},[3259],{"type":72,"value":1016},{"type":66,"tag":989,"props":3261,"children":3262},{"style":996},[3263],{"type":72,"value":1021},{"type":66,"tag":989,"props":3265,"children":3266},{"style":1002},[3267],{"type":72,"value":1026},{"type":66,"tag":989,"props":3269,"children":3270},{"style":1029},[3271],{"type":72,"value":3272},".\u002Fmy-stores",{"type":66,"tag":989,"props":3274,"children":3275},{"style":1002},[3276],{"type":72,"value":1037},{"type":66,"tag":989,"props":3278,"children":3279},{"class":991,"line":1082},[3280],{"type":66,"tag":989,"props":3281,"children":3282},{"emptyLinePlaceholder":1168},[3283],{"type":72,"value":1171},{"type":66,"tag":989,"props":3285,"children":3286},{"class":991,"line":1099},[3287,3291,3295,3300,3305,3309,3313,3318,3323,3327],{"type":66,"tag":989,"props":3288,"children":3289},{"style":996},[3290],{"type":72,"value":2498},{"type":66,"tag":989,"props":3292,"children":3293},{"style":1223},[3294],{"type":72,"value":2503},{"type":66,"tag":989,"props":3296,"children":3297},{"style":1008},[3298],{"type":72,"value":3299}," chatPersistence ",{"type":66,"tag":989,"props":3301,"children":3302},{"style":1002},[3303],{"type":72,"value":3304},"=",{"type":66,"tag":989,"props":3306,"children":3307},{"style":1229},[3308],{"type":72,"value":3202},{"type":66,"tag":989,"props":3310,"children":3311},{"style":1008},[3312],{"type":72,"value":1237},{"type":66,"tag":989,"props":3314,"children":3315},{"style":1229},[3316],{"type":72,"value":3317},"memoryPersistence",{"type":66,"tag":989,"props":3319,"children":3320},{"style":1008},[3321],{"type":72,"value":3322},"()",{"type":66,"tag":989,"props":3324,"children":3325},{"style":1002},[3326],{"type":72,"value":1510},{"type":66,"tag":989,"props":3328,"children":3329},{"style":1002},[3330],{"type":72,"value":1096},{"type":66,"tag":989,"props":3332,"children":3333},{"class":991,"line":1113},[3334,3339,3343,3347,3351,3355,3360],{"type":66,"tag":989,"props":3335,"children":3336},{"style":1292},[3337],{"type":72,"value":3338},"  overrides",{"type":66,"tag":989,"props":3340,"children":3341},{"style":1002},[3342],{"type":72,"value":1248},{"type":66,"tag":989,"props":3344,"children":3345},{"style":1002},[3346],{"type":72,"value":1005},{"type":66,"tag":989,"props":3348,"children":3349},{"style":1008},[3350],{"type":72,"value":1515},{"type":66,"tag":989,"props":3352,"children":3353},{"style":1002},[3354],{"type":72,"value":1510},{"type":66,"tag":989,"props":3356,"children":3357},{"style":1008},[3358],{"type":72,"value":3359}," runs ",{"type":66,"tag":989,"props":3361,"children":3362},{"style":1002},[3363],{"type":72,"value":3364},"},\n",{"type":66,"tag":989,"props":3366,"children":3367},{"class":991,"line":1126},[3368,3372],{"type":66,"tag":989,"props":3369,"children":3370},{"style":1002},[3371],{"type":72,"value":1145},{"type":66,"tag":989,"props":3373,"children":3374},{"style":1008},[3375],{"type":72,"value":2115},{"type":66,"tag":75,"props":3377,"children":3378},{},[3379,3381,3386,3388,3393,3395,3400],{"type":72,"value":3380},"Only listed keys move. There is ",{"type":66,"tag":81,"props":3382,"children":3383},{},[3384],{"type":72,"value":3385},"no cross-store transaction",{"type":72,"value":3387}," — if ",{"type":66,"tag":89,"props":3389,"children":3391},{"className":3390},[],[3392],{"type":72,"value":541},{"type":72,"value":3394},"\nlives in Postgres and ",{"type":66,"tag":89,"props":3396,"children":3398},{"className":3397},[],[3399],{"type":72,"value":2738},{"type":72,"value":3401}," in Redis, a write touching both is two\nwrites. The idempotency invariants are exactly what make those retries safe.",{"type":66,"tag":224,"props":3403,"children":3405},{"id":3404},"wire-it-into-the-chat-route",[3406],{"type":72,"value":3407},"Wire it into the chat route",{"type":66,"tag":977,"props":3409,"children":3411},{"className":979,"code":3410,"language":981,"meta":982,"style":983},"import {\n  chat,\n  chatParamsFromRequest,\n  toServerSentEventsResponse,\n} from '@tanstack\u002Fai'\nimport { openaiText } from '@tanstack\u002Fai-openai'\nimport { withPersistence } from '@tanstack\u002Fai-persistence'\nimport { chatPersistence } from '@\u002Flib\u002Fchat-persistence'\n\nexport async function POST(request: Request) {\n  const params = await chatParamsFromRequest(request)\n  const stream = chat({\n    adapter: openaiText('gpt-5.5'),\n    messages: params.messages,\n    threadId: params.threadId,\n    runId: params.runId,\n    ...(params.resume ? { resume: params.resume } : {}),\n    middleware: [withPersistence(chatPersistence)],\n  })\n  return toServerSentEventsResponse(stream)\n}\n",[3412],{"type":66,"tag":89,"props":3413,"children":3414},{"__ignoreMap":983},[3415,3426,3438,3450,3462,3486,3523,3559,3595,3602,3649,3687,3716,3757,3784,3812,3840,3918,3957,3969,3994],{"type":66,"tag":989,"props":3416,"children":3417},{"class":991,"line":992},[3418,3422],{"type":66,"tag":989,"props":3419,"children":3420},{"style":996},[3421],{"type":72,"value":999},{"type":66,"tag":989,"props":3423,"children":3424},{"style":1002},[3425],{"type":72,"value":1096},{"type":66,"tag":989,"props":3427,"children":3428},{"class":991,"line":1040},[3429,3434],{"type":66,"tag":989,"props":3430,"children":3431},{"style":1008},[3432],{"type":72,"value":3433},"  chat",{"type":66,"tag":989,"props":3435,"children":3436},{"style":1002},[3437],{"type":72,"value":1110},{"type":66,"tag":989,"props":3439,"children":3440},{"class":991,"line":1082},[3441,3446],{"type":66,"tag":989,"props":3442,"children":3443},{"style":1008},[3444],{"type":72,"value":3445},"  chatParamsFromRequest",{"type":66,"tag":989,"props":3447,"children":3448},{"style":1002},[3449],{"type":72,"value":1110},{"type":66,"tag":989,"props":3451,"children":3452},{"class":991,"line":1099},[3453,3458],{"type":66,"tag":989,"props":3454,"children":3455},{"style":1008},[3456],{"type":72,"value":3457},"  toServerSentEventsResponse",{"type":66,"tag":989,"props":3459,"children":3460},{"style":1002},[3461],{"type":72,"value":1110},{"type":66,"tag":989,"props":3463,"children":3464},{"class":991,"line":1113},[3465,3469,3473,3477,3482],{"type":66,"tag":989,"props":3466,"children":3467},{"style":1002},[3468],{"type":72,"value":1145},{"type":66,"tag":989,"props":3470,"children":3471},{"style":996},[3472],{"type":72,"value":1021},{"type":66,"tag":989,"props":3474,"children":3475},{"style":1002},[3476],{"type":72,"value":1026},{"type":66,"tag":989,"props":3478,"children":3479},{"style":1029},[3480],{"type":72,"value":3481},"@tanstack\u002Fai",{"type":66,"tag":989,"props":3483,"children":3484},{"style":1002},[3485],{"type":72,"value":1037},{"type":66,"tag":989,"props":3487,"children":3488},{"class":991,"line":1126},[3489,3493,3497,3502,3506,3510,3514,3519],{"type":66,"tag":989,"props":3490,"children":3491},{"style":996},[3492],{"type":72,"value":999},{"type":66,"tag":989,"props":3494,"children":3495},{"style":1002},[3496],{"type":72,"value":1005},{"type":66,"tag":989,"props":3498,"children":3499},{"style":1008},[3500],{"type":72,"value":3501}," openaiText",{"type":66,"tag":989,"props":3503,"children":3504},{"style":1002},[3505],{"type":72,"value":1016},{"type":66,"tag":989,"props":3507,"children":3508},{"style":996},[3509],{"type":72,"value":1021},{"type":66,"tag":989,"props":3511,"children":3512},{"style":1002},[3513],{"type":72,"value":1026},{"type":66,"tag":989,"props":3515,"children":3516},{"style":1029},[3517],{"type":72,"value":3518},"@tanstack\u002Fai-openai",{"type":66,"tag":989,"props":3520,"children":3521},{"style":1002},[3522],{"type":72,"value":1037},{"type":66,"tag":989,"props":3524,"children":3525},{"class":991,"line":1139},[3526,3530,3534,3539,3543,3547,3551,3555],{"type":66,"tag":989,"props":3527,"children":3528},{"style":996},[3529],{"type":72,"value":999},{"type":66,"tag":989,"props":3531,"children":3532},{"style":1002},[3533],{"type":72,"value":1005},{"type":66,"tag":989,"props":3535,"children":3536},{"style":1008},[3537],{"type":72,"value":3538}," withPersistence",{"type":66,"tag":989,"props":3540,"children":3541},{"style":1002},[3542],{"type":72,"value":1016},{"type":66,"tag":989,"props":3544,"children":3545},{"style":996},[3546],{"type":72,"value":1021},{"type":66,"tag":989,"props":3548,"children":3549},{"style":1002},[3550],{"type":72,"value":1026},{"type":66,"tag":989,"props":3552,"children":3553},{"style":1029},[3554],{"type":72,"value":1032},{"type":66,"tag":989,"props":3556,"children":3557},{"style":1002},[3558],{"type":72,"value":1037},{"type":66,"tag":989,"props":3560,"children":3561},{"class":991,"line":1164},[3562,3566,3570,3574,3578,3582,3586,3591],{"type":66,"tag":989,"props":3563,"children":3564},{"style":996},[3565],{"type":72,"value":999},{"type":66,"tag":989,"props":3567,"children":3568},{"style":1002},[3569],{"type":72,"value":1005},{"type":66,"tag":989,"props":3571,"children":3572},{"style":1008},[3573],{"type":72,"value":2508},{"type":66,"tag":989,"props":3575,"children":3576},{"style":1002},[3577],{"type":72,"value":1016},{"type":66,"tag":989,"props":3579,"children":3580},{"style":996},[3581],{"type":72,"value":1021},{"type":66,"tag":989,"props":3583,"children":3584},{"style":1002},[3585],{"type":72,"value":1026},{"type":66,"tag":989,"props":3587,"children":3588},{"style":1029},[3589],{"type":72,"value":3590},"@\u002Flib\u002Fchat-persistence",{"type":66,"tag":989,"props":3592,"children":3593},{"style":1002},[3594],{"type":72,"value":1037},{"type":66,"tag":989,"props":3596,"children":3597},{"class":991,"line":1174},[3598],{"type":66,"tag":989,"props":3599,"children":3600},{"emptyLinePlaceholder":1168},[3601],{"type":72,"value":1171},{"type":66,"tag":989,"props":3603,"children":3604},{"class":991,"line":1211},[3605,3609,3614,3618,3623,3627,3632,3636,3641,3645],{"type":66,"tag":989,"props":3606,"children":3607},{"style":996},[3608],{"type":72,"value":2498},{"type":66,"tag":989,"props":3610,"children":3611},{"style":1223},[3612],{"type":72,"value":3613}," async",{"type":66,"tag":989,"props":3615,"children":3616},{"style":1223},[3617],{"type":72,"value":1774},{"type":66,"tag":989,"props":3619,"children":3620},{"style":1229},[3621],{"type":72,"value":3622}," POST",{"type":66,"tag":989,"props":3624,"children":3625},{"style":1002},[3626],{"type":72,"value":1237},{"type":66,"tag":989,"props":3628,"children":3629},{"style":1240},[3630],{"type":72,"value":3631},"request",{"type":66,"tag":989,"props":3633,"children":3634},{"style":1002},[3635],{"type":72,"value":1248},{"type":66,"tag":989,"props":3637,"children":3638},{"style":1251},[3639],{"type":72,"value":3640}," Request",{"type":66,"tag":989,"props":3642,"children":3643},{"style":1002},[3644],{"type":72,"value":1308},{"type":66,"tag":989,"props":3646,"children":3647},{"style":1002},[3648],{"type":72,"value":1096},{"type":66,"tag":989,"props":3650,"children":3651},{"class":991,"line":1219},[3652,3657,3662,3666,3670,3675,3679,3683],{"type":66,"tag":989,"props":3653,"children":3654},{"style":1223},[3655],{"type":72,"value":3656},"  const",{"type":66,"tag":989,"props":3658,"children":3659},{"style":1008},[3660],{"type":72,"value":3661}," params",{"type":66,"tag":989,"props":3663,"children":3664},{"style":1002},[3665],{"type":72,"value":1339},{"type":66,"tag":989,"props":3667,"children":3668},{"style":996},[3669],{"type":72,"value":1344},{"type":66,"tag":989,"props":3671,"children":3672},{"style":1229},[3673],{"type":72,"value":3674}," chatParamsFromRequest",{"type":66,"tag":989,"props":3676,"children":3677},{"style":1292},[3678],{"type":72,"value":1237},{"type":66,"tag":989,"props":3680,"children":3681},{"style":1008},[3682],{"type":72,"value":3631},{"type":66,"tag":989,"props":3684,"children":3685},{"style":1292},[3686],{"type":72,"value":2115},{"type":66,"tag":989,"props":3688,"children":3689},{"class":991,"line":1270},[3690,3694,3699,3703,3708,3712],{"type":66,"tag":989,"props":3691,"children":3692},{"style":1223},[3693],{"type":72,"value":3656},{"type":66,"tag":989,"props":3695,"children":3696},{"style":1008},[3697],{"type":72,"value":3698}," stream",{"type":66,"tag":989,"props":3700,"children":3701},{"style":1002},[3702],{"type":72,"value":1339},{"type":66,"tag":989,"props":3704,"children":3705},{"style":1229},[3706],{"type":72,"value":3707}," chat",{"type":66,"tag":989,"props":3709,"children":3710},{"style":1292},[3711],{"type":72,"value":1237},{"type":66,"tag":989,"props":3713,"children":3714},{"style":1002},[3715],{"type":72,"value":2534},{"type":66,"tag":989,"props":3717,"children":3718},{"class":991,"line":1283},[3719,3724,3728,3732,3736,3740,3745,3749,3753],{"type":66,"tag":989,"props":3720,"children":3721},{"style":1292},[3722],{"type":72,"value":3723},"    adapter",{"type":66,"tag":989,"props":3725,"children":3726},{"style":1002},[3727],{"type":72,"value":1248},{"type":66,"tag":989,"props":3729,"children":3730},{"style":1229},[3731],{"type":72,"value":3501},{"type":66,"tag":989,"props":3733,"children":3734},{"style":1292},[3735],{"type":72,"value":1237},{"type":66,"tag":989,"props":3737,"children":3738},{"style":1002},[3739],{"type":72,"value":1382},{"type":66,"tag":989,"props":3741,"children":3742},{"style":1029},[3743],{"type":72,"value":3744},"gpt-5.5",{"type":66,"tag":989,"props":3746,"children":3747},{"style":1002},[3748],{"type":72,"value":1382},{"type":66,"tag":989,"props":3750,"children":3751},{"style":1292},[3752],{"type":72,"value":1308},{"type":66,"tag":989,"props":3754,"children":3755},{"style":1002},[3756],{"type":72,"value":1110},{"type":66,"tag":989,"props":3758,"children":3759},{"class":991,"line":1315},[3760,3764,3768,3772,3776,3780],{"type":66,"tag":989,"props":3761,"children":3762},{"style":1292},[3763],{"type":72,"value":2560},{"type":66,"tag":989,"props":3765,"children":3766},{"style":1002},[3767],{"type":72,"value":1248},{"type":66,"tag":989,"props":3769,"children":3770},{"style":1008},[3771],{"type":72,"value":3661},{"type":66,"tag":989,"props":3773,"children":3774},{"style":1002},[3775],{"type":72,"value":222},{"type":66,"tag":989,"props":3777,"children":3778},{"style":1008},[3779],{"type":72,"value":541},{"type":66,"tag":989,"props":3781,"children":3782},{"style":1002},[3783],{"type":72,"value":1110},{"type":66,"tag":989,"props":3785,"children":3786},{"class":991,"line":1366},[3787,3792,3796,3800,3804,3808],{"type":66,"tag":989,"props":3788,"children":3789},{"style":1292},[3790],{"type":72,"value":3791},"    threadId",{"type":66,"tag":989,"props":3793,"children":3794},{"style":1002},[3795],{"type":72,"value":1248},{"type":66,"tag":989,"props":3797,"children":3798},{"style":1008},[3799],{"type":72,"value":3661},{"type":66,"tag":989,"props":3801,"children":3802},{"style":1002},[3803],{"type":72,"value":222},{"type":66,"tag":989,"props":3805,"children":3806},{"style":1008},[3807],{"type":72,"value":532},{"type":66,"tag":989,"props":3809,"children":3810},{"style":1002},[3811],{"type":72,"value":1110},{"type":66,"tag":989,"props":3813,"children":3814},{"class":991,"line":1389},[3815,3820,3824,3828,3832,3836],{"type":66,"tag":989,"props":3816,"children":3817},{"style":1292},[3818],{"type":72,"value":3819},"    runId",{"type":66,"tag":989,"props":3821,"children":3822},{"style":1002},[3823],{"type":72,"value":1248},{"type":66,"tag":989,"props":3825,"children":3826},{"style":1008},[3827],{"type":72,"value":3661},{"type":66,"tag":989,"props":3829,"children":3830},{"style":1002},[3831],{"type":72,"value":222},{"type":66,"tag":989,"props":3833,"children":3834},{"style":1008},[3835],{"type":72,"value":560},{"type":66,"tag":989,"props":3837,"children":3838},{"style":1002},[3839],{"type":72,"value":1110},{"type":66,"tag":989,"props":3841,"children":3842},{"class":991,"line":1411},[3843,3848,3852,3857,3861,3866,3871,3875,3880,3884,3888,3892,3896,3900,3905,3910,3914],{"type":66,"tag":989,"props":3844,"children":3845},{"style":1002},[3846],{"type":72,"value":3847},"    ...",{"type":66,"tag":989,"props":3849,"children":3850},{"style":1292},[3851],{"type":72,"value":1237},{"type":66,"tag":989,"props":3853,"children":3854},{"style":1008},[3855],{"type":72,"value":3856},"params",{"type":66,"tag":989,"props":3858,"children":3859},{"style":1002},[3860],{"type":72,"value":222},{"type":66,"tag":989,"props":3862,"children":3863},{"style":1008},[3864],{"type":72,"value":3865},"resume",{"type":66,"tag":989,"props":3867,"children":3868},{"style":1002},[3869],{"type":72,"value":3870}," ?",{"type":66,"tag":989,"props":3872,"children":3873},{"style":1002},[3874],{"type":72,"value":1005},{"type":66,"tag":989,"props":3876,"children":3877},{"style":1292},[3878],{"type":72,"value":3879}," resume",{"type":66,"tag":989,"props":3881,"children":3882},{"style":1002},[3883],{"type":72,"value":1248},{"type":66,"tag":989,"props":3885,"children":3886},{"style":1008},[3887],{"type":72,"value":3661},{"type":66,"tag":989,"props":3889,"children":3890},{"style":1002},[3891],{"type":72,"value":222},{"type":66,"tag":989,"props":3893,"children":3894},{"style":1008},[3895],{"type":72,"value":3865},{"type":66,"tag":989,"props":3897,"children":3898},{"style":1002},[3899],{"type":72,"value":1016},{"type":66,"tag":989,"props":3901,"children":3902},{"style":1002},[3903],{"type":72,"value":3904}," :",{"type":66,"tag":989,"props":3906,"children":3907},{"style":1002},[3908],{"type":72,"value":3909}," {}",{"type":66,"tag":989,"props":3911,"children":3912},{"style":1292},[3913],{"type":72,"value":1308},{"type":66,"tag":989,"props":3915,"children":3916},{"style":1002},[3917],{"type":72,"value":1110},{"type":66,"tag":989,"props":3919,"children":3920},{"class":991,"line":1420},[3921,3926,3930,3935,3939,3943,3948,3953],{"type":66,"tag":989,"props":3922,"children":3923},{"style":1292},[3924],{"type":72,"value":3925},"    middleware",{"type":66,"tag":989,"props":3927,"children":3928},{"style":1002},[3929],{"type":72,"value":1248},{"type":66,"tag":989,"props":3931,"children":3932},{"style":1292},[3933],{"type":72,"value":3934}," [",{"type":66,"tag":989,"props":3936,"children":3937},{"style":1229},[3938],{"type":72,"value":2701},{"type":66,"tag":989,"props":3940,"children":3941},{"style":1292},[3942],{"type":72,"value":1237},{"type":66,"tag":989,"props":3944,"children":3945},{"style":1008},[3946],{"type":72,"value":3947},"chatPersistence",{"type":66,"tag":989,"props":3949,"children":3950},{"style":1292},[3951],{"type":72,"value":3952},")]",{"type":66,"tag":989,"props":3954,"children":3955},{"style":1002},[3956],{"type":72,"value":1110},{"type":66,"tag":989,"props":3958,"children":3959},{"class":991,"line":1468},[3960,3965],{"type":66,"tag":989,"props":3961,"children":3962},{"style":1002},[3963],{"type":72,"value":3964},"  }",{"type":66,"tag":989,"props":3966,"children":3967},{"style":1292},[3968],{"type":72,"value":2115},{"type":66,"tag":989,"props":3970,"children":3971},{"class":991,"line":1477},[3972,3976,3981,3985,3990],{"type":66,"tag":989,"props":3973,"children":3974},{"style":996},[3975],{"type":72,"value":1276},{"type":66,"tag":989,"props":3977,"children":3978},{"style":1229},[3979],{"type":72,"value":3980}," toServerSentEventsResponse",{"type":66,"tag":989,"props":3982,"children":3983},{"style":1292},[3984],{"type":72,"value":1237},{"type":66,"tag":989,"props":3986,"children":3987},{"style":1008},[3988],{"type":72,"value":3989},"stream",{"type":66,"tag":989,"props":3991,"children":3992},{"style":1292},[3993],{"type":72,"value":2115},{"type":66,"tag":989,"props":3995,"children":3996},{"class":991,"line":1487},[3997],{"type":66,"tag":989,"props":3998,"children":3999},{"style":1002},[4000],{"type":72,"value":1710},{"type":66,"tag":75,"props":4002,"children":4003},{},[4004,4009,4011,4016],{"type":66,"tag":89,"props":4005,"children":4007},{"className":4006},[],[4008],{"type":72,"value":532},{"type":72,"value":4010}," is a bare string to the stores. ",{"type":66,"tag":81,"props":4012,"children":4013},{},[4014],{"type":72,"value":4015},"Authorize thread access at the\nroute",{"type":72,"value":4017}," — derive the user from the session, never trust a client-supplied id.",{"type":66,"tag":224,"props":4019,"children":4021},{"id":4020},"verify-required",[4022],{"type":72,"value":4023},"Verify (required)",{"type":66,"tag":75,"props":4025,"children":4026},{},[4027],{"type":72,"value":4028},"This matters more here than anywhere else: there is no reference driver to\ncompare against, so the testkit is the only thing standing between a subtle\nidempotency bug and stuck approvals in production.",{"type":66,"tag":977,"props":4030,"children":4032},{"className":979,"code":4031,"language":981,"meta":982,"style":983},"import { runPersistenceConformance } from '@tanstack\u002Fai-persistence\u002Ftestkit'\nimport { chatPersistence } from '..\u002Fsrc\u002Flib\u002Fchat-persistence'\n\nrunPersistenceConformance('app-custom', () => chatPersistence)\n",[4033],{"type":66,"tag":89,"props":4034,"children":4035},{"__ignoreMap":983},[4036,4073,4109,4116],{"type":66,"tag":989,"props":4037,"children":4038},{"class":991,"line":992},[4039,4043,4047,4052,4056,4060,4064,4069],{"type":66,"tag":989,"props":4040,"children":4041},{"style":996},[4042],{"type":72,"value":999},{"type":66,"tag":989,"props":4044,"children":4045},{"style":1002},[4046],{"type":72,"value":1005},{"type":66,"tag":989,"props":4048,"children":4049},{"style":1008},[4050],{"type":72,"value":4051}," runPersistenceConformance",{"type":66,"tag":989,"props":4053,"children":4054},{"style":1002},[4055],{"type":72,"value":1016},{"type":66,"tag":989,"props":4057,"children":4058},{"style":996},[4059],{"type":72,"value":1021},{"type":66,"tag":989,"props":4061,"children":4062},{"style":1002},[4063],{"type":72,"value":1026},{"type":66,"tag":989,"props":4065,"children":4066},{"style":1029},[4067],{"type":72,"value":4068},"@tanstack\u002Fai-persistence\u002Ftestkit",{"type":66,"tag":989,"props":4070,"children":4071},{"style":1002},[4072],{"type":72,"value":1037},{"type":66,"tag":989,"props":4074,"children":4075},{"class":991,"line":1040},[4076,4080,4084,4088,4092,4096,4100,4105],{"type":66,"tag":989,"props":4077,"children":4078},{"style":996},[4079],{"type":72,"value":999},{"type":66,"tag":989,"props":4081,"children":4082},{"style":1002},[4083],{"type":72,"value":1005},{"type":66,"tag":989,"props":4085,"children":4086},{"style":1008},[4087],{"type":72,"value":2508},{"type":66,"tag":989,"props":4089,"children":4090},{"style":1002},[4091],{"type":72,"value":1016},{"type":66,"tag":989,"props":4093,"children":4094},{"style":996},[4095],{"type":72,"value":1021},{"type":66,"tag":989,"props":4097,"children":4098},{"style":1002},[4099],{"type":72,"value":1026},{"type":66,"tag":989,"props":4101,"children":4102},{"style":1029},[4103],{"type":72,"value":4104},"..\u002Fsrc\u002Flib\u002Fchat-persistence",{"type":66,"tag":989,"props":4106,"children":4107},{"style":1002},[4108],{"type":72,"value":1037},{"type":66,"tag":989,"props":4110,"children":4111},{"class":991,"line":1082},[4112],{"type":66,"tag":989,"props":4113,"children":4114},{"emptyLinePlaceholder":1168},[4115],{"type":72,"value":1171},{"type":66,"tag":989,"props":4117,"children":4118},{"class":991,"line":1099},[4119,4124,4128,4132,4137,4141,4145,4150,4155],{"type":66,"tag":989,"props":4120,"children":4121},{"style":1229},[4122],{"type":72,"value":4123},"runPersistenceConformance",{"type":66,"tag":989,"props":4125,"children":4126},{"style":1008},[4127],{"type":72,"value":1237},{"type":66,"tag":989,"props":4129,"children":4130},{"style":1002},[4131],{"type":72,"value":1382},{"type":66,"tag":989,"props":4133,"children":4134},{"style":1029},[4135],{"type":72,"value":4136},"app-custom",{"type":66,"tag":989,"props":4138,"children":4139},{"style":1002},[4140],{"type":72,"value":1382},{"type":66,"tag":989,"props":4142,"children":4143},{"style":1002},[4144],{"type":72,"value":1510},{"type":66,"tag":989,"props":4146,"children":4147},{"style":1002},[4148],{"type":72,"value":4149}," ()",{"type":66,"tag":989,"props":4151,"children":4152},{"style":1223},[4153],{"type":72,"value":4154}," =>",{"type":66,"tag":989,"props":4156,"children":4157},{"style":1008},[4158],{"type":72,"value":4159}," chatPersistence)\n",{"type":66,"tag":75,"props":4161,"children":4162},{},[4163,4165,4171,4173,4179,4181,4187],{"type":72,"value":4164},"Point it at a throwaway database and reset between runs. Declare intentional\nomissions with ",{"type":66,"tag":89,"props":4166,"children":4168},{"className":4167},[],[4169],{"type":72,"value":4170},"skip: ['metadata']",{"type":72,"value":4172}," — it accepts only\n",{"type":66,"tag":89,"props":4174,"children":4176},{"className":4175},[],[4177],{"type":72,"value":4178},"'messages' | 'runs' | 'interrupts' | 'metadata'",{"type":72,"value":4180},", never ",{"type":66,"tag":89,"props":4182,"children":4184},{"className":4183},[],[4185],{"type":72,"value":4186},"'locks'",{"type":72,"value":4188},", which is not\na state store.",{"type":66,"tag":4190,"props":4191,"children":4192},"style",{},[4193],{"type":72,"value":4194},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":4196,"total":4338},[4197,4213,4225,4237,4252,4264,4274,4284,4297,4307,4318,4328],{"slug":4198,"name":4198,"fn":4199,"description":4200,"org":4201,"tags":4202,"stars":4210,"repoUrl":4211,"updatedAt":4212},"aggregation","perform data aggregation in TanStack Table","Aggregate TanStack Table columns independently of grouping, including grand totals, caller-selected row totals, multiple keyed aggregations, custom context-based definitions, grouped merges, manual values, and worker constraints.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4203,4206,4209],{"name":4204,"slug":4205,"type":16},"Data Analysis","data-analysis",{"name":4207,"slug":4208,"type":16},"Frontend","frontend",{"name":10,"slug":9,"type":16},28175,"https:\u002F\u002Fgithub.com\u002FTanStack\u002Ftable","2026-07-30T05:25:59.429787",{"slug":4214,"name":4214,"fn":4215,"description":4216,"org":4217,"tags":4218,"stars":4210,"repoUrl":4211,"updatedAt":4224},"api-not-found","diagnose TanStack Table API errors","Diagnose missing TanStack Table v9 exports, options, state slices, and instance methods. Load before inventing an API when code sees a type error, undefined feature method, absent object key, adapter mismatch, or v8-shaped example.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4219,4222,4223],{"name":4220,"slug":4221,"type":16},"Debugging","debugging",{"name":4207,"slug":4208,"type":16},{"name":10,"slug":9,"type":16},"2026-07-30T05:26:05.418735",{"slug":4226,"name":4226,"fn":4227,"description":4228,"org":4229,"tags":4230,"stars":4210,"repoUrl":4211,"updatedAt":4236},"cell-selection","select rectangular cell ranges in tables","Select rectangular cell ranges with cellSelectionFeature: two-corner range state keyed by row and column id, mousedown\u002Fmouseenter handlers, selection edges, render-order resolution under pinning, and autoResetCellSelection. Load when ranges widen unexpectedly after sorting or column reordering, when a drag re-renders the whole table, or when building copy-to-clipboard from a selection.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4231,4232,4233],{"name":4204,"slug":4205,"type":16},{"name":10,"slug":9,"type":16},{"name":4234,"slug":4235,"type":16},"UI Components","ui-components","2026-07-30T05:25:38.403427",{"slug":4238,"name":4238,"fn":4239,"description":4240,"org":4241,"tags":4242,"stars":4210,"repoUrl":4211,"updatedAt":4251},"client-vs-server","manage TanStack Table data pipelines","Choose client or server ownership for filtering, grouping, sorting, expanding, and pagination in TanStack Table v9. Load for manual* flags, mixed pipelines, server counts, or deciding which dataset each row-model stage receives.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4243,4246,4247,4250],{"name":4244,"slug":4245,"type":16},"Data Pipeline","data-pipeline",{"name":4207,"slug":4208,"type":16},{"name":4248,"slug":4249,"type":16},"Performance","performance",{"name":10,"slug":9,"type":16},"2026-07-30T05:25:45.400104",{"slug":4253,"name":4253,"fn":4254,"description":4255,"org":4256,"tags":4257,"stars":4210,"repoUrl":4211,"updatedAt":4263},"column-faceting","build faceted filter UIs","Build faceted filter UIs with columnFacetingFeature, facetedRowModel, facetedUniqueValues, and facetedMinMaxValues. Load for facet counts, numeric ranges, own-filter exclusion, or server-page facet completeness.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4258,4261,4262],{"name":4259,"slug":4260,"type":16},"Data Visualization","data-visualization",{"name":4207,"slug":4208,"type":16},{"name":10,"slug":9,"type":16},"2026-07-30T05:25:41.397257",{"slug":4265,"name":4265,"fn":4266,"description":4267,"org":4268,"tags":4269,"stars":4210,"repoUrl":4211,"updatedAt":4273},"column-filtering","implement column filtering in TanStack Table","Filter columns with columnFilteringFeature, filteredRowModel, filterFns, filterMeta, nested-row direction, and manualFiltering. Load for accessor compatibility, controlled filter updaters, fuzzy metadata, or client\u002Fserver ownership.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4270,4271,4272],{"name":4204,"slug":4205,"type":16},{"name":4207,"slug":4208,"type":16},{"name":10,"slug":9,"type":16},"2026-07-30T05:25:53.391632",{"slug":4275,"name":4275,"fn":4276,"description":4277,"org":4278,"tags":4279,"stars":4210,"repoUrl":4211,"updatedAt":4283},"column-ordering","manage TanStack Table column ordering","Control TanStack Table v9 leaf columnOrder with stable IDs while accounting for pinning regions, visibility, and groupedColumnMode precedence. Load for drag-and-drop columns or rendered order that differs from state.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4280,4281,4282],{"name":4207,"slug":4208,"type":16},{"name":10,"slug":9,"type":16},{"name":4234,"slug":4235,"type":16},"2026-07-30T05:26:03.37801",{"slug":4285,"name":4285,"fn":4286,"description":4287,"org":4288,"tags":4289,"stars":4210,"repoUrl":4211,"updatedAt":4296},"column-pinning","configure column pinning in TanStack Table","Pin columns into logical start, center, and end regions with columnPinningFeature and renderer-owned sticky CSS. Load for RTL offsets, z-index, backgrounds, overflow, widths, gaps, or overlaps.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4290,4293,4294,4295],{"name":4291,"slug":4292,"type":16},"CSS","css",{"name":4207,"slug":4208,"type":16},{"name":10,"slug":9,"type":16},{"name":4234,"slug":4235,"type":16},"2026-07-30T05:25:55.377366",{"slug":4298,"name":4298,"fn":4299,"description":4300,"org":4301,"tags":4302,"stars":4210,"repoUrl":4211,"updatedAt":4306},"column-resizing","implement column resizing in TanStack Table","Wire columnResizingFeature, header.getResizeHandler, resize mode and direction, pointer or touch events, and performant CSS-variable updates. Load when resize state changes but widths do not, or large tables resize slowly.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4303,4304,4305],{"name":4207,"slug":4208,"type":16},{"name":10,"slug":9,"type":16},{"name":4234,"slug":4235,"type":16},"2026-07-30T05:25:51.400011",{"slug":4308,"name":4308,"fn":4309,"description":4310,"org":4311,"tags":4312,"stars":4210,"repoUrl":4211,"updatedAt":4317},"column-sizing","configure column sizing in TanStack Table","Use columnSizingFeature numeric size, minSize, maxSize, getSize, getStart, getAfter, and total-size APIs in table, grid, or flex CSS. Load for auto or percentage misconceptions and sizing\u002Fpinning layout mismatch.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4313,4314,4315,4316],{"name":4291,"slug":4292,"type":16},{"name":4207,"slug":4208,"type":16},{"name":10,"slug":9,"type":16},{"name":4234,"slug":4235,"type":16},"2026-07-30T05:25:48.703799",{"slug":4319,"name":4319,"fn":4320,"description":4321,"org":4322,"tags":4323,"stars":4210,"repoUrl":4211,"updatedAt":4327},"column-visibility","manage column visibility in TanStack Table","Hide columns with columnVisibilityFeature while rendering visibility-aware header, column, and cell collections. Load when hidden columns remain in the DOM, false-versus-absent state is confused, or enableHiding is misunderstood.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4324,4325,4326],{"name":4207,"slug":4208,"type":16},{"name":10,"slug":9,"type":16},{"name":4234,"slug":4235,"type":16},"2026-07-30T05:25:47.367943",{"slug":4329,"name":4329,"fn":4330,"description":4331,"org":4332,"tags":4333,"stars":4210,"repoUrl":4211,"updatedAt":4337},"core","build data grids with TanStack Table","Use TanStack Table v9 as a headless data-grid state and row-processing engine. Load for first-table architecture, stable data and columns, row numbering with getDisplayIndex, semantic rendering, framework adapter choice, or deciding what Table owns versus the renderer.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4334,4335,4336],{"name":4204,"slug":4205,"type":16},{"name":4207,"slug":4208,"type":16},{"name":4234,"slug":4235,"type":16},"2026-07-30T05:25:52.366295",125,{"items":4340,"total":1592},[4341,4359,4374,4389,4402,4414,4426],{"slug":4342,"name":4342,"fn":4343,"description":4344,"org":4345,"tags":4346,"stars":30,"repoUrl":31,"updatedAt":4358},"ai-code-mode","execute sandboxed TypeScript code with LLMs","LLM-generated TypeScript execution in sandboxed environments: createCodeModeTool() with isolate drivers (createNodeIsolateDriver, createQuickJSIsolateDriver, createCloudflareIsolateDriver), codeModeWithSkills() for persistent skill libraries, trust strategies, skill storage (FileSystem, LocalStorage, InMemory, Mongo), client-side execution progress via code_mode:* custom events in useChat.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4347,4349,4352,4355,4356],{"name":4348,"slug":38,"type":16},"AI SDK",{"name":4350,"slug":4351,"type":16},"Code Execution","code-execution",{"name":4353,"slug":4354,"type":16},"Sandboxing","sandboxing",{"name":10,"slug":9,"type":16},{"name":4357,"slug":52,"type":16},"TypeScript","2026-07-16T06:04:13.597905",{"slug":4360,"name":4360,"fn":4361,"description":4362,"org":4363,"tags":4364,"stars":30,"repoUrl":31,"updatedAt":4373},"ai-core","configure TanStack AI agent features","Entry point for TanStack AI skills. Routes to chat-experience, tool-calling, media-generation, structured-outputs, adapter-configuration, ag-ui-protocol, middleware, locks, custom-backend-integration, and debug-logging, plus the skills shipped by companion packages (@tanstack\u002Fai-persistence, @tanstack\u002Fai-code-mode). Use chat() not streamText(), openaiText() not createOpenAI(), toServerSentEventsResponse() not manual SSE, middleware hooks not onEnd callbacks.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4365,4368,4370],{"name":4366,"slug":4367,"type":16},"Agents","agents",{"name":4369,"slug":36,"type":16},"AI",{"name":4371,"slug":4372,"type":16},"Middleware","middleware","2026-07-30T05:26:10.404565",{"slug":4375,"name":4376,"fn":4377,"description":4378,"org":4379,"tags":4380,"stars":30,"repoUrl":31,"updatedAt":4388},"ai-coreadapter-configuration","ai-core\u002Fadapter-configuration","configure AI provider adapters","Provider adapter selection and configuration: openaiText, anthropicText, geminiText, ollamaText, grokText, groqText, openRouterText, bedrockText, openaiCompatible. Per-model type safety with modelOptions, reasoning\u002Fthinking configuration, runtime adapter switching, extendAdapter() for custom models, createModel(). Generic OpenAI-compatible providers (DeepSeek, Together, Fireworks, etc.) via openaiCompatible({ baseURL, apiKey, models }) from @tanstack\u002Fai-openai\u002Fcompatible. API key env vars: OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY\u002FGEMINI_API_KEY, XAI_API_KEY, GROQ_API_KEY, OPENROUTER_API_KEY, OLLAMA_HOST, BEDROCK_API_KEY (or AWS_BEARER_TOKEN_BEDROCK).\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4381,4382,4385,4387],{"name":4348,"slug":38,"type":16},{"name":4383,"slug":4384,"type":16},"Configuration","configuration",{"name":4386,"slug":44,"type":16},"LLM",{"name":10,"slug":9,"type":16},"2026-07-16T06:04:17.82075",{"slug":4390,"name":4391,"fn":4392,"description":4393,"org":4394,"tags":4395,"stars":30,"repoUrl":31,"updatedAt":4401},"ai-coreag-ui-protocol","ai-core\u002Fag-ui-protocol","implement TanStack AI streaming protocol","Server-side AG-UI streaming protocol implementation: StreamChunk event types (RUN_STARTED, TEXT_MESSAGE_START\u002FCONTENT\u002FEND, TOOL_CALL_START\u002FARGS\u002FEND, RUN_FINISHED, RUN_ERROR, STEP_STARTED\u002FSTEP_FINISHED, STATE_SNAPSHOT\u002FDELTA, CUSTOM), toServerSentEventsStream() for SSE format, toHttpStream() for NDJSON format. For backends serving AG-UI events without client packages.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4396,4399,4400],{"name":4397,"slug":4398,"type":16},"API Development","api-development",{"name":4386,"slug":44,"type":16},{"name":10,"slug":9,"type":16},"2026-07-16T06:04:10.093367",{"slug":4403,"name":4404,"fn":4405,"description":4406,"org":4407,"tags":4408,"stars":30,"repoUrl":31,"updatedAt":4413},"ai-corechat-experience","ai-core\u002Fchat-experience","implement chat experiences with TanStack AI","End-to-end chat implementation: server endpoint with chat() and toServerSentEventsResponse(), client-side useChat hook with fetchServerSentEvents(), message rendering with UIMessage parts, multimodal content, thinking\u002Freasoning display. Covers streaming states, connection adapters, and message format conversions. NOT Vercel AI SDK — uses chat() not streamText().\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4409,4410,4411,4412],{"name":4397,"slug":4398,"type":16},{"name":4207,"slug":4208,"type":16},{"name":4386,"slug":44,"type":16},{"name":10,"slug":9,"type":16},"2026-07-30T05:26:11.505241",{"slug":4415,"name":4416,"fn":4417,"description":4418,"org":4419,"tags":4420,"stars":30,"repoUrl":31,"updatedAt":4425},"ai-coreclient-persistence","ai-core\u002Fclient-persistence","implement browser chat persistence for TanStack AI","Browser chat persistence on useChat \u002F ChatClient: localStoragePersistence, sessionStoragePersistence, indexedDBPersistence. Client-authoritative (adapter, full transcript) vs server-authoritative (persistence: true, no client cache). Reload restore, pending interrupts, mid-stream rejoin with delivery durability. Use for SPA reload durability — NOT server history alone. No extra package: the adapters ship in the framework packages.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4421,4422,4423,4424],{"name":4369,"slug":36,"type":16},{"name":4207,"slug":4208,"type":16},{"name":27,"slug":28,"type":16},{"name":10,"slug":9,"type":16},"2026-07-30T05:53:35.503176",{"slug":4427,"name":4428,"fn":4429,"description":4430,"org":4431,"tags":4432,"stars":30,"repoUrl":31,"updatedAt":4439},"ai-corecustom-backend-integration","ai-core\u002Fcustom-backend-integration","integrate custom backends with TanStack AI","Connect useChat to a non-TanStack-AI backend through custom connection adapters. ConnectConnectionAdapter (single async iterable) vs SubscribeConnectionAdapter (separate subscribe\u002Fsend). Customize fetchServerSentEvents() and fetchHttpStream() with auth headers, custom URLs, and request options. Import from framework package, not @tanstack\u002Fai-client.\n",{"slug":9,"name":10,"logoUrl":11,"githubOrg":10},[4433,4434,4435,4438],{"name":4369,"slug":36,"type":16},{"name":4397,"slug":4398,"type":16},{"name":4436,"slug":4437,"type":16},"Backend","backend",{"name":10,"slug":9,"type":16},"2026-07-17T06:06:39.855351"]