Agent SkillsTanStack/ai › ai-persistence/server

ai-persistence/server

GitHub

提供服务端聊天状态持久化中间件,支持多设备同步、历史重建及中断恢复。用于服务器端管理对话历史与运行生命周期,确保数据持久性。

packages/ai-persistence/skills/ai-persistence/server/SKILL.md TanStack/ai

触发场景

需要服务端持久化聊天记录 实现多设备会话同步 处理AI工具调用中断与恢复

安装

npx skills add TanStack/ai --skill ai-persistence/server -g -y
更多选项

非标准路径

npx skills add https://github.com/TanStack/ai/tree/main/packages/ai-persistence/skills/ai-persistence/server -g -y

不安装直接使用

npx skills use TanStack/ai@ai-persistence/server

指定 Agent (Claude Code)

npx skills add TanStack/ai --skill ai-persistence/server -a claude-code -g -y

安装 repo 全部 skill

npx skills add TanStack/ai --all -g -y

预览 repo 内 skill

npx skills add TanStack/ai --list

SKILL.md

Frontmatter
{
    "name": "ai-persistence\/server",
    "type": "sub-skill",
    "library": "tanstack-ai",
    "sources": [
        "TanStack\/ai:docs\/persistence\/chat-persistence.md",
        "TanStack\/ai:docs\/persistence\/overview.md",
        "TanStack\/ai:docs\/persistence\/controls.md"
    ],
    "description": "Server chat state with withPersistence from @tanstack\/ai-persistence. Authoritative transcript, run lifecycle, durable interrupts\/approvals, chatParamsFromRequest, reconstructChat, snapshotStreaming. Use when the server owns history, multi-device, or durable tool approvals. NOT client localStorage (see ai-core\/client-persistence in @tanstack\/ai) and NOT stream reconnect alone.\n",
    "library_version": "0.0.0"
}

Server Chat Persistence

Builds on ai-persistence. Package: @tanstack/ai-persistence.

withPersistence(persistence) is a ChatMiddleware that writes chat state to a backend: messages, runs, interrupts (optional metadata). It does not mutate the chunk stream and does not replace delivery durability.

Setup

import {
  chat,
  chatParamsFromRequest,
  toServerSentEventsResponse,
} from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
import { withPersistence } from '@tanstack/ai-persistence'
// Your adapter — see ai-persistence/stores.
import { persistence } from './persistence'

export async function POST(request: Request) {
  const params = await chatParamsFromRequest(request)
  const stream = chat({
    adapter: openaiText('gpt-5.5'),
    messages: params.messages,
    threadId: params.threadId,
    runId: params.runId,
    ...(params.resume ? { resume: params.resume } : {}),
    middleware: [withPersistence(persistence)],
  })
  return toServerSentEventsResponse(stream)
}

Always pass threadId and runId from the client (via chatParamsFromRequest / body helpers). Forward resume when the client resolves pending interrupts.

For dev and tests, memoryPersistence() from @tanstack/ai-persistence is a drop-in backend that implements all four stores in process.

What each store does

Store Role Required?
messages Full model-message transcript load/save Yes for withPersistence
runs Run status, timing, usage, errors Optional; needed for interrupt durability
interrupts Pending/resolved tool approvals & waits Optional; requires runs
metadata App-owned namespaced key/value Optional

Named shapes: ChatTranscriptPersistence (floor), ChatPersistence (all four). Annotate your factory with one of these, not with bare AIPersistence — the unparameterized type is the all-optional bag, and withPersistence rejects it because stores.messages is possibly undefined.

Authoritative-history contract

  • Non-empty messages → finish overwrites the stored thread with that array. Post the complete transcript, never a delta.
  • Empty messages → middleware loads the stored thread and continues.

When state is written

Moment Writes Best-effort?
onStart Pending turn snapshot (user + history) Yes — failure does not abort
Interrupt boundary New interrupts, run → interrupted, message snapshot No
onFinish Full transcript first, then run → completed, commit resumes No
Stream (optional) Throttled partial assistant text Yes if snapshotStreaming: true
onError Run → failed Resumes stay pending
onAbort Run → abortedbut only sometimes (see below) Resumes stay pending
withPersistence(persistence, {
  snapshotStreaming: true,
  snapshotIntervalMs: 1000, // default
})

onAbort writes conditionally, not always

A user pressing Stop and a user closing the tab produce the identical connection close, so onAbort can never infer intent from the abort alone. It writes:

  • 'aborted' (terminal, with finishedAt) when the abort is an explicit cancel — info.cancelRequested === true, or a durable cancel request found via wasCancelRequested(runs, runId) (both from @tanstack/ai; paired with requestRunCancel/RUN_CANCEL_REASON) — or when the run is not detachable at all (no sandbox/journal behind it, so there is nothing to reattach to).
  • Nothing when it is a plain disconnect on a detachable run (some other middleware, e.g. @tanstack/ai-sandbox, has provided DetachableRunCapability from @tanstack/ai). The record deliberately stays 'running' — the agent keeps running and a later attach can take it over. (The detaching middleware, not withPersistence, is what stamps detachedSince.)

Chat's onAbort and generation's onAbort (withGenerationPersistence) are asymmetric on purpose: a generation job has no journal and no agent loop to reattach to, so its onAbort always writes 'aborted' unconditionally. Do not "fix" that asymmetry by making generation conditional, or chat unconditional — both are correct for what they wrap.

