Korely

Concepts

Agent notices

An agent that tells Korely its name gets a warning, in its next context block, when a fact it read has ended since. Since 1.0.15, on the Cloud and on Self-hosted.

Without this, an agent that read "Giulia lives in Rome" keeps using it after another agent has learned she moved to Milan. With agent notices, Korely remembers which facts each named reader was handed, and tells that reader what changed the next time it asks for its context.

Turn it on: pass reader

Add reader, the reading agent's own name, to GET /v1/context or GET /v1/facts (or to korely_get_context or korely_get_facts over MCP). It is optional and filters nothing: without it, every answer is the one you got in 1.0.14, and Korely keeps nothing about the read. The name follows the rule of agent_id on a write: empty, ., .., a /, a line break or any other control character, or over 255 characters, is a 422 invalid_request.

Terminal window
curl -G https://api.korely.ai/v1/context \
-H "Authorization: Bearer kor_live_..." \
--data-urlencode "query=Where does Giulia live?" \
--data-urlencode "user_id=customer-giulia-4812" \
--data-urlencode "reader=support-bot"

Name the agent, never an end user. If a reader's name is an end user's id, that end user's erasure also takes the marks read under that name.

What you get back

When a fact this reader was handed has ended since, and is not true again in another row, the volatile part of the block opens with a section before the known facts:

## Changed since you last read
- was: Giulia lives in Rome -> now: Giulia lives in Milan (2026-11-10, read from a memory)
- no longer true: Giulia uses the Pro plan (2026-11-10, forgotten)
  • The two forms. was: ... -> now: ... when a fact true now replaced it, down the chain of what replaced it. no longer true: ... when nothing holds now, or when the fact that replaced it is one the key may not read (nothing else of it is told).
  • The day and the cause. The day Korely learned of the change and origin, the action that ended the fact: corrected (PATCH /v1/facts/{id}), forgotten (POST /v1/facts/{id}/forget), stated (a fact written with POST /v1/facts replaced it), or read_from_memory for every other closure: Korely's reading of a memory, and a fact ended in place by a past statement.
  • At most 3 lines in a block, newest first, inside your token_budget. The rest are counted.
  • Told once. A change is told at most once, even to two reads at the same moment, whether or not the agent uses it.
  • On REST, the answer also carries changed, the changes the block shows (with the facts whole), and changed_more, how many more wait. changed_more is null when the changes could not be read this time, under a lock on their records: they wait for the next read. An answer to a read without reader has neither field.
  • A forgotten memory's words are never told. A fact replaced and then forgotten with its memory is told as "a fact whose memory was forgotten" (was is null).

Which changes are told: changes

GET /v1/context and korely_get_context take changes. Left out, every kind of change is told in the memory of an end user or of no id at all. In an agent's memory with no end user (the memory of a team of agents), only the explicit ones are told: corrected, forgotten and stated by a caller. There, the closures Korely read from memories were mostly wrong when measured on our own team's memory (21 of 32 with memory_core 6.6.0, an estimated 4 of 5 with 6.7.0). changes=all tells those too. A change not told is not counted in changed_more.

What Korely keeps

A read with reader of what is true now keeps one row for each fact it handed over: the account, the project, the reader's name, the fact's id, the end user the fact is about, and the time of the first and of the last read. A second read moves the time and adds no row. No text of a fact is kept. Only facts are marked, never memories.

  • How long. 90 days after the last read, then the worker deletes the mark (once an hour). A mark whose change was shown also goes at the next trim.
  • Deleted earlier with its fact, with its project, with the account, and with the erasure of its end user (deleted, not kept under a pseudonym). The erasure also takes the marks whose reader's name is that end user's id, in the project it is made in. DELETE /v1/agents/{agent_id} takes the marks read under that name, and its answer counts them in read_marks_deleted.
  • What is not marked. A read of the past (as_of, known_as_of) or of the history (include_invalidated=true), the dashboard's reads, GET /v1/profile, GET /v1/memories/{id} with its /history, and the memory search.
  • A mark never fails the read. It waits at most 0.2 s for a lock another transaction holds, then gives way and the read is answered all the same.

Known limits

  • Told at the next context block, and only there. Nothing is pushed. GET /v1/facts with reader keeps its marks and tells nothing; there is no route of its own for the changes that wait yet.
  • Lost if the answer is lost. A block reserves the changes it shows before it is written. If the answer then fails (the response is lost on its way), those changes are not told again.
  • The section takes room after the known facts, from the memories' share: at most half of it, and never what the first memory needs to be shown. A block that its known facts fill, with no memory, has no room for changes: they wait for a larger token_budget. In a block with memories, a small budget can tell nothing where a smaller one did.
  • Newest first. It looks at the 20 newest changes (up to 200 when there is room). A writer that changes three or more of the reader's facts before every read keeps an older change out for as long as it goes on; it is counted in changed_more. A change too long for the room is written short, and one that does not fit even short waits for a block with room.
  • A notice is as right as the closure it tells. A past statement that ends a fact in place reads read_from_memory whoever said it, so it is never among the explicit ones.
  • The reader's name is not checked against the key. Any caller of the project can read under another agent's name, leave marks under it and be told the changes waiting for that agent, which is then not told them. It never sees more than its own key does.
  • One name, several namespaces. It is the reader's whatever the block's agent_id: an agent that reads several namespaces under one name is told the changes of all of them. To keep them apart, read under two names.
  • A fact true again in another row (given back after a deletion, or told again) is no change while that row is true. If that row ends later, a reader handed both can be told twice in one block.
  • A change of a fact written after extraction is known only once extraction has run.
  • No ceiling on the readers of a project. Each name keeps its own row for every fact it read, until the marks are 90 days old. A ceiling is not decided yet.
  • Cost. Measured on a laptop, with no network and no model: a read of 40 facts with reader took 1 to 2 ms more at the median. Not measured on a server, nor with more than one request at a time.

Related