PAMbaseDocs
How-to

Request context.

getContext() is the read primitive for a single moment in your app. You give it an intent (a label for the moment, e.g. opening the app) and an optional query (free text describing what you're looking for), and you get back a permission-filtered, relevance-scored context bundle: the slice of the user's memory that matters right now. You can act on it with or without your own model.

Prerequisites
You hold a connection token with the relevant read scopes — e.g. context:read:app.session.start and memory:read:note.* (see Connect an app). A scope is one permission. Examples use Margin, a reading companion.

The bundle, field by field (read this before the code)

Every getContext() call returns one bundle with these fields:

FieldWhat it is
identityThe light profile: just aiId (a stable handle for this user's memory) and an optional name (display name, only if shared via identity:read). Nothing else — see the callout below.
memoriesAn array of relevant memories, already filtered to your read scopes and scored. Each has id, type, scope, content, importance, occurredAt, and an optional sourceApp (which app wrote it).
relationshipsTyped links between memories, drawn from the memory graph — each a { subject, relation, object } triple (e.g. “essay relatesTo distributed-systems”). They tell you how facts connect.
suggestionsA ready-to-use hint: adaptation (a one-line, human-readable nudge for this moment) and optional tags (short topic labels you can branch on).
generatedAtISO timestamp of when the bundle was assembled.
typescript
const ctx = await pambase.getContext({
intent: "app.session.start",
query: "recent reading interests", // optional; informs the semantic match
limit: 10, // optional; default 10
});
// Shape:
{
identity: {
aiId: "ai_8f3c1b2d",
name: "Sam" // optional display name, only if shared (identity:read)
},
memories: [
{ id, type, scope, content, importance, occurredAt, sourceApp }
],
relationships: [
{ subject, relation, object }
],
suggestions: {
adaptation: "Reader has been deep on distributed systems — surface related long-form.",
tags: ["distributed-systems", "long-form", "infrastructure"]
},
generatedAt: "2026-05-22T18:04:11.002Z"
}
identity is light by design
The bundle's identity is only { aiId, name }. Never expect or render an archetype, personality, mood, or voice — users don't author personas, so those don't exist here. Your app brings the persona; PAMbase brings the person. See Identity.

Pick an intent that means something

The intent string biases retrieval toward the right kind of memory for the moment. Use a descriptive one tied to where the call happens:

IntentWhen Margin uses it
app.session.startThe user opens Margin — build the home screen.
app.companion.turnEach turn of Margin's recommendation chat.

Declare every intent in your manifest's contextIntents — the API rejects an undeclared intent. The standard set is in the Catalog.

Pattern A — adapt with no LLM

You don't need a model to personalize. Branch on suggestions.tags and the structured memories, and render suggestions.adaptation verbatim if you want a human-readable nudge. This is the cheapest, fastest path. Here Margin builds its home screen:

margin/home.ts
const ctx = await pambase.getContext({
intent: "app.session.start",
query: "recent reading interests",
});
// Drive the home screen straight from tags + memories — no model call.
const onSystems = ctx.suggestions.tags?.includes("distributed-systems")
|| ctx.memories.some(m => m.scope.startsWith("note") && /systems|consensus|rate limit/i.test(m.content));
if (onSystems) {
renderShelf({
heading: "Because you've been reading about distributed systems…",
items: recommendFrom("distributed-systems"),
});
} else {
renderShelf({ heading: "Pick up where you left off", items: recentReads() });
}
// Or just show the adaptation hint as-is:
showBanner(ctx.suggestions.adaptation);

Expected result: a returning reader lands on a shelf titled by their real interests — assembled from memory alone, with no model and no cost.

Pattern B — RAG with your own model

Hosting your own model? The memories list is already permission-filtered and scored, so feed it straight in as retrieval context (this is RAG — retrieval-augmented generation). The persona and instructions are yours; PAMbase only supplies the grounding facts.

margin/recommend.ts
const ctx = await pambase.getContext({
intent: "app.companion.turn",
query: "What should I read next?",
});
const memoryBlock = ctx.memories
.map(m => `- [${m.scope}] ${m.content} (importance ${m.importance})`)
.join("\n");
const system = [
marginPersona, // YOUR app's persona, not PAMbase's
ctx.identity.name && `The reader's name is ${ctx.identity.name}.`,
`What you remember about the reader:\n${memoryBlock}`,
].filter(Boolean).join("\n\n");
const reply = await myReadingModel.chat({ system, messages });

Expected result: your model recommends a next read grounded in the user's saved highlights and observed taste, in Margin's own voice.

Don't want to host a model at all?
If you'd rather PAMbase host the model and assemble the prompt for you, use chat() — it builds the context bundle internally. For a one-line portrait instead of structured memories, use the memory brief.

Empty results?

A new user, a missing read scope, or the wrong scope filter all yield few or no memories. Confirm what this user actually granted (and how much data is in each scope) with listUserScopes() before assuming the store is empty.

What to expect next

  • Memory — how memories are typed, scoped, and scored.
  • Vector graph — where relationships come from.
  • Host the AI — let PAMbase build the bundle and run the model.