Memory

ExploreSelf-hostDocumentationBlogSign In
Documentation
Overview
Getting Started
Getting StartedPlatform IntegrationsAgentPlaybooks in ClaudePortable Bot TeamsHermes Memory ProviderHermes for OrganisationsAgentPlaybooks Tools in HermesCLI & Editor PluginsPython SecretsCross-platform Agent BackupsRelease DistributionTeam CollaborationFederated MCP & OpenAPI ToolsMCP Registry PublishingRoadmap
Core Concepts
PlaybooksSkillsMemoryMCP Integration
Reference
ArchitectureAPI ReferenceManagement APIDeveloper GuideSelf-HostingEnvironment Setup
View on GitHub
DocsMemory

Memory

Memory is a persistent, hierarchical storage system that AI agents can read from and write to. It enables agents to maintain context across sessions, manage complex task plans, and share information between AI platforms.

Hermes native memory provider

Follow the Hermes memory setup guide for direct installation, configuration, a first-session check, and the current plugin-store status.

AgentPlaybooks includes a native Hermes provider in packages/hermes-memory. It integrates with hermes memory setup and Hermes Desktop's memory settings, mirrors explicit built-in memory writes, and exposes memory read/write/search, archive, deletion and history tools. Use a separate private playbook per profile. Shared playbooks are explicitly configured read-only sources.

The provider currently uses this API's literal search. It does not perform semantic retrieval, upload full conversations, or implement caching and retries. Hermes's local MEMORY.md/USER.md remain active: correcting a mirrored fact remotely does not rewrite the local copy. Correct or remove both copies when needed.

From a CLI release containing the provider:

apb memory setup <private-playbook-guid> --target=hermes
apb memory setup <private-playbook-guid> --target=hermes --apply
hermes memory setup

Set the playbook-scoped AGENTPLAYBOOKS_MEMORY_API_KEY through Hermes's secret setup. The provider is listed in the Hermes plugin catalog; source-checkout and catalog installation are documented in the guide linked above.

Memory Tiers

Memory uses a 3-tier hierarchy inspired by human memory:

TierPurposeDefault Priority

workingActive scratch pad for current tasksHighest
contextualBackground context and recent information (default)Medium
longtermDurable knowledge and completed workLowest

Tier and archive status are independent. An active long-term memory is still searchable; an archived memory is hidden from normal search and context.

Memory Types

  • flat (default) — Simple key-value pairs for facts, preferences, and state
  • hierarchical — Task graphs with parent-child relationships and status tracking

Use Cases

Simple Memory (Flat)

Store facts, preferences, and context:

PUT /api/playbooks/:guid/memory/user_profile
{
  "value": {
    "name": "John Smith",
    "role": "Senior Developer",
    "preferences": { "language": "TypeScript" }
  },
  "tier": "contextual",
  "tags": ["profile", "preferences"]
}

Task Graphs (Hierarchical)

Create multi-step task plans that agent swarms can work on in parallel:

// Via MCP tool: create_task_graph
{
  "plan_key": "refactor-auth",
  "plan_summary": "Refactor authentication to use JWT",
  "tasks": [
    { "key": "research", "description": "Research JWT best practices" },
    { "key": "implement", "description": "Implement JWT middleware", "depends_on": ["research"] },
    { "key": "test", "description": "Write integration tests", "depends_on": ["implement"] }
  ]
}

Each task gets its own memory node with status tracking (pending → running → completed). When all children complete, the parent auto-completes.

Reading Memory

Via API (Search and List)

GET /api/playbooks/:guid/memory
GET /api/playbooks/:guid/memory?tier=working
GET /api/playbooks/:guid/memory?memory_type=hierarchical
GET /api/playbooks/:guid/memory?search=coffee&limit=50&offset=0
GET /api/playbooks/:guid/memory?after=2026-09-01T00:00:00Z&before=2026-09-30T23:59:59Z

Search covers keys, full JSON values, descriptions and summaries. It is case-insensitive and treats search text literally, including % and _. Tag filters match any requested tag. Results default to the latest 100 current, non-archived entries, ordered by memory_at descending; limit accepts 1–200 and offset enables pagination. after and before are inclusive bounds on memory_at and require an ISO timestamp with a timezone. REST includes children by default; MCP search_memory defaults to root entries (include_children: true includes children).

The migration adds playbook/time indexes on both storage engines. Standard Postgres heap tables also get trigram search indexes; OrioleDB uses the same literal search semantics with the supported B-tree filters, without changing the existing storage engine.

Via API (Specific Key)

GET /api/playbooks/:guid/memory?key=user_profile

A direct key read returns the current entry even if it is archived. Other searches return an array.

Memory Time

memory_at is the single writable memory timestamp. Supply it when the memory refers to a specific time; omit it when writing a value to use the time of saving. Reads return it, and the editor displays it in local time. Archive-only and administrative changes preserve it. Context such as confidence, source or additional dates belongs in the JSON value if the agent needs it.

{
  "value": { "location": "hallway drawer" },
  "memory_at": "2026-09-11T10:21:00+02:00"
}

Existing entries receive their previous updated_at as memory_at during migration. History starts when the migration is applied; past overwritten values cannot be recovered.

Archive and History

Content edits save the previous version automatically in the same database transaction. Reading a memory or changing only its tier, priority or archive status does not generate a revision. Explicit changes to memory_at do. The entry keeps its identity, and history is linked to it.

GET /api/playbooks/:guid/memory?scope=archived&search=drawer
GET /api/playbooks/:guid/memory?scope=all&search=drawer
GET /api/playbooks/:guid/memory?history_key=user_profile

  • scope=active (default): current, non-archived entries.
  • scope=archived: manually archived entries and previous versions.
  • scope=all: both groups.
  • history_key: previous versions of one entry, identified by its current key. Do not combine with key or id.

Search results include memory_id (the original entry) and history_id (null for a current entry, a revision ID for a previous version). Scope searches are ordered by memory time; per-entry history is ordered by the saved versions' update times. History follows the same playbook read permissions as current memory. Archiving is not a privacy boundary.

Use the editor's Archive, Memory history and Restore this version actions to maintain entries. MCP clients can archive selected keys with archive_memories, inspect versions with get_memory_history, and restore a version by writing its contents and original memory_at to the current key with is_archived: false. This also saves the replaced contents as history. promote_memory restores visibility without replacing the contents.

The management API accepts an archive-only change without resending the value:

PUT /api/manage/playbooks/:id/memory/:key
{ "is_archived": true }

The public key write endpoint and MCP write_memory require value; they also accept is_archived. A permanent delete removes the entry and its history. Use archive to keep both.

Writing Memory

Writing requires an API key with memory:write permission.

Set/Update a Value

PUT /api/playbooks/:guid/memory/:key
Authorization: Bearer apb_live_xxx
Content-Type: application/json

{
  "value": { "name": "Updated Name" },
  "tier": "working",
  "priority": 80,
  "tags": ["important"],
  "summary": "Updated user profile",
  "memory_type": "flat",
  "status": null,
  "parent_key": null,
  "metadata": {}
}

Delete a Value

DELETE /api/playbooks/:guid/memory/:key
Authorization: Bearer apb_live_xxx

MCP Memory Tools

When using AgentPlaybooks via MCP, these tools are available:

ToolDescription

read_memoryRead a specific memory by key
search_memorySearch by text, tags, tier, type, status, memory time and archive scope
get_memory_historyRead the previous versions of one current key
write_memoryWrite a value with optional memory time and archive status; preserve previous contents
delete_memoryPermanently delete a memory entry and its history
create_task_graphCreate a full task plan with subtasks
update_task_statusUpdate task status (auto-completes parent)
consolidate_memoriesCombine related memories into a parent
promote_memoryMove to higher tier or boost priority
get_memory_contextContext-optimized view across tiers
archive_memoriesHide memories from normal search/context and move to longterm tier
get_memory_treeVisualize hierarchical structure

Memory Fields

FieldTypeDefaultDescription

keystringrequiredUnique identifier
valueJSONrequiredStored data
memory_atISO timestamp with timezonesave timeTime represented by this memory; returned and searchable
is_archivedbooleanfalseHidden from default searches and context; retained for explicit reads
tagsstring[][]Categorization tags
descriptionstringnullHuman-readable description
tierworking/contextual/longtermcontextualMemory hierarchy level
priority1-10050Importance ranking
parent_keystringnullParent memory for hierarchy
summarystringnullCompact text summary
memory_typeflat/hierarchicalflatMemory structure type
statuspending/running/completed/failed/blockednullTask status
metadataJSON{}Free-form. Two keys have a defined shape — see below

Episodic memories

metadata is free-form, with two conventions that the tools understand:

  • metadata.episode — { location, task, outcome }: where an episode happened and what came of it. When is the memory's memory_at. get_memory_context filters on location and task, and on after and before for memory_at, which is how an agent recalls what it did somewhere.
  • metadata.recording — a recording of the episode, in the same shape a skill's demonstrations use. Returned with its links resolved.

{
  "key": "2026-08-20-fire-door-B2",
  "summary": "Opened the B2 fire door on the second attempt.",
  "memory_at": "2026-08-20T14:32:00Z",
  "metadata": {
    "episode": {
      "location": "building-B/floor-2",
      "task": "open-fire-door",
      "outcome": "completed"
    },
    "recording": [{ "provider": "youtube", "ref": "dQw4w9WgXcQ", "role": "reference" }]
  }
}

Keep the written summary either way. It is what the full-text index searches and what a text-only agent reads; the recording is a pointer beside it, never a replacement. See Demonstrations.

Best Practices

  • Use tiers intentionally — working for active tasks, longterm for durable knowledge; archive status controls visibility
  • Keep keys descriptive — user_profile vs data1
  • Use tags for cross-cutting search — ["auth", "security"]
  • Consolidate when context grows — Use consolidate_memories to merge
  • Set summaries — Enables efficient get_memory_context views
  • Use task graphs for complex work — create_task_graph manages the structure

Security

  • Memory is public read by default for public playbooks
  • Memory is private for non-public playbooks
  • Write operations always require API key
  • API keys are hashed before storage
  • Keys can have granular permissions
Edit this page on GitHub
On this page
Hermes native memory providerMemory TiersMemory TypesUse CasesSimple Memory (Flat)Task Graphs (Hierarchical)Reading MemoryVia API (Search and List)Via API (Specific Key)Memory TimeArchive and HistoryWriting MemorySet/Update a ValueDelete a ValueMCP Memory ToolsMemory FieldsEpisodic memoriesBest PracticesSecurity