Write & recall
Writes and reads go through workflows. Discover live schemas at reference.orkestia.dev or via MCP (get_workflow_schema). Inputs are UUIDs (agent_config_uuid, session_uuid, organization_uuid).
Workflows
| Type | When | Result |
|---|---|---|
agents.memory-load | Explicit recall (session launch uses the same strategies) | Serialized memory, count, optional pack id |
agents.memory-distill | After session complete/fail if memory_write_enabled | Starts agents.memory-save, or a skip reason |
agents.memory-save | Distill, or runner elective | { saved, skipped, reinforced } |
agents.memory-prune | Over cap, or POST /api/app/agents/memory/prune | { deleted, remaining } |
Distill is fire-and-forget after the session ends. A failed distill does not fail the session.
Elective write (runner)
POST /api/agents/sessions/{session_ref}/memory-save
Authorization: Agent <session-token>
Body: { "entries": [ { "text": "…", "importance": 0.7 } ] } or { "content": "…" }. Accepted 202 — same agents.memory-save path. Distill is what actually fills the pool unless the runner calls this.
Distill gate
agents.memory-distill skips unless:
- The config has
memory_write_enabled - The session ended
successorfailed(failures are distilled on purpose) - The session ran at least two steps
It then summarizes the tool trace (at most 25 calls), asks the org's model for at most 5 lessons, and saves them. [] is normal — most sessions teach nothing.
Fingerprint
normalize(text) = lowercase → collapse whitespace → strip " .;:,-"
fingerprint = sha256(utf-8(normalize(text)))
On save, a matching fingerprint on the same agent config:
- keeps the higher importance
- increments the reinforcement count
- does not create a second memory
This is exact-text dedupe after normalize, not embedding similarity. Near-duplicates with different wording stay separate.
importance < 0.1 → skipped.
Recall strategies
Config field memory_strategy (default last_k).
| Strategy | Uses the task as a cue? | What you get |
|---|---|---|
last_k | no | Newest top_k (default 10) |
importance | no | Highest importance, then newest |
full | no | Newest, capped at 1000 |
pack | yes | Ranked and budgeted (below) |
semantic | — | Not available yet — falls back to last_k |
Org or config memory_enabled off → empty context, no recall.
Ordered strategies join texts with ---. pack prefixes ## Recalled memory (ranked for this task).
Pack score (strategy pack)
Candidates: up to 250 newest and 250 highest-importance memories this caller is allowed to see.
score = 0.60·relevance + 0.25·importance + 0.15·recency
relevance = token overlap with the task, damped by entry length
recency = half-life of 30 days
Then top_k, skip duplicate normalized text, fit a 20 000 character budget. Empty cue → importance + recency only.
Recalled memories bump their access count. The recall is recorded (strategy, cue, pack id). When the session ends, that recall is labelled success or failed; an explicit session rating replaces the terminal label.
Who sees whose memory
Memory is scoped to the agent config, then to a principal:
| Caller | Sees |
|---|---|
| No principal (shared config memory) | Only unscoped memories |
| End-user | Only that user's memories — never the shared pool |
| Actor (staff / member) | Their memories plus the shared pool |
An end-user session that cannot resolve who the user is runs without memory. App end-users share one agent config; serving shared memories to an unidentified user would leak.
HTTP (org session)
Base: https://api.orkestia.dev/api/app/agents/memory
| Method | Path | Notes |
|---|---|---|
GET | ?agent_config_uuid=&min_importance=&page=&per_page= | Highest importance first. Default 20 per page |
DELETE | /{entry_id} | Org-scoped |
POST | /prune | { agent_config_uuid, max_entries } → 202 + workflow_id |
Prune drops lowest importance, then oldest, until under the cap.
Live tail: Field & feed.
