TypeScript Conversation History

This guide continues from TypeScript Getting Started and adds history channel recording.

Prerequisites

Starting checkpoint: typescript/examples/vecelai/doc-checkpoints/02-with-memory

Make sure you’ve completed the TypeScript Getting Started guide first.

Add History Recording

Checkpoint 03 adds only the history recording delta:

app.ts

  const result = await withMemoryService(
    {
      ...memoryServiceConfig,
      conversationId,
      authorization,
      userText: userMessage,
      memoryContentType: "vercelai",
    },
    async (contextMemory) => {
      contextMemory.append({ role: "user", content: userMessage });
      const generated = await generateText({
        model,
        messages: [
          {
            role: "system",
            content: "You are a TypeScript memory-service demo agent.",
          },
          ...contextMemory.get(),
        ],
      });
      contextMemory.append({ role: "assistant", content: generated.text });
      return generated;
    },
  );
  const assistantText = result.text;

  res.type("text/plain").send(assistantText);

What changed: The app uses withMemoryService(...) and passes userText.

Note: When userText is provided, withMemoryService(...) records both the USER history turn and the resulting AI history response automatically.

Why needed: memory keeps model context while history stores user-facing conversation turns for timeline, replay, and search features.

Make sure you define a shell function that can get the bearer token for the bob user:

function get-token() {
  curl -sSfX POST http://localhost:8081/realms/memory-service/protocol/openid-connect/token \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "client_id=memory-service-client" \
    -d "client_secret=change-me" \
    -d "grant_type=password" \
    -d "username=bob" \
    -d "password=bob" \
    | jq -r '.access_token'
}

Send a chat turn:

curl -NsSfX POST http://localhost:9090/chat/52b0f67a-6d8a-4d35-a2e5-8abf0e7f1c22 \
  -H "Content-Type: text/plain" \
  -H "Authorization: Bearer $(get-token)" \
  -d "Give me a random number between 1 and 100."

Example output:

42

Filtering conversations by metadata

listConversations accepts a metadata array of filter expressions. The service combines them with AND and supports at most five predicates.

import { createMemoryServiceProxy } from "@chirino/memory-service-vercelai";

const proxy = createMemoryServiceProxy(config);

const res1 = await proxy.listConversations({ metadata: ["status=waiting"] });

const res2 = await proxy.listConversations({
  metadata: ["status=waiting", "agent-id!=worker-2"],
});
const { data: conversations } = await res2.json();

Operators are = (equal) and != (not equal). Matching is exact and case-sensitive against scalar string values. Missing keys, null, numbers, booleans, arrays, and objects do not match either operator. Filter keys may contain only ASCII letters, digits, underscores, and hyphens.

Completed Checkpoint

Completed code: typescript/examples/vecelai/doc-checkpoints/03-with-history

Next Steps

Continue to: