Self-healing agents are just a loop you forgot to build — LoreKit blog
All postsli]:my-2 [&_ol]:my-4 [&_ol]:list-decimal [&_ol]:pl-5 [&_ol>li]:my-2 [&_code]:rounded [&_code]:bg-[var(--color-bg-elevated)] [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.85em] [&_pre]:my-5 [&_pre]:overflow-x-auto [&_pre]:rounded-lg [&_pre]:border [&_pre]:border-[var(--color-border)] [&_pre]:bg-[var(--color-bg-elevated)] [&_pre]:p-4 [&_pre]:font-mono [&_pre]:text-xs [&_pre]:leading-relaxed [&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_pre_code]:text-xs [&_blockquote]:my-5 [&_blockquote]:border-l-4 [&_blockquote]:border-[var(--color-accent)] [&_blockquote]:pl-4 [&_blockquote]:text-[var(--color-content-secondary)] [&_blockquote]:italic [&_table]:my-5 [&_table]:w-full [&_table]:border-collapse [&_table]:text-sm [&_th]:border [&_th]:border-[var(--color-border)] [&_th]:bg-[var(--color-bg-elevated)] [&_th]:px-3 [&_th]:py-2 [&_th]:text-left [&_td]:border [&_td]:border-[var(--color-border)] [&_td]:px-3 [&_td]:py-2 [&_td]:align-top [&_hr]:my-10 [&_hr]:border-[var(--color-border)]">Your agent has amnesia
Here's a bug you've already paid for, probably more than once.
Your agent runs a Supabase query, gets back 200 OK and an empty array, and confidently concludes the row doesn't exist. It doesn't. RLS silently filtered it out. The agent burns ten minutes going down the wrong path, you nudge it, it recovers.
Next session? Same query. Same empty array. Same wrong conclusion. Same ten minutes.
It's not a dumb agent. It's an agent with anterograde amnesia — every session starts from zero, so every lesson dies at the end of the conversation that earned it. You're not paying for one mistake. You're paying rent on it, forever.
"Self-healing agents" and "self-improving agents" are the shiny words for fixing this. And they get pitched like an ML problem — fine-tune the model, train on your traces, wait a week, hope.
It's not an ML problem. It's a loop you forgot to build.
Self-healing isn't magic — it's read → fail → write
Strip the mystique away and a self-improving agent is three verbs:
Read what you learned last time, at the start of the run.
Fail — hit friction, a stuck loop, a gate that should've caught something.
Write the lesson down so the next run reads it.
That's the whole thing. No new model call. No fine-tune. No embeddings pipeline you have to babysit.
LoreKit runs that loop through deterministic lifecycle hooks — the same SessionStart / tool-result / Stop events your coding agent already fires. On SessionStart it injects the lessons relevant to where you are. When a tool call fails it looks up the lessons that match the failure and drops them in — before the agent retries. On Stop it nudges a short retrospective.
The important detail — the one that makes this shippable instead of scary — is that every hook exits 0, best-effort, and never blocks the host . Memory is down? No matches? Store not connected? The hook shrugs and the agent runs exactly as it did before. Self-healing that can't break the patient.
The failure is the trigger
Most memory systems write on success. You finish something, you save a summary, you move on. Fine — but success is the boring case. Nobody rediscovers how they shipped the thing that worked.
The interesting move is writing on failure . That's where the reusable knowledge actually lives.
So the trigger is a failing tool call. LoreKit reads the tool result and asks a deliberately conservative question — did this actually fail?
// packages/cli/src/core/failure.mjs — framework-agnostic, conservative on purpose<br>export function isFailure(toolName, response) {<br>if (!response || typeof response !== 'object') return false;<br>if (response.is_error === true || response.isError === true) return true;<br>if (response.interrupted === true) return false; // user abort, not a lesson<br>for (const field of ['exit_code', 'exitCode', 'code', 'returnCode']) {<br>const n = num(response[field]);<br>if (n !== null) return n !== 0;<br>if (typeof response.status === 'string') return /^(error|fail(ed)?)$/i.test(response.status);<br>return false;<br>Conservative on purpose — a false "you failed" nags the agent for no reason, so the bar to trigger is high. When it does trigger, LoreKit distils search terms from the tool name and the error text, pulls the matching prior lessons (capped at 3, so it's a hint, not a wall of text), and injects them right there. Then it nudges: write down what you just learned.
The agent that hit the RLS empty-array trap once writes this:
memory.write {<br>scope: "repo::mthines/lorekit",<br>key: "aw-lessons::supabase-rls-debugging",<br>value: "RLS failures return 200 with an empty array, not a 4xx.\nAlways check .data.length before concluding the query returned no rows.",<br>tags: ["loop::aw-lessons", "source::stuck-loop"],<br>trigger: "stuck-loop"<br>Next time a Supabase call comes back empty, that lesson is already on the table before the agent talks itself into the wrong answer. The ten minutes never...