# Everos

Persistent agent memory

- Package: @bijectionhq/everos
- Category: AI & Agents
- Status: Preview
- License: Apache-2.0
- Bijection source (repository access required): https://github.com/bijectionhq/everos/tree/a04f24a5cd2c27aef9f8cce2ee0b2b3aec327494
- Upstream: https://github.com/EverMind-AI/EverOS-Convex/tree/d7093b3ef4e37c024a2001bc9ffd2d9cde515194
- Documentation: https://docs.bijection.com/components/using
- Directory: https://bijection.com/components/everos

## Get the source

Bijection repository access is required. Request access at https://bijection.com/contact.

```sh
git clone https://github.com/bijectionhq/everos.git
cd everos
git checkout a04f24a5cd2c27aef9f8cce2ee0b2b3aec327494
```

## Before using this component

Read the package README at the recorded revision for setup, dependencies, and API examples. Upstream documentation describes the original package and may differ from the Bijection adaptation. This is a source candidate for evaluation. Catalog inclusion does not establish npm availability or runtime qualification. Check compatibility with your SDK and deployment before adoption. External providers run their own compute.

## Upstream package documentation

Recorded 2026-10-23. This describes @everos-ai/convex; check the Bijection adaptation separately.

EverOS Memory provides persistent, cross-thread, cross-session long-term memory for Convex apps and agents via the EverOS Cloud API. It exposes `remember` and `recall` methods that handle async memory extraction, local pending queues, and ranked retrieval with atomic fact provenance. The component integrates directly with `@convex-dev/agent` as either a tool or context injection, making user memory portable across agents, models, and sessions without requiring manual vector search infrastructure.

### Use cases

- Building a multi-agent system where different agents or models need shared user context without access to each other's chat history
- Adding user preference and behavioral memory to a support or assistant app so returning users are recognized across sessions
- Constructing RAG context for agent prompts from structured user memories rather than raw chat log embeddings
- Storing and auditing user-generated facts with timestamped, session-scoped provenance for consequential decisions like refunds or account changes
- Implementing per-user memory namespacing across staging and production deployments on a single EverOS account

### How it works

After installing the package and registering the component in `convex/convex.config.ts`, you instantiate `new EverOS(components.everos)` in your Convex module. Credentials are set via `EVEROS_API_KEY` in your Convex deployment environment and threaded into component calls as arguments, following Convex component authoring conventions where secrets are resolved in the app rather than inside the component itself.

The `remember` method is a Convex mutation that enqueues content locally and flushes it to EverOS Cloud asynchronously. Extraction typically completes in 15 to 30 seconds. During that window, `recall` returns pending items marked `pending: true` from the local queue alongside already-extracted memories, so no content is invisible immediately after ingestion. Once extraction completes, queue rows are deleted and all reads go directly to EverOS.

The `recall` action returns ranked episodic memories, a semantic user profile, and any pending items. Each episodic result includes `atomicFacts` with individual scores, timestamps, and session IDs for provenance tracing. For agent integration, `everos.asTool({ userId })` produces a `@convex-dev/agent`-compatible tool, and `everos.contextMessages` returns a message array for prepending recalled context before a prompt. Namespace isolation between environments is handled by passing `appId` and `projectId` options to the constructor.

### Upstream README

# @everos-ai/convex

Long-term memory for [Convex](https://convex.dev) apps, backed by
[EverOS Cloud](https://evermind.ai/everos), the hosted service of
[EverOS](https://github.com/EverMind-AI/EverOS) — the open-source agent memory
OS (state of the art on LoCoMo).

Drop-in `remember` / `recall` / user memory profiles for any Convex app, plus
**one-line long-term memory for [`@convex-dev/agent`](https://docs.convex.dev/agents)**:
persistent, cross-thread, cross-session memory that goes beyond vector search
over chat logs.

```ts
const everos = new EverOS(components.everos);

await everos.remember(ctx, { userId, content: "I'm vegetarian" });
const memories = await everos.recall(ctx, { userId, query: "what do I eat?" });
```

---

## Install

```bash
npm install @everos-ai/convex @convex-dev/agent zod
```

> `@convex-dev/agent` and `zod` are peer dependencies. The main entry imports
> them for `asTool`, and Convex resolves imports when it pushes your code, so
> they are required even if you only call `remember` / `recall`. Splitting the
> agent helpers into their own entry point is tracked for a later release.
> Both `@convex-dev/agent` 0.6 (AI SDK v6) and 0.7 (AI SDK v7) are supported;
> npm installs the matching `ai` packages as its peers.

### 1. Register the component

```ts
// convex/convex.config.ts
import { defineApp } from "convex/server";
import everos from "@everos-ai/convex/convex.config";

const app = defineApp();
app.use(everos);
export default app;
```

### 2. Set your EverOS API key

Get a free key at **[everos.evermind.ai](https://everos.evermind.ai/)** (free
tier; API reference at
[docs.evermind.ai](https://docs.evermind.ai/api-reference/introduction)), then:

```bash
npx convex env set EVEROS_API_KEY <your-key>
# optional — defaults to https://api.evermind.ai
npx convex env set EVEROS_BASE_URL https://api.evermind.ai
```

To keep separate memory spaces on one account (staging, a public demo,
production), give each a namespace:

```ts
new EverOS(components.everos, { appId: "staging", projectId: "staging" });
```

Reads, writes and deletes are all scoped to it, so nothing under one namespace
is searchable or deletable from another. Issuing a second API key does **not**
do this: keys on the same account share a namespace. `EVEROS_APP_ID` /
`EVEROS_PROJECT_ID` work too.

The app-side client reads credentials from `process.env` (you can also pass
`{ apiKey, baseUrl }` to `new EverOS(...)`). Per Convex component authoring
best practice, secrets are resolved in your app and threaded into the component
— they are never read inside the component itself.

---

## Usage

> **Memory extraction is asynchronous.** EverOS runs layered extraction on
> ingested content in the background — after `remember`, extracted memories
> typically become searchable within **~15–30 seconds** (the component drives
> extraction automatically). Until then, `recall` still returns the freshly
> saved content from the component's local queue, marked `pending: true`, so
> nothing is ever invisible. Long-term memory complements (doesn't replace)
> your thread's conversation context: use the thread for "what was just
> said", EverOS for "what's worth remembering across sessions".

```ts
// convex/memory.ts
import { action, mutation } from "./_generated/server";
import { components } from "./_generated/api";
import { v } from "convex/values";
import { EverOS } from "@everos-ai/convex";

const everos = new EverOS(components.everos);

export const remember = mutation({
  args: { content: v.string() },
  handler: async (ctx, { content }) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject; // auth in the app
    await everos.remember(ctx, { userId, content });
  },
});

export const recall = action({
  args: { query: v.string() },
  handler: async (ctx, { query }) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;
    return await everos.recall(ctx, { userId, query, topK: 5 });
  },
});
```

`recall` returns ranked memories with provenance, plus anything remembered in
the last few seconds that EverOS has not finished extracting yet:

```ts
// abridged: every item also carries everosMemoryId and userId
[
  {
    kind: "episodic",
    text: "Alex Chen said he is on the Pro plan, billed annually, and asked to be contacted by email.",
    score: 0.41,
    timestamp: 1789509522956,
    sessionId: "support-2026-09",
    atomicFacts: [{ text: "Alex Chen is on the Pro plan.", score: 0.41, timestamp: 1789509522956 }],
  },
  { kind: "profile", text: "Prefers email over phone.; Uses a Node.js stack on Vercel." },
  { kind: "episodic", text: "Our webhooks started failing again this morning.", pending: true },
]
```

### API

| Method | Kind | Description |
| --- | --- | --- |
| `everos.remember(ctx, { userId, content, role?, senderName?, sessionId?, timestamp? })` | mutation | Enqueue content; flushed to EverOS asynchronously |
| | | Pass `senderName` whenever `userId` is opaque (a UUID, an auth subject). It is the attribution key, and without a display name extraction writes it into the fact text itself — `"1036ffce-… said their webhooks fail"`. |
| `everos.rememberMessages(ctx, { userId, messages, sessionId? })` | mutation | Remember a whole turn (prompt **and** reply) in one call; each message takes its own `senderName` |
| `everos.recall(ctx, { userId, query, topK?, kind?, includeRecent? })` | action | Retrieve relevant memories, ranked (plus not-yet-extracted content marked `pending`) |
| | | `topK` bounds the ranked episodes only. Profiles ride along, and up to 5 `pending` items may be appended, so size prompts from the returned array rather than from `topK`. |
| `everos.getProfile(ctx, { userId })` | action | Fetch the user's profile / semantic memory |
| `everos.forgetSession(ctx, { userId, sessionId })` | action | Delete one session's memories remotely + locally |
| `everos.forgetUser(ctx, { userId })` | action | Delete ALL of a user's memories remotely + locally |
| `everos.getPendingStatus(ctx, { userId })` | query | Whether anything is still on its way to EverOS, and why if it is stuck |
| `everos.listMemories(ctx, { userId, page?, pageSize? })` | action | Page through everything EverOS holds for a user, newest first |

> **If something looks missing**, `getPendingStatus` says whether it is still
> in flight or actually failed. A rejected API key shows up there as
> `lastError`; without checking it, a bad key is indistinguishable from
> extraction being slow. The component keeps no copy of your memories: queue
> rows are deleted once EverOS has extracted them, and `recall` /
> `listMemories` always read EverOS itself. Internals are in
> [CONTRIBUTING.md](./CONTRIBUTING.md).

---

## Security model

**`userId` is trusted input — derive it server-side.** The component performs
no authentication of its own (it can't; only your app knows who is signed in).
Every method reads and writes exactly the memory space of the `userId` you
pass, so a `userId` taken from a client-supplied argument lets any caller
read — or, via `forgetUser`, delete — anyone's memories. Always derive it
from your app's auth, as every example in this README does:

```ts
const userId = (await ctx.auth.getUserIdentity())!.subject; // never an arg
```

**Memories are user-generated content, not verified facts.** Whatever a user
says is extracted and later replayed into prompts as context. That is the
point of a memory system, but if your agent makes decisions on it (refunds,
account changes, access), remember that users can seed their own memory with
convenient "facts" ("support agreed to a full refund last month"). For
anything with consequences, audit the claim: each recalled memory decomposes
into `atomicFacts` with timestamps and session provenance.

**Where the API key travels.** The key lives in your deployment's environment
variables and is threaded into component calls as an argument, so it also
appears in scheduled-function arguments (the `_scheduled_functions` system
table) — visible to people with dashboard access to your deployment, who can
already read your environment variables. It is never exposed to clients, and
component functions are not callable from clients. Requests refuse non-HTTPS
base URLs (loopback excepted, for self-hosted local dev), and `baseUrl`
should only ever come from an environment variable or a constant — never from
user input.

---

## Agent integration (`@convex-dev/agent`)

Add persistent memory to any Convex agent in one line — either as a **tool** the
model calls, or as **context** you prepend.

```ts
import { Agent } from "@convex-dev/agent";
import { openai } from "@ai-sdk/openai";
import { EverOS } from "@everos-ai/convex";
import { components } from "./_generated/api";

const everos = new EverOS(components.everos);
const agent = new Agent(components.agent, {
  languageModel: openai.chat("gpt-4o-mini"),
  instructions: "You have long-term memory of the user. Use searchMemory.",
});

export const chat = action({
  args: { threadId: v.string(), prompt: v.string() },
  handler: async (ctx, { threadId, prompt }) => {
    const userId = (await ctx.auth.getUserIdentity())!.subject;

    // Option A — give the agent a memory-search tool:
    const result = await agent.generateText(
      ctx,
      { threadId, userId },
      { prompt, tools: { searchMemory: everos.asTool({ userId }) } },
    );

    // Option B — prepend recalled memories as context:
    const messages = await everos.contextMessages(ctx, { userId, prompt });
    // await agent.generateText(ctx, { threadId, userId }, { messages, prompt });

    // Remember the turn for future threads/sessions:
    await everos.remember(ctx, { userId, content: prompt });
    return result.text;
  },
});
```

- `everos.asTool({ userId, topK? })` → a `@convex-dev/agent` tool that searches
  long-term memory.
- `everos.contextMessages(ctx, { userId, prompt, topK? })` → an array of
  `{ role, content }` messages to pass as `messages` before your `prompt`.

---

## Demo app

[`example/`](./example) is **Mindy**, an AI support desk built on
`@convex-dev/agent` + this component. A returning customer chats with the
frontline agent (GPT-4o-mini); clicking **Escalate to specialist** hands the
conversation to a different agent on a different model (Llama-3.3-70B) in a
**brand-new thread with zero shared chat history** — the specialist greets the
customer already knowing their plan, stack, and issue, purely from EverOS
memory. A live agent console shows the customer's memory profile, per-message
recalled context with traceable atomic facts (scored + timestamped), and the
memory pipeline activity feed. It demonstrates the differentiator vs a model's
built-in memory: **memory belongs to the user — portable across agents,
models, and sessions — and auditable**.

```bash
cd example
npm install
npx convex env set EVEROS_API_KEY <your-key>
# LLM: either OpenRouter (any model) or plain OpenAI
npx convex env set OPENROUTER_API_KEY sk-or-...   # uses openai/gpt-4o-mini
# or: npx convex env set OPENAI_API_KEY sk-...
npm run dev
```

To reset between recordings: `npx convex run demo:clearAll` wipes all demo
conversations, the agent console state, and the customers' EverOS memories.

---

## Testing

To test the component inside your own app, register it with a
[`convex-test`](https://docs.convex.dev/testing/convex-test) instance:

```ts
import { convexTest } from "convex-test";
import schema from "./schema.js";
import everos from "@everos-ai/convex/test";

const t = convexTest(schema, import.meta.glob("./**/*.ts"));
everos.register(t);
```

---

## Contributing

Design notes, how the ingestion pipeline works, and local development setup
are in [CONTRIBUTING.md](./CONTRIBUTING.md). Changes go through pull requests;
CI (build, tests, secret scan) must pass before merging.

---

## License

Apache-2.0 © [EverMind](https://evermind.ai)