---
title: "ai.oz0/memory"
description: "Notes, decisions and definitions agents keep beyond a job, under the platform's retention, holds and erasure, and the accepted ones in an agent's start-up context."
canonical_url: "https://docs.oz0.ai/build/plugins/verified/memory"
---
# ai.oz0/memory

> Notes, decisions and definitions agents keep beyond a job, under the platform's retention, holds and erasure, and the accepted ones in an agent's start-up context.

For: tenant administrators and plugin users.

`ai.oz0/memory` stores notes, decisions and term definitions beyond the end of a job. Its entries use [plugin records](https://docs.oz0.ai/build/parts/records-and-startup), so tenant [retention, legal holds and erasure](https://docs.oz0.ai/govern/personal-data/plugin-records#what-plugins-keep) apply to them.

Tenant exports include the entries, and the audit log records each read with the returned versions. Search matches words; it does not use embeddings or semantic search.

<table>
<thead>
  <tr>
    <th>
      Function
    </th>
    
    <th>
      What it does
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        oz0_memory.remember
      </code>
    </td>
    
    <td>
      Keep a note or a decision, about work (the default) or about people (<code>
        about: person
      </code>
      
      ). A decision is proposed until a person accepts it. The same text (case and spacing aside) is kept once per scope
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        oz0_memory.recall
      </code>
    </td>
    
    <td>
      Read entries by scope, kind and words: the newest version of each, with its ID and version; <code>
        include_ended
      </code>
      
       adds replaced entries and those past their review date
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        oz0_memory.supersede
      </code>
    </td>
    
    <td>
      Replace an entry, naming the version you read: the new entry links to the old one and has only the tags the call gives. A note or a proposed decision is replaced at once; an accepted one stays in force until a person accepts its replacement
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        oz0_memory.accept
      </code>
    </td>
    
    <td>
      Accept a proposed decision or definition, naming its text (and a definition's term). The call asks a person by default, and the person reads the text and the term being accepted
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        oz0_memory.changed_since
      </code>
    </td>
    
    <td>
      What was written, replaced, accepted or ended since this agent last asked
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        oz0_memory.define
      </code>
    </td>
    
    <td>
      Read a term's definition, or propose one: one definition per term and scope. A new definition of an accepted term is proposed, and the accepted one stays in force until it is accepted
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        oz0_memory.startup_context
      </code>
    </td>
    
    <td>
      Called by the platform when a job starts, never by a model
    </td>
  </tr>
</tbody>
</table>

The agent `oz0-decision-log` keeps a team's decision log and glossary with these functions.

## Safe by default

Memory follows the tenant's rules, which the platform holds, not the plugin:

- Work or people. Every entry is memory about work (decisions, procedures, definitions, facts about systems) or
about people (a preference, what someone asked or decided). Memory about people is off for every agent until a
tenant admin turns it on for that agent and a purpose ([below](https://docs.oz0.ai/build/plugins/verified/memory#memory-about-people)). The writer's word is not
enough: whatever a job with a [data subject](https://docs.oz0.ai/govern/personal-data) writes counts as memory about people, and is
refused while that is off, with the reason and the command that turns it on.
- Every entry ends. On the date its writer gives (`ends`), at the latest the tenant's longest end (365 days by
default), or else after the tenant's default (90 days). An entry about people is deleted at its end. An entry about
work leaves start-up context and recall at its end, which is a review date: a `memory_review` finding asks its
owner to renew it or let it go, and nothing about work is deleted before retention.

### Purposes

Every entry carries the purpose its job ran for (the purpose the job tree was started with, never what the plugin says). Jobs read entries written for their own purpose, or for one the tenant maps as compatible, and entries about work written without a purpose, which are the tenant's general knowledge. A job without a purpose reads only entries written without one.

A job changes only entries of its own purpose: reading through a compatible pair never lets it write, and an entry about work written without a purpose (the tenant's shared decision log and glossary) is changed only by jobs started without one, so it stays in force for every purpose. `supersede` and `accept` check every entry they would change before writing anything, and refuse with the purpose that may change it; a job that disagrees with a shared decision proposes a new one of its own purpose instead.

- Caps. One plugin keeps at most 5,000 entries and 8 MiB in one scope by default. Replaced entries and entries past
their end count until retention (or, for entries about people, their end) removes them. A write past a cap is
refused with the cap in the answer, never dropped.

<table>
<thead>
  <tr>
    <th>
      Setting
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      How
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Default end
    </td>
    
    <td>
      90 days
    </td>
    
    <td>
      <code>
        oz0 memory settings set --default-days N
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Longest end
    </td>
    
    <td>
      365 days (at most 3,650)
    </td>
    
    <td>
      <code>
        oz0 memory settings set --max-days N
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Entries per scope
    </td>
    
    <td>
      5,000 (at most 20,000)
    </td>
    
    <td>
      <code>
        oz0 memory settings set --max-records N
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Bytes per scope
    </td>
    
    <td>
      8 MiB (at most 64 MiB)
    </td>
    
    <td>
      <code>
        oz0 memory settings set --max-bytes N
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Compatible purposes
    </td>
    
    <td>
      none
    </td>
    
    <td>
      <code>
        oz0 memory settings set --compatible from>to
      </code>
    </td>
  </tr>
</tbody>
</table>

Each change is a [governance change](https://docs.oz0.ai/govern/reviewed-changes#four-eyes-for-governance-changes): it takes a reason,
and a longer end, a higher cap or a new compatible pair waits for another admin while four eyes is on. A pair is one
way: `--compatible support>billing` lets jobs for `billing` read what was written for `support`, not the reverse, and
never write it. Changes apply to writes from then on. After a shorter longest end, accepting or replacing an entry
brings the end it carries down to the new longest end.

## Memory about people

```bash [Terminal]
oz0 inventory purposes set --legal-basis contract='The support contract' --purpose customer-support='Answering customers' \
  --purpose-basis customer-support=contract --reason 'DPIA 2026-14'
oz0 memory person support-triage --purpose customer-support --reason 'DPIA 2026-14 approved'
oz0 run support-triage "…" --purpose customer-support
```

- The purpose must be one the agent's definition declares (`purposes:`), with a legal basis in the tenant's lists
([purposes and legal bases](https://docs.oz0.ai/govern/personal-data)). The platform checks the basis again at every write, so a basis
removed from the lists stops new entries at once.
- Only the agent's jobs that run for one of those purposes keep memory about people; a job without a purpose never
does.
- Its entries about people stay in its own scope. `--share agent:<name>` lets another agent read them (and only them);
`--share project:<name>` or `--share tenant` lets them be written there.
- Turning it on, or sharing it further, waits for another admin while four eyes is on, and **always in works-council
mode** (on for every tenant by default), since the platform cannot tell which people are employees. `--off` turns it
off at once.
- What the tenant may keep about people, for which purpose and on which legal basis, is the tenant's to decide; the
platform enforces what the tenant set.

`oz0 memory settings` shows the settings and every agent that keeps memory about people.

## Review dates

An entry about work past its end is out of agents' start-up context and recall. The expiry run, part of the platform's
retention pass, opens one finding per plugin and scope that lists such entries:

```bash [Terminal]
oz0 eval findings --kind memory_review
oz0 memory renew memory tenant d-3f2a91c07b --days 180
```

Renewing moves the entry's review date (the tenant's default when `--days` is left out, at most its longest end), and the finding closes once nothing of its scope is due. To let the entries go, resolve the finding with a reason (`oz0 eval resolve ID --reason ...`): it opens again only when another entry of that scope reaches its review date. An entry let go stays out of start-up and recall until retention deletes it. Entries about people cannot be renewed: their end deletes them, unless the writing agent's [keep-for period](https://docs.oz0.ai/govern/personal-data/retention#keep-what-a-law-asks-you-to-keep) runs longer. A legal hold stops the run.

Each run that changed something is in the audit log as `plugin.records.expired`, with counts only.

## Scopes

Its agent `oz0-decision-log` asks for the tenant's `fast` [model alias](https://docs.oz0.ai/spend/models-and-keys/aliases#the-standard-aliases) with tools, never a vendor's model, so it runs on whichever provider the tenant set up first; map it elsewhere with `oz0 model alias set fast <provider>/<model>`.

An entry lives in one scope:

- `agent`: the calling agent's own. Only jobs of that agent read or write it; the platform holds them to it. Another
agent reads the entries about people in it only when the tenant shares them with that agent.
- `project:<name>`: a project's, which any agent that uses the plugin reads, for the purposes it may read. The plugin
remembers which projects each agent used.
- `tenant`: the whole tenant's.

Notes and entries about people go to the agent's own scope unless the call names another; decisions and definitions
go to the tenant's. Entries about people go elsewhere only where the tenant shares them.

## Decisions and definitions in the start-up context

When a job of an agent that uses the plugin starts, the platform asks the plugin for the accepted decisions and definitions that have not ended, of the purposes the job may read, and adds them to the agent's start-up context, after its instructions and its skills index, under the heading `## Context from plugin memory`. The agent's own scope comes first, then its projects', then the tenant's, newest first.

They share the agent's `limits.startup_tokens` (4,000 by default) with its skills index: what does not fit is left out in order, and the agent reads it with `recall` (by scope) or `define` (by exact term). A job that continues as new keeps the text its first run got.

```text
## Context from plugin memory

- Accepted decisions and definitions you work under; more with recall (by scope) or define (by term).
- Decision (tenant): Deploy on Wednesdays, never on Fridays. [tenant/d-3f2a91c07b v2]
- Definition of 'active customer' (tenant): A customer with an order in the last 90 days. [tenant/t-5b1e0c2d4a v2]
```

The job's trace shows the start-up step (`startup memory`), and the audit log's `plugin.records.read` entry for it
lists the entries and versions the agent was shown, with `startup: true`, never their text.

## Accepting

The plugin's suggested policy asks a person about every write by default ("the plugin asks before it writes"): `remember`, `define`, `supersede`, `changed_since` (whose only write is the agent's own bookmark) and `accept`, which asks before it accepts a decision or a definition ([approvals](https://docs.oz0.ai/agents/approvals)).

The install writes these as rules of the tenant's tool policy, marked as the plugin's; an admin switches them with a reason, for one function or the group: `oz0 plugin policy memory --set write=allow --reason "agents keep their own notes"`, and `--set accept=allow` (or a [rule by caller](https://docs.oz0.ai/govern/tool-policy/conditions#rules-by-caller) for some agents) where some decisions may be accepted without a person.

The call names the entry, its version and its text, and a definition's term too, and is refused when the text or the term is not that version's: the person approving reads what will reach every agent's start, not only an ID. Up to 1.1.0 `accept` was under `requires_approval`, which no tenant rule could lower; an update from one of those keeps asking about it until an admin switches it.

What a person accepted stays in force until a person accepts what replaces it. Superseding an accepted decision, or proposing a new definition of an accepted term, only proposes: the start-up context keeps the accepted one, and `define` shows both. Accepting the replacement ends the old decision, or makes the new definition the one in force. A proposal superseded by a better one before anyone accepted it ends at once, and its successor still replaces the accepted decision, so accepting the last proposal ends that one.

When another replacement of the same decision was accepted in the meantime, accepting is refused: supersede the decision now in force instead, so that two never hold at once. No call without an approval takes an accepted entry out of agents' start-up context; an entry's review date (its end) and the tenant's retention do, and renewing it brings it back.

## Versions, stale writes and supersede

Every write is a new version; the old ones stay. `supersede` and `accept` name the version the agent read, and are
refused when a newer one exists, so two agents cannot overwrite each other's work unseen: the refused agent recalls
the entry again and decides from its newest version. A replaced entry is closed by a version that links to its
successor, and keeps its own end.

A call that the platform retries (a node lost on the way, a timeout) writes once and answers with what its first try
wrote. Entry IDs say nothing of what they hold: a definition's ID is not its term, so the audit log and an export
without content never show the term.

## Trust and walls

What a job reads from memory joins its [labels](https://docs.oz0.ai/govern/taint-and-labels) with the source `memory`: an entry written
by a job that was tainted, or by an agent of another plugin than the reader's, taints the job that reads it, from its
start when the start-up step read it. An entry keeps the compartments of the job that wrote it, and what a call read
goes with what it writes next.

When the tenant's label policies keep compartments apart, a read withholds an entry whose compartments the tenant keeps
apart from the reading job's, or from those of an entry the same call was already shown, so that one call never reads
both sides: the answer says how many entries were withheld, never what they hold. A tenant without such a policy has no
walls, and nothing is withheld.

## Secrets

A write whose text looks like a key or a token in a well-known format (cloud and source-control keys, provider API
keys, private keys, tokens in URLs) is refused with what it seems to hold, never echoed. The platform also refuses any
tool call whose arguments carry one of the tenant's own secrets.

## Install

```bash [Terminal]
oz0 plugin install hub:ai.oz0/memory
oz0 run oz0-decision-log "We decided to deploy on Wednesdays from now on"
```

Agents of other plugins use it by naming its functions in their `tools` and the plugin in their manifest's `uses:`,
which a tenant admin grants at install. Such an agent gets the start-up text too; to give it no functions but the
start-up text, name `oz0_memory.recall` only.

## A glossary in a skill or in memory

A glossary that is part of how a plugin works, the same for every tenant and reviewed with the plugin's code, belongs
in a [skill](https://docs.oz0.ai/build/parts/skills): it ships and changes with the plugin's versions. Terms your own people decide,
that change while agents run and need someone to accept them, belong in memory as definitions.


## Sitemap

See the full [sitemap](https://docs.oz0.ai/sitemap.md) for all pages.