Never build a client, or a persistence backend, that assumes a disconnect always finalizes the run — for a detachable run it usually does not, and inventing a finishedAt for a still-'running' record breaks takeover. Use isTerminalRunStatus(status) (from @tanstack/ai-persistence) to test whether a status is finished, rather than re-listing 'completed' | 'failed' | 'aborted' by hand.

Streaming snapshots default off (finish is authoritative). Enable only when partial-output durability is worth extra writes.

Resumes accepted in onConfig commit only at a success boundary (interrupt or finish). A failed run leaves interrupts pending so the same resume batch can retry.

Interrupt / resume flow

  1. Middleware records pending interrupts and gates new input: if pending exist, the request must include a matching resume batch or onConfig throws.
  2. On valid resume, middleware builds resumeToolState and clears config.resume so the engine does not double-reconstruct from client history (server owns transcript).
  3. On success boundary, interrupts are marked resolved/cancelled.

Hydrate a thread for the client (reconstructChat)

Server-authoritative clients load history by threadId (often GET):

import { reconstructChat } from '@tanstack/ai-persistence'

export async function GET(request: Request) {
  return reconstructChat(persistence, request, {
    // Multi-user: required in production
    authorize: async (threadId, req) => {
      const userId = await sessionUserId(req)
      return userOwnsThread(userId, threadId)
    },
  })
}

Returns { messages, activeRun, interrupts }:

  • messages — UI messages for paint
  • activeRun{ runId } if a run is still generating (runs.findActiveRun)
  • interrupts — pending human-in-the-loop state for re-prompt

Without authorize, anyone who guesses ?threadId= gets the transcript.

Generation activities

withGenerationPersistence(persistence) tracks run records for non-chat activities (image, audio, TTS, video, transcription). Do not fake threadId = requestId on chat run stores — use the generation helper.

Common mistakes

CRITICAL: Posting a message delta as messages

Wipes the stored thread down to that delta. Always send full history or [].

HIGH: Omitting threadId / runId

Persistence keys and resume need stable ids. Use chatParamsFromRequest.

HIGH: Interrupts without runs

interrupts requires runs; withPersistence throws otherwise.

HIGH: Typing a factory as bare AIPersistence

AIPersistence defaults to the sparse all-optional bag, so withPersistence and reconstructChat reject the value. Return ChatPersistence (or ChatTranscriptPersistence) instead.

MEDIUM: Expecting withPersistence to reconnect a dropped stream

That is delivery durability (resumable streams), not state persistence.

Cross-references

  • ai-persistence — layers and recommended stack
  • ai-persistence/stores — implement the store interfaces
  • ai-core/client-persistence (@tanstack/ai) — browser half
  • ai-core/locks — multi-instance coordination

版本历史

  • aade077 当前 2026-08-05 18:54

    新增沙箱实例持久化支持,优化withSandbox配置以直接传入实例存储而非中间件透传,并增强核心运行生命周期类型与流耐久性偏移量处理。

  • 05280a5 2026-07-30 23:52

同 Skill 集合

.claude/skills/gap-analysis/SKILL.md
packages/ai-code-mode/skills/ai-code-mode/SKILL.md
packages/ai-mcp/skills/ai-mcp/SKILL.md
packages/ai-memory/skills/tanstack-ai-memory-hindsight/SKILL.md
packages/ai-memory/skills/tanstack-ai-memory-honcho/SKILL.md
packages/ai-memory/skills/tanstack-ai-memory-in-memory/SKILL.md
packages/ai-memory/skills/tanstack-ai-memory-mem0/SKILL.md
packages/ai-memory/skills/tanstack-ai-memory-redis/SKILL.md
packages/ai-memory/skills/tanstack-ai-memory/SKILL.md
packages/ai-persistence/skills/ai-persistence/build-cloudflare-adapter/SKILL.md
packages/ai-persistence/skills/ai-persistence/build-cloudflare-artifact-store/SKILL.md
packages/ai-persistence/skills/ai-persistence/build-custom-adapter/SKILL.md
packages/ai-persistence/skills/ai-persistence/build-drizzle-adapter/SKILL.md
packages/ai-persistence/skills/ai-persistence/build-prisma-adapter/SKILL.md
packages/ai-persistence/skills/ai-persistence/SKILL.md
packages/ai-persistence/skills/ai-persistence/stores/SKILL.md
packages/ai/skills/ai-core/ag-ui-protocol/SKILL.md
packages/ai/skills/ai-core/chat-experience/SKILL.md
packages/ai/skills/ai-core/custom-backend-integration/SKILL.md
packages/ai/skills/ai-core/debug-logging/SKILL.md
packages/ai/skills/ai-core/locks/SKILL.md
packages/ai/skills/ai-core/middleware/SKILL.md
packages/ai/skills/ai-core/SKILL.md
packages/ai/skills/ai-core/tool-calling/SKILL.md
.claude/skills/triage-github/SKILL.md
packages/ai-sandbox/skills/ai-sandbox/SKILL.md
packages/ai/skills/ai-core/adapter-configuration/SKILL.md
packages/ai/skills/ai-core/client-persistence/SKILL.md
packages/ai/skills/ai-core/media-generation/SKILL.md
packages/ai/skills/ai-core/structured-outputs/SKILL.md

元信息

文件数
0
版本
aade077
Hash
39d841f7
收录时间
2026-07-30 23:52

首页 - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-06 13:42
浙ICP备14020137号-1 $访客地图$