<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: OKAFOR KOSISOCHUKWU</title>
    <description>The latest articles on DEV Community by OKAFOR KOSISOCHUKWU (@okafor_kosisochukwu_f3e57).</description>
    <link>https://dev.to/okafor_kosisochukwu_f3e57</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2928518%2Fb63b4d18-88ed-4268-b942-0c2fc7a80a83.jpg</url>
      <title>DEV Community: OKAFOR KOSISOCHUKWU</title>
      <link>https://dev.to/okafor_kosisochukwu_f3e57</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/okafor_kosisochukwu_f3e57"/>
    <language>en</language>
    <item>
      <title>What it took to give an AI agent a memory it could delete</title>
      <dc:creator>OKAFOR KOSISOCHUKWU</dc:creator>
      <pubDate>Fri, 30 Oct 2026 19:49:32 +0000</pubDate>
      <link>https://dev.to/okafor_kosisochukwu_f3e57/what-it-took-to-give-an-ai-agent-a-memory-it-could-delete-35i</link>
      <guid>https://dev.to/okafor_kosisochukwu_f3e57/what-it-took-to-give-an-ai-agent-a-memory-it-could-delete-35i</guid>
      <description>&lt;p&gt;In Nigerian towns, hiring a tradesperson runs on word of mouth. You ask in a group chat for someone who can fix a breaker or clear a blocked drain, and you get a phone number. If the person takes your money and stops answering, you have no recourse and no record that it happened.&lt;/p&gt;

&lt;p&gt;Gigr is a marketplace that replaces that with something accountable. Every user gets a wallet on signup. Money sits in an escrow contract and is released when work is confirmed, in milestones when a job has stages. On completion the artisan receives a vouch, which is a compressed on-chain certificate proving a settled job actually happened. A vouch cannot be bought or faked because it can only be minted through an escrow transaction that moved real money.&lt;/p&gt;

&lt;p&gt;On top of that sits Gigidy, an assistant you talk to instead of filling in forms. Say what you need and it finds providers, drafts the request, and holds your budget.&lt;/p&gt;

&lt;p&gt;This post is about the problem we hit building the assistant, and what we had to do to fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The assistant could not remember anything
&lt;/h2&gt;

&lt;p&gt;The first version was stateless. Each conversation started from nothing, so the agent asked again for a budget the user had given yesterday and a neighbourhood they had already named. Worse, it could not learn from outcomes. A user who hired someone who did poor work got no benefit from that experience in the next job.&lt;/p&gt;

&lt;p&gt;We had been storing a small amount of this in a JSON column on the user row. That is not memory. It is a settings table. It cannot be searched by meaning, it cannot hold a hundred observations without becoming a mess, and it belongs to our database rather than to the person.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we moved it to
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/MystenLabs/walrus-memory" rel="noopener noreferrer"&gt;Walrus Memory&lt;/a&gt; is a memory service built on &lt;a href="https://walrus.xyz" rel="noopener noreferrer"&gt;Walrus&lt;/a&gt;, which stores data as blobs on the &lt;a href="https://sui.io" rel="noopener noreferrer"&gt;Sui blockchain&lt;/a&gt;. Each user gets their own namespace inside our account, so one person's memories are never recalled against another's.&lt;/p&gt;

&lt;p&gt;The agent now writes facts about a user there and recalls them by meaning rather than by key. When someone asks for a plumber in Akoka with a budget of fifteen thousand naira, the search returns sentences about their past jobs and their area rather than requiring us to know in advance which field to look up.&lt;/p&gt;

&lt;p&gt;Two decisions shaped how this works. First, facts are produced by templates over real database rows, never generated by a language model. A completed job produces a sentence because the job has a category, a price and a year. If the row is missing a field, no fact is written. A guessed memory is worse than no memory, because the user has no way to know it was guessed.&lt;/p&gt;

&lt;p&gt;Second, memory grows with use. Early in development we backfilled existing history, and the facts we have now are a snapshot rather than a fixed number. The number changes every time someone completes a job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deletion on a store that does not delete
&lt;/h2&gt;

&lt;p&gt;Walrus is immutable by design. A blob is an object on Sui, and you cannot edit a field inside it. When we started, the Python SDK had no delete method at all, so we filed &lt;a href="https://github.com/MystenLabs/walrus-memory/issues/1061" rel="noopener noreferrer"&gt;an issue describing it&lt;/a&gt;. Mysten reproduced it and shipped &lt;code&gt;forget&lt;/code&gt; in &lt;a href="https://pypi.org/project/memwal/0.2.2/" rel="noopener noreferrer"&gt;version 0.2.2 of the SDK&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That method deletes a whole namespace, which is not what users want. A user who wants one memory removed, or one fact corrected, is asking for something the storage model does not offer.&lt;/p&gt;

&lt;p&gt;We built it on top of what exists. To delete one fact, the service reads every memory in the namespace, works out what should remain, deletes the namespace, and writes the remainder back. The dangerous moment is the gap between the delete and the rewrite. If the process dies there, the user has lost everything.&lt;/p&gt;

&lt;p&gt;Before deleting anything, we write the exact list of what must be restored to a journal file on disk, using an atomic rename so a half-written file cannot be read. Every later operation finishes any pending restore before doing its own work. There is a test that kills the process in the middle of the rewrite and proves a new instance repairs the namespace on the next call. You can read that test at &lt;a href="https://github.com/0GigR/GigR/blob/main/backend/tests/unit/test_memory_reconcile.py" rel="noopener noreferrer"&gt;test_memory_reconcile.py&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The interface does not pretend this is free. The confirmation tells the user that the memories they keep will be stored again with today's date, because that is what actually happens when you rebuild a namespace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the chain through Tatum
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://tatum.io" rel="noopener noreferrer"&gt;Tatum&lt;/a&gt; is a blockchain infrastructure provider, and we use it in three places across two chains.&lt;/p&gt;

&lt;p&gt;On Solana, account and blockhash reads go through Tatum's RPC gateway. Each read has a short timeout and one fallback if it does not answer. Writes never go through Tatum. Routing a transaction through any third party is how you break escrow, and we had already learned that the hard way.&lt;/p&gt;

&lt;p&gt;On Sui, verification reads the Walrus blob object through Tatum's Sui gateway and reports its certified epoch. This one is gRPC rather than JSON-RPC, because Tatum's Sui JSON-RPC endpoint does not exist, and &lt;a href="https://docs.sui.io/develop/accessing-data/json-rpc-migration" rel="noopener noreferrer"&gt;Sui itself has deprecated public JSON-RPC&lt;/a&gt; in favour of gRPC and GraphQL.&lt;/p&gt;

&lt;p&gt;The particular blob we verified reads as object type &lt;code&gt;0xfdc88f7d7cf30afab2f82e8380d11ee8f70efb90e863d1de8616fae1bb09ea77::blob::Blob&lt;/code&gt; at version 810023862, which you can open on a &lt;a href="https://suivision.xyz/object/0x0000003c64bf4bda578d911acb4f93f470a89f66d9fd8993fdb032cf007b1c93" rel="noopener noreferrer"&gt;Sui explorer&lt;/a&gt; rather than taking our word for it.&lt;/p&gt;

&lt;p&gt;The third use is verifying payments, which is the last section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Selling to software
&lt;/h2&gt;

&lt;p&gt;The marketplace has a second kind of customer that is not a person. An AI assistant working for someone else may want to know whether a provider is reliable before recommending them. That assistant has no email address and no card, and it cannot complete a signup form.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://x402.org" rel="noopener noreferrer"&gt;x402&lt;/a&gt; is a payment scheme built on the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402" rel="noopener noreferrer"&gt;HTTP 402 status code&lt;/a&gt;, which was reserved in 1997 for exactly this and almost never used. When a program asks Gigr for a brief on a provider, the endpoint answers with a price instead of a login wall. The program pays in &lt;a href="https://www.circle.com/usdc" rel="noopener noreferrer"&gt;USDC&lt;/a&gt;, a dollar-pegged stablecoin, and retries with the settlement signature. Gigr reads that transaction on chain and checks the token, the recipient, the amount, and that the signature has not been spent before. One payment buys one brief.&lt;/p&gt;

&lt;p&gt;A detail worth stating: &lt;a href="https://raw.githubusercontent.com/coinbase/x402/main/docs/core-concepts/network-and-token-support.mdx" rel="noopener noreferrer"&gt;x402 does not support Sui at this time&lt;/a&gt;. Its supported networks include Solana, but not Sui, so a payment cannot settle on the chain the rest of this system uses. We built it on Solana because that is where it works, and we say so rather than implying otherwise.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we did not get right
&lt;/h2&gt;

&lt;p&gt;The deleted memory is not erased from Walrus. The blobs persist; the rows stop being retrievable. We describe it that way in the interface because claiming otherwise would be a lie a user could not check.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is there to look at
&lt;/h2&gt;

&lt;p&gt;The code is at &lt;a href="https://github.com/0GigR/GigR" rel="noopener noreferrer"&gt;github.com/0GigR/GigR&lt;/a&gt; with 477 backend tests and 295 frontend tests, and CI runs are at &lt;a href="https://github.com/0GigR/GigR/actions" rel="noopener noreferrer"&gt;github.com/0GigR/GigR/actions&lt;/a&gt;. The application is at &lt;a href="https://gigrr.vercel.app" rel="noopener noreferrer"&gt;gigrr.vercel.app&lt;/a&gt;. A walkthrough with the demo video and every component named here is at &lt;a href="https://gigrr.vercel.app/judges" rel="noopener noreferrer"&gt;gigrr.vercel.app/judges&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We filed five issues against Mysten's SDK and relayer while building this, and all five are closed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/MystenLabs/walrus-memory/issues/1061" rel="noopener noreferrer"&gt;Python SDK has no forget or delete method&lt;/a&gt;, shipped as &lt;code&gt;forget&lt;/code&gt; in memwal 0.2.2&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MystenLabs/walrus-memory/issues/1062" rel="noopener noreferrer"&gt;recall() cannot pass scoring weights&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MystenLabs/walrus-memory/issues/1063" rel="noopener noreferrer"&gt;recall() drops the created_at and score fields&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MystenLabs/walrus-memory/issues/1064" rel="noopener noreferrer"&gt;Python SDK defaults server_url to localhost&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/MystenLabs/walrus-memory/issues/1312" rel="noopener noreferrer"&gt;An idempotency key reused under a different namespace returns a 500 instead of a conflict&lt;/a&gt;, fixed in relayer 0.1.0 after they confirmed the cause&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>memwal</category>
      <category>sui</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I gave my assistant a memory that survives the server it runs on</title>
      <dc:creator>OKAFOR KOSISOCHUKWU</dc:creator>
      <pubDate>Mon, 05 Oct 2026 20:03:39 +0000</pubDate>
      <link>https://dev.to/okafor_kosisochukwu_f3e57/i-gave-my-assistant-a-memory-that-survives-the-server-it-runs-on-3aea</link>
      <guid>https://dev.to/okafor_kosisochukwu_f3e57/i-gave-my-assistant-a-memory-that-survives-the-server-it-runs-on-3aea</guid>
      <description>&lt;p&gt;I built an assistant that remembers durable facts about the person using it, and&lt;br&gt;
the interesting part turned out not to be the storing.&lt;/p&gt;

&lt;p&gt;A quick summary of what it is. Cheta is a personal assistant you can reach from&lt;br&gt;
Telegram, a web page, a command line client, or a browser extension, and all four&lt;br&gt;
share one memory space per person. It picks between thirteen tools on its own,&lt;br&gt;
transcribes voice notes, reads PDF, Word, PowerPoint with the speaker notes,&lt;br&gt;
Excel and any plain text or source file, and can crawl a site you point it at.&lt;br&gt;
The browser extension reads the page you are currently on and can act on it.&lt;/p&gt;

&lt;p&gt;Now the failure that taught me the most.&lt;/p&gt;

&lt;p&gt;My first version kept its index in a SQLite file on the server. Every redeploy&lt;br&gt;
erased it, so the assistant told a user with twenty stored facts that it had&lt;br&gt;
nothing about them. The facts were safe the whole time. They live on Walrus&lt;br&gt;
Memory, which stores data as blobs on the Walrus network and is reached through&lt;br&gt;
an HTTP relayer, and the local index was only a cache. I had forgotten that, and&lt;br&gt;
the result was an assistant confidently denying it knew someone it had been&lt;br&gt;
talking to for weeks.&lt;/p&gt;

&lt;p&gt;It now rebuilds its index from a snapshot held in Walrus itself, so a fresh&lt;br&gt;
server recovers what it lost.&lt;/p&gt;

&lt;p&gt;The second lesson was about deciding what to keep. My first consolidation pass&lt;br&gt;
compared raw text, so two notes saying the same thing in different words never&lt;br&gt;
matched and an exact duplicate could be stored twice. Comparing a normalised form&lt;br&gt;
fixed that. A changed preference now retires the older note rather than sitting&lt;br&gt;
beside it, and two facts that conflict get flagged so the assistant can say so&lt;br&gt;
instead of contradicting itself later.&lt;/p&gt;

&lt;p&gt;The third lesson was about latency, and the cause surprised me. Turns were taking&lt;br&gt;
up to forty seconds. It was not the model. The provider was rate limiting, and&lt;br&gt;
the client library's automatic retry was sleeping for up to fifty seconds before&lt;br&gt;
trying again, against a timeout of thirty. Every one of those retries was wasted&lt;br&gt;
work. Turning the SDK retry off and rotating across several API keys took a rate&lt;br&gt;
limited turn from about four seconds of dead waiting to effectively nothing.&lt;/p&gt;

&lt;p&gt;The thing I would tell anyone adding memory to a bot: do not trust your own&lt;br&gt;
summary of whether it works. I compared two answers to the same question, one&lt;br&gt;
with memory switched on and one with it switched off, same model and same minute,&lt;br&gt;
and put them side by side. That comparison caught more problems than any test I&lt;br&gt;
wrote.&lt;/p&gt;

&lt;p&gt;Code and setup instructions are here, and it runs without any paid service:&lt;br&gt;
&lt;a href="https://github.com/Ksschkw/" rel="noopener noreferrer"&gt;https://github.com/Ksschkw/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn0088k0clviuorr2t8uu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn0088k0clviuorr2t8uu.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Longer write-up of the build:&lt;br&gt;
&lt;a href="https://medium.com/@kookafor893/i-gave-my-chatbot-a-memory-that-outlived-its-own-database-449ed737d0ec" rel="noopener noreferrer"&gt;https://medium.com/@kookafor893/i-gave-my-chatbot-a-memory-that-outlived-its-own-database-449ed737d0ec&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
