Skip to content

Chats

Permission required: chats

List, inspect, update, and delete chat sessions. This operates on chat entities (metadata, name, lifecycle). For reading and modifying individual messages within a chat, see Chat Mutation.

Usage

// List all chats (paginated)
const { data, total } = await spindle.chats.list({ limit: 20, offset: 0 })

// List chats for a specific character
const characterChats = await spindle.chats.list({ characterId: 'char-id' })

// Get a single chat
const chat = await spindle.chats.get('chat-id')
if (chat) {
  spindle.log.info(`Chat "${chat.name}" with character ${chat.character_id}`)
}

// Get the user's currently active chat
const active = await spindle.chats.getActive()
if (active) {
  spindle.log.info(`User is in chat: ${active.name}`)
} else {
  spindle.log.info('No active chat')
}

// Update a chat's name or metadata
const updated = await spindle.chats.update('chat-id', {
  name: 'Renamed Chat',
  metadata: { custom_field: 'value' },
})

// Delete a chat (cascades all messages)
const deleted = await spindle.chats.delete('chat-id')

Methods

Method Returns Description
list(options?) Promise<{ data: ChatDTO[], total: number }> List chats. Options: { characterId?, limit?, offset? }. Defaults: limit 50, max 200.
get(chatId) Promise<ChatDTO \| null> Get a chat by ID. Returns null if not found.
getActive() Promise<ChatDTO \| null> Get the user's currently active chat. Returns null if none is open.
update(chatId, input) Promise<ChatDTO> Update a chat's name or metadata.
delete(chatId) Promise<boolean> Delete a chat and all its messages. Returns true if deleted.

ChatDTO

{
  id: string
  character_id: string
  name: string
  metadata: Record<string, unknown>
  created_at: number   // unix epoch seconds
  updated_at: number
}

ChatUpdateDTO

Field Type Description
name string New chat name
metadata Record<string, unknown> Replace the chat's metadata object

Both fields are optional. Only provided fields are updated.

Active Chat

getActive() reads the activeChatId setting that the frontend persists whenever the user opens or closes a chat. This lets extensions discover the current chat without subscribing to events.

You can also react to chat switches in real time by subscribing to the CHAT_SWITCHED event:

spindle.on('CHAT_SWITCHED', (payload) => {
  if (payload.chatId) {
    spindle.log.info(`User switched to chat ${payload.chatId}`)
  } else {
    spindle.log.info('User returned to the home screen')
  }
})
// React to the active chat
const active = await spindle.chats.getActive()
if (active) {
  // Combine with chat mutation to read messages
  const messages = await spindle.chat.getMessages(active.id)
  spindle.log.info(`Active chat has ${messages.length} messages`)
}

Chat Memories

Retrieve long-term memory chunks for a chat via vector search. This is the same memory system that powers the {{memories}} macro during prompt assembly — semantic search over vectorized chat history using embeddings.

spindle.chats.getMemories(chatId, options?)

const memories = await spindle.chats.getMemories('chat-id')

if (memories.enabled && memories.count > 0) {
  spindle.log.info(`Retrieved ${memories.count} memory chunks`)
  spindle.log.info(`Available: ${memories.chunksAvailable}, Pending: ${memories.chunksPending}`)

  for (const chunk of memories.chunks) {
    spindle.log.info(`Score: ${chunk.score}${chunk.content.slice(0, 80)}...`)
  }

  // The formatted string is ready to inject (uses the user's template settings)
  spindle.log.info(`Formatted output:\n${memories.formatted}`)
} else {
  spindle.log.info('Chat memory is not enabled or no chunks available')
}

Override the number of chunks retrieved with topK:

// Get the top 8 most relevant memories instead of the default (usually 4)
const memories = await spindle.chats.getMemories('chat-id', { topK: 8 })

Options

Field Type Description
topK number Override the number of chunks to retrieve (default comes from user's chat memory settings, usually 4). Range: 1-24.
userId string For operator-scoped extensions only.

ChatMemoryResultDTO

Field Type Description
chunks ChatMemoryChunkDTO[] The retrieved memory chunks, sorted by relevance.
formatted string Pre-formatted output using the user's memoryHeaderTemplate and chunkTemplate settings. Ready to inject.
count number Number of chunks retrieved.
enabled boolean Whether chat memory is enabled (requires embedding config + vectorized chat messages).
queryPreview string Truncated query text built from recent messages (for debugging).
settingsSource string "global" or "per_chat" — where the memory settings came from.
chunksAvailable number Total vectorized chunks in the store for this chat.
chunksPending number Chunks awaiting vectorization. If > 0, results may be incomplete.

ChatMemoryChunkDTO

Field Type Description
content string The chunk text (concatenated messages from a conversation segment).
score number Similarity score (lower = more similar in cosine distance).
metadata Record<string, unknown> Chunk metadata (may include startIndex, endIndex, etc.).

Prerequisites

Chat memories require the user to have embedding/vectorization configured (an embedding provider with an API key) and vectorize_chat_messages enabled. When not configured, the method returns { enabled: false, chunks: [], count: 0, ... } without error.

Memories vs Chat Mutation

  • getMemories() — retrieves semantically relevant past conversation segments via vector search. Read-only, no side effects.
  • spindle.chat.getMessages() (Chat Mutation) — returns the full raw message list for a chat.

Chats vs Chat Mutation

  • spindle.chats (this page) — CRUD on chat sessions (list, get, rename, delete). Permission: chats.
  • spindle.chat (Chat Mutation) — read and modify messages within a chat. Permission: chat_mutation.

These are separate permissions so extensions can request only the access they need.

Note

For user-scoped extensions, the user context is inferred automatically. For operator-scoped extensions, the user ID is resolved from the extension context.