plungeai-memory
Memory — run data (SharedMemory) vs long-term memory. Two systems, never confuse them
PlungeAI has TWO memory systems with different jobs:
PlungeAI has TWO memory systems with different jobs:
| SharedMemory | Long-term memory | |
|---|---|---|
| Holds | Every task's output within/after a run | What the agent LEARNED about the user and its work |
| Keyed by | (workflow_id, task_id, user) | Per user (or per bot namespace) |
| Lifetime | Per execution, durable for retrieval | Standing, across all runs |
| You read it via | plungeai_get_result, /v1/*/results/… | Automatic recall in runs; plungeai_memory |
| You write it via | Never directly — agents store results | plungeai_memory remember; the agent's own memory tool |
"Fetch what that run produced" is SharedMemory. "Remember that the user prefers weekly summaries" is long-term memory.
SharedMemory — the run-data fabric
Every task result — including single agent calls — is stored under
(workflow_id, task_id) scoped to your user. That is why every execution
acknowledgement returns those ids: they are redemption tickets.
# MCP — final result (full conversation thread for conversational runs)
plungeai_get_result {workflow_id: "exec-…"}
# One step of a fan-out
plungeai_get_result {workflow_id: "exec-…", task_id: "web"}# One API
curl -s https://api.plungeai.com/v1/workflows/results/{workflowId}/{taskId} \
-H "Authorization: Bearer ozk_YOUR_KEY"
curl -s https://api.plungeai.com/v1/agents/results/{workflowId}/{taskId} \
-H "Authorization: Bearer ozk_YOUR_KEY"
# 200 {content, content_type, workflow_id, task_id} | 404 not_readyFacts to rely on:
404 not_ready= still running (or a beat behind) — poll; not an error.- Results are scoped to the executing user — you cannot redeem another user's ids.
- The final result of a run lists its steps (MCP renders them); open any step in
full with its
task_id. Use this to inspect ONE branch of a parallel block instead of re-running. - Within a workflow, data handoff between tasks IS SharedMemory — automatic; never hand-wire results between tasks in YAML.
- A scheduler run id is not a SharedMemory id: go through
plungeai_schedule {action: "runs"}and use its Execution ID column (plungeai-scheduling).
Long-term memory — the agent's durable brain
Per user, three layers of plain markdown:
| Layer | File | What it holds |
|---|---|---|
| L3 | USER.md | The agent's learned model of the user (preferences, context) |
| L2 | MEMORY.md | Curated agent notes shared across this user's missions |
| L1 | episodes.md | Append-only run log: goal + self-reported outcome per mission |
Recall — a frozen snapshot per run
At mission start the platform builds ONE snapshot of the curated stores only —
MEMORY.md + USER.md. episodes.md is never injected (task outcomes go stale; pull
run history explicitly with search_runs or the mission's recall tool). The
snapshot is prepended to the agent's system prompt. Mid-run writes are durable but
do not mutate the running prompt (prompt-prefix stays cache-stable). So: what a
mission "knows" is fixed at launch; what it learns benefits the NEXT run. Two
missions launched together do not see each other's writes.
Writing
- In-loop: the agent's
memorytool (missions write learnings as they work; episodes append automatically at run end). - From outside:
plungeai_memory:
plungeai_memory {action: "recall"} # read the durable store
plungeai_memory {action: "remember", target: "user", # user | memory
operation: "add", # add | replace | remove | read
content: "Prefers weekly summaries, no emojis in reports"}
plungeai_memory {action: "search_runs", query: "competitor analysis"}
plungeai_memory {action: "get_run", run_id: "…"}Use remember whenever the user says "remember this". Note the split: recall
reads the curated stores; the run journal (search_runs/get_run) is separate
and deliberately NOT part of recall — history is queried, not ambient.
Budgets — memory is curated, not a dump
The stores are small on purpose (defaults: MEMORY ~2200 chars, USER ~1375; configurable per user, clamped to 500–20000). Writes beyond the limit force curation — replace/remove stale entries rather than appending forever. Write memory like an engineer writes a runbook: few, load-bearing, current facts. Ten vague notes crowd out the one that matters.
Safety
Every entry is threat-scanned twice: at write (a prompt-injection-looking entry is
rejected) and at snapshot build (a poisoned stored entry is replaced with a
[BLOCKED] placeholder; the raw text stays inspectable by the user). Do not try to
store instructions-to-future-agents phrased as commands — that is exactly the
pattern the scanner exists to stop. Store facts.
Namespaces and special runs
- Bots: policy since 2026-08-23 is to omit
memory_ownerentirely — the first 20 bots were authored with a hardcodedmemory_owner: "<ownerUserId>:bot:<workflowId>"and all had it stripped. Without it, a bot run reads/writes the owner's general memory namespace, shared across all their bots and runs ("check memory" / "store in memory" phrasing still works; prompts are a bit larger since the recall snapshot is built from the whole store). The harness guard (resolveMemoryUser) honors a hardcodedmemory_owneronly when it names the runner's own identity — anything else is rejected with a warning and falls back to the owner's store, so a copied YAML can never read or write another user's memory even if it tries. Full policy and rationale:orchestration/BOT-CREATION.md. (Do not confuse this with a workflow'sfollowup.memory_scope: all_tasks|last_task, which only scopes follow-up chat context.) - Scheduled runs keep the full per-user memory lifecycle — a daily mission
genuinely accumulates. Only system jobs and heartbeat condition checks are
memory-free (heartbeat is a user-facing job type — see
plungeai-scheduling— whose ticks are watch-and-notify checks, not learning runs). - Delegate children never write memory: the
memorytool is stripped from fan-out children by design — one writer per run.
Choosing the right memory move
| Need | Move |
|---|---|
| Output of a run/step | plungeai_get_result (+ task_id) |
| Pass data between workflow steps | Nothing — automatic |
| "Remember X about me/us" | plungeai_memory remember |
| Mission should apply past learnings | Automatic recall — just run it; curate memory if recall is noisy |
| "What did we do about X before?" | plungeai_memory search_runs → get_run |
| Standing company/product context for many runs | Not memory — a background card (plungeai-skills-plugins) |
| Reusable method/knowledge pack | Not memory — a skill, created with plungeai_learn (below) |
The last two rows matter: memory is per-user learned state. Shared, versioned, deliberately-authored context belongs in backgrounds and skills, where injection is explicit and reviewable.
MCP tool: plungeai_memory
Purpose: the user's long-term memory. Two stores, deliberately separate:
the durable memory that recall reads (and remember writes), and the run
journal of past mission runs (search_runs/get_run).
Actions
| Action | Requires | Notes |
|---|---|---|
recall | — | Everything the platform remembers about the user (the same recall missions get automatically). |
remember | content (add/replace) or old_text (remove); neither for read | Durable write. target: user (profile facts) | memory (working knowledge, default). operation: add (default) | replace (needs old_text + content) | remove (needs old_text) | read. recall sees writes immediately. |
search_runs | query | Search the run journal (optional limit, default 10). |
get_run | run_id | One journal entry in full. |
Example: {"user_request": "remember that I prefer summaries under 200 words", "action": "remember", "target": "user", "content": "Prefers summaries under 200 words"}
Returns: recall → the memory document (or "No memory recorded yet.");
remember → "Saved to your user profile/memory ... recall will see it
immediately."; failed writes return the error plus the CURRENT entries so a
replace/remove can be retried with an exact old_text.
Failures & fixes: "content is required for remember" / "query is required
for search_runs" / "run_id is required for get_run" → supply them. A
replace/remove that misses names the current entries — copy old_text
verbatim from that list and retry. Use remember whenever the user says
"remember this" — chat context alone does not persist.
MCP tool: plungeai_learn
Purpose: distill a source into a reusable skill, saved PRIVATE to the
user — future runs can inject it. The source can be a URL, pasted
text/markdown, or a description of what was just accomplished in this chat
(learn-back). Runs the platform's learn agent card, which resolves live at
run time. Async by default.
Parameters
| Param | Type | Notes |
|---|---|---|
action | learn (default) | list | forget | learn: distill source into a skill. list: your learned skills. forget: delete one (name required). |
source | string | Required for action: learn (a URL, pasted text/markdown to distill, or a description of what you just did). Not used by list/forget. |
name | string (kebab-case) | On learn: optional skill id — the agent picks one if omitted. On forget: required (the id from action: list). |
mode | sync | async | Only applies to learn; default async. |
Example (learn): {"user_request": "save what we learned about CNL debugging as a skill", "source": "Distilled findings from this session: ...", "name": "cnl-debugging"}
Example (list): {"user_request": "what skills have I saved?", "action": "list"}
Example (forget): {"user_request": "delete my cnl-debugging skill", "action": "forget", "name": "cnl-debugging"}
Returns:
learn, async (default): "Started distilling<id>(async)..." → pollplungeai_get_workflow_status, fetch withplungeai_get_result(the result names the saved skill).list: a table of your learned skills (- \id` — description`), or "No learned skills yet."forget: "Forgot skill<name>."
Failures & fixes: learn without source → needs_input. forget without
name → "forget needs name (the skill id from action: list)." forget on
an id you don't own, or that doesn't exist → "Not yours to delete" /
"No skill named <name>. Use action: list to see yours." Authentication
required (needs a real user key); unreachable URLs on learn surface in the run
result — retry with pasted text instead.
This is the "learn-back" move: when a session produced real research, offer to
distill it so future runs can inject it as a skill — see plungeai-skills-plugins
for how a saved skill gets declared and injected on later runs.