<?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: liuyuyan6100</title>
    <description>The latest articles on DEV Community by liuyuyan6100 (@liuyuyan6100).</description>
    <link>https://dev.to/liuyuyan6100</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%2F4090729%2Ffb00ff96-ae18-4967-8745-5f69e244f6f0.png</url>
      <title>DEV Community: liuyuyan6100</title>
      <link>https://dev.to/liuyuyan6100</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/liuyuyan6100"/>
    <language>en</language>
    <item>
      <title>06 — Chat Works. Does the Agent Actually Retrieve Memory?</title>
      <dc:creator>liuyuyan6100</dc:creator>
      <pubDate>Tue, 22 Sep 2026 00:03:16 +0000</pubDate>
      <link>https://dev.to/liuyuyan6100/06-chat-works-does-the-agent-actually-retrieve-memory-21hf</link>
      <guid>https://dev.to/liuyuyan6100/06-chat-works-does-the-agent-actually-retrieve-memory-21hf</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2F8tmqJgfFV7dU7O19G8RFHLQricMzwN7N2a9ztjah6Kq5fSnabuO2EBZXfREzQcvvp57dl7p25V7TicxYumvkVj2LR3P9l6ASFMn0iarrpjgDuw%2F0%3Ffrom%3Dappmsg" 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%2Fmmbiz.qpic.cn%2Fmmbiz_png%2F8tmqJgfFV7dU7O19G8RFHLQricMzwN7N2a9ztjah6Kq5fSnabuO2EBZXfREzQcvvp57dl7p25V7TicxYumvkVj2LR3P9l6ASFMn0iarrpjgDuw%2F0%3Ffrom%3Dappmsg" alt="Illustrated cover: a working chat does not prove that an agent can retrieve memory" width="2400" height="1020"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The images in this article are explanatory diagrams with Chinese labels, not screenshots of test results. Each caption summarizes the diagram in English.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I wanted to make projects easier to resume across conversations and agents. Before testing whether the next agent could pick up where the previous one stopped, I checked something more basic: could it actively retrieve memory?&lt;/p&gt;

&lt;p&gt;In the Docker deployment I was investigating, retrieval requests timed out while ordinary conversation continued. That combination was easy to misread. The assistant still answered, and memory content appeared in its context. Neither observation proved that its retrieval tools worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  A successful conversation validates only part of the system
&lt;/h2&gt;

&lt;p&gt;Chat and tool execution involve different requests. A working conversation showed that the client could communicate with the gateway. It did not establish that the client's tool execution environment could reach an address supplied inside a tool prompt.&lt;/p&gt;

&lt;p&gt;The responsibility split matters: &lt;strong&gt;the model chooses a tool; the agent client's execution environment sends the request.&lt;/strong&gt; A sensible tool choice can therefore produce a network failure. Debugging the model's decision alone will not reveal where that request actually went.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmbiz.qpic.cn%2Fsz_mmbiz_png%2F8tmqJgfFV7cRiaxujRM7PUY3rDmOxicbu8ticlKHqpEeicicKscAcqupQHR8pATxA15wZdYwd56slH01n9icnMktiaBDJicrdAUwsmFlXBXODoKv70c%2F0%3Ffrom%3Dappmsg" 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%2Fmmbiz.qpic.cn%2Fsz_mmbiz_png%2F8tmqJgfFV7cRiaxujRM7PUY3rDmOxicbu8ticlKHqpEeicicKscAcqupQHR8pATxA15wZdYwd56slH01n9icnMktiaBDJicrdAUwsmFlXBXODoKv70c%2F0%3Ffrom%3Dappmsg" alt="Simplified architecture showing the agent client, gateway, memory service, and tool request paths" width="1800" height="2000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1: A simplified architecture diagram, rearranged from an existing Archify diagram. The chat connection and client-executed tool requests need separate checks.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory in context is not proof of retrieval
&lt;/h2&gt;

&lt;p&gt;The proxy has two paths that share configuration and injection machinery but can succeed or fail separately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Passive injection&lt;/strong&gt; reads memory content or indexes on the server side and includes them in the context. This can include L3 core memory, L2 scenario indexes, persona information, and a skill list. In this deployment, passive injection continued to work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Active retrieval&lt;/strong&gt; supplies endpoints through tool prompts. The client then requests addresses such as &lt;code&gt;${base}/memory-bridge/v3&lt;/code&gt; and &lt;code&gt;${base}/skill-bridge/v3/skill&lt;/code&gt;. This depends on the client being able to reach the generated address.&lt;/p&gt;

&lt;p&gt;That explains how context could contain memory while retrieval failed. The server's read path worked; the URL handed to the client's tool executor did not.&lt;/p&gt;

&lt;p&gt;The failure was also easy to miss in the conversation. Tool requests timed out or produced no HTTP response, recorded as &lt;code&gt;000&lt;/code&gt;. The assistant continued with file reads and local searches, without presenting a visible tool error to the user. I did not measure how much this fallback affected answer quality.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmbiz.qpic.cn%2Fsz_mmbiz_png%2F8tmqJgfFV7enTSYcfgB66hk4oYCOQH1IGzKsZC4yPP9G9barAZiaTa3kq0MlVZbZBr5QurG0cSviaL0aWyxPby8uWFIrIyxSPyh6YnE2EgSeE%2F0%3Ffrom%3Dappmsg" 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%2Fmmbiz.qpic.cn%2Fsz_mmbiz_png%2F8tmqJgfFV7enTSYcfgB66hk4oYCOQH1IGzKsZC4yPP9G9barAZiaTa3kq0MlVZbZBr5QurG0cSviaL0aWyxPby8uWFIrIyxSPyh6YnE2EgSeE%2F0%3Ffrom%3Dappmsg" alt="Two paths: server-side passive memory injection and client-side active retrieval" width="1800" height="2540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2: Passive injection can supply context even when active retrieval fails. A plausible answer is insufficient evidence that the retrieval path succeeded.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The missing setting was the injected gateway address
&lt;/h2&gt;

&lt;p&gt;Following the failing path led to the URL-generation logic.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;feat/server_team&lt;/code&gt; source I checked on September 19, 2026, &lt;code&gt;injection.externalGatewayUrl&lt;/code&gt; takes precedence when configured. If it is absent and the configured host is &lt;code&gt;0.0.0.0&lt;/code&gt; or &lt;code&gt;127.0.0.1&lt;/code&gt;, the fallback selects the first non-internal IPv4 address and builds an &lt;code&gt;http://&amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;&lt;/code&gt; base URL. It also emits a warning.&lt;/p&gt;

&lt;p&gt;My Docker deployment had taken that fallback. The selected address belonged to the deployment's container network, while the client's tool execution environment was outside that network. Retrieval requests consequently timed out or received no HTTP response.&lt;/p&gt;

&lt;p&gt;The missing piece was an existing configuration field. The source already documented explicit configuration for multi-node deployments; this was a deployment mistake, not a newly discovered upstream vulnerability.&lt;/p&gt;

&lt;p&gt;Two useful starting points are the startup warning and the address actually injected into the tool prompt. Comparing that address with the client's gateway configuration can expose a discrepancy. A discrepancy still needs investigation: different addresses may be intentional, so test from the environment that executes the tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interpret the probe before changing configuration
&lt;/h2&gt;

&lt;p&gt;Two probe results deserve careful reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;401&lt;/code&gt; means an HTTP service responded and requires authentication.&lt;/strong&gt; It does not demonstrate a successful memory operation, or establish by itself that this is the intended service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;000&lt;/code&gt; means the probe received no HTTP response.&lt;/strong&gt; It is not an HTTP status code identifying one cause. Timeouts, DNS resolution, and routing still need examination.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the gateway, the proposed configuration change is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;injection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;externalGatewayUrl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gateway.example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The domain above is a placeholder. The real entry point must be reachable from the tool execution environment, and the reverse proxy must forward the relevant bridge paths. The source handles a trailing slash; reachability and routing are the consequential checks.&lt;/p&gt;

&lt;p&gt;I also need to change the persistent configuration source. Editing a file regenerated by a startup script would make the correction disappear on the next run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knowledge resources have their own URLs
&lt;/h2&gt;

&lt;p&gt;Another failure in the same investigation had a separate cause. Tools for resources such as a wiki or code graph use URLs stored when those resources are registered. The injector copies those resource URLs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Changing &lt;code&gt;externalGatewayUrl&lt;/code&gt; does not update independently registered knowledge-resource addresses.&lt;/strong&gt; Each one needs its own check.&lt;/p&gt;

&lt;p&gt;The example in my notes used &lt;code&gt;host.docker.internal&lt;/code&gt;. That name is a Docker Desktop convention; in a Linux environment without corresponding configuration, it may not resolve. The useful question is whether the name resolves and connects from this particular tool execution environment.&lt;/p&gt;

&lt;p&gt;These failures pointed to injected addresses and name resolution. They did not establish that memory storage was damaged.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmbiz.qpic.cn%2Fsz_mmbiz_png%2F8tmqJgfFV7ecA6ZQkV1eJP4YGBqNAQJIGXib0ouSnRfkyC8SU0XTU4KAgM6LkFeSQibg0N6RUKgDUyhXny8jkPn23excHxYeAKlyxnELMbypI%2F0%3Ffrom%3Dappmsg" 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%2Fmmbiz.qpic.cn%2Fsz_mmbiz_png%2F8tmqJgfFV7ecA6ZQkV1eJP4YGBqNAQJIGXib0ouSnRfkyC8SU0XTU4KAgM6LkFeSQibg0N6RUKgDUyhXny8jkPn23excHxYeAKlyxnELMbypI%2F0%3Ffrom%3Dappmsg" alt="Separate URL sources for gateway bridge tools and registered knowledge resources" width="1800" height="2300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3: Memory and skill bridge addresses use the gateway base URL. Knowledge tools use their individually registered resource URLs. Fixing one source does not fix the other.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The verification I still need to finish
&lt;/h2&gt;

&lt;p&gt;I have the diagnosis and a proposed correction. The post-fix checks are still pending. My next steps are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Probe from the actual tool execution environment.&lt;/strong&gt; Confirm that the injected address is reachable and routes to the intended gateway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update persistent configuration and start a fresh conversation.&lt;/strong&gt; Inspect the newly injected tool addresses and confirm they no longer come from the unwanted fallback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make an authorized memory-tool request.&lt;/strong&gt; Verify application-level success and content from the expected source, rather than inferring success from a later answer assembled through local search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test each registered knowledge-resource URL independently.&lt;/strong&gt; Check resolution and connectivity, then retrieve an expected item and confirm its source.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmbiz.qpic.cn%2Fsz_mmbiz_png%2F8tmqJgfFV7fYic1PdicCxNicdvbHwkYqD0BDjvkuPXWicWGp5fiaz78YKY6QINDtQBkN2ZJa2UpjFiaGHtmHO43mqVxXGrsVNr19peDwzicbkBhKzE%2F0%3Ffrom%3Dappmsg" 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%2Fmmbiz.qpic.cn%2Fsz_mmbiz_png%2F8tmqJgfFV7fYic1PdicCxNicdvbHwkYqD0BDjvkuPXWicWGp5fiaz78YKY6QINDtQBkN2ZJa2UpjFiaGHtmHO43mqVxXGrsVNr19peDwzicbkBhKzE%2F0%3Ffrom%3Dappmsg" alt="Four-step checklist for verifying gateway reachability, refreshed injection, memory retrieval, and knowledge-resource retrieval" width="1800" height="2480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 4: My planned post-fix verification checklist. These steps have not yet been completed.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Only after those checks will I evaluate recall quality and cross-agent handoffs. Otherwise, I could spend time judging “memory” while the assistant is quietly using a different source. First I need to establish that retrieval actually happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/feat/server_team/MemoryProxy/src/injection/index.ts" rel="noopener noreferrer"&gt;Injection address generation in TencentDB-Agent-Memory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/feat/server_team/MemoryProxy/config.example.yaml" rel="noopener noreferrer"&gt;Configuration example, including externalGatewayUrl&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My blog: &lt;a href="https://blog.aiclawonline.website/" rel="noopener noreferrer"&gt;Aiclaw — AI learning notes&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>debugging</category>
      <category>docker</category>
      <category>architecture</category>
    </item>
    <item>
      <title>05 - After Wiring Up TencentDB Agent Memory, Why I'm Still Reading About Mnemosyne OS</title>
      <dc:creator>liuyuyan6100</dc:creator>
      <pubDate>Tue, 22 Sep 2026 00:03:02 +0000</pubDate>
      <link>https://dev.to/liuyuyan6100/05-after-wiring-up-tencentdb-agent-memory-why-im-still-reading-about-mnemosyne-os-11a2</link>
      <guid>https://dev.to/liuyuyan6100/05-after-wiring-up-tencentdb-agent-memory-why-im-still-reading-about-mnemosyne-os-11a2</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmbiz.qpic.cn%2Fsz_mmbiz_png%2F8tmqJgfFV7e7Mkcv8KbKPwx2fDdOf9JVTjHzIW3WpnYxAicWRvb8ZEwlgd6jIuj6jwYKyG7LQr2DmGiaE4OcyRXvwXx7QRaRN7brrNCy523vs%2F0%3Ffrom%3Dappmsg" 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%2Fmmbiz.qpic.cn%2Fsz_mmbiz_png%2F8tmqJgfFV7e7Mkcv8KbKPwx2fDdOf9JVTjHzIW3WpnYxAicWRvb8ZEwlgd6jIuj6jwYKyG7LQr2DmGiaE4OcyRXvwXx7QRaRN7brrNCy523vs%2F0%3Ffrom%3Dappmsg" alt="Cover: a continuous paper path linking past project records to the next step, representing handing work over to an AI assistant" width="759" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover: accumulated project experience leading into the next handoff. AI-generated concept illustration.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A note before we start: all five diagrams below carry Chinese labels; the English captions summarize what each one shows.&lt;/p&gt;

&lt;h2&gt;
  
  
  The draft that made me restate the question
&lt;/h2&gt;

&lt;p&gt;In the previous post I finished digging into DSH's plugin loading — DSH being the agent environment I've been working in across this series. Installing a plugin requires a restart; changing user configuration hot-reloads. They touch different layers.&lt;/p&gt;

&lt;p&gt;That post left two threads open: build a plugin myself, and go deeper on memory integration. I said I'd write whichever produced something worth telling first. This time it's memory — but the trigger was a draft that came back wrong.&lt;/p&gt;

&lt;p&gt;I handed my reference material to WorkBuddy and asked for a draft combining my recent study of TencentDB Agent Memory with ideas from Mnemosyne OS. It came back fast. The concepts were all there. There was even a table. It read like the real thing.&lt;/p&gt;

&lt;p&gt;The further I got, the more something felt off: &lt;strong&gt;it knew the topic "memory systems," but it didn't pick up why I had arrived there.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The research from posts 01–04 receded into background. The structure of the reference article became the spine instead. Somebody else's experience got rewritten in the first person as "mine."&lt;/p&gt;

&lt;p&gt;I wanted post 05. What I got looked like a brand-new opening with little connection to what came before.&lt;/p&gt;

&lt;p&gt;That pushed me to restate the question: after all this time spent on agent memory, what exactly do I want the AI to remember for me?&lt;/p&gt;

&lt;h2&gt;
  
  
  From "the tool can be called" to "the project can be continued"
&lt;/h2&gt;

&lt;p&gt;Looking back, the first four posts were all heading the same way.&lt;/p&gt;

&lt;p&gt;In 01 I studied TencentDB Agent Memory: how conversations settle into memory, how docs and code become reusable assets. In 02 I went to DSH's source to understand how an agent environment carrying those capabilities is organized.&lt;/p&gt;

&lt;p&gt;In 03 I connected the two — proxies, addresses, tool calls, debugging layer by layer until the integration ran. In 04 I went further into plugins: how capability gets installed, how config takes effect, why some updates force a restart.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2F8tmqJgfFV7d2vjmibV11w4NvOXsfKYXanLalL3UT4ZKSPC34CSOwOmIeg0V7hLs3eT0nqKeMvJyOXY4zS3dyeVgicQcSBLtvyPh8P6gJL7CX4%2F0%3Ffrom%3Dappmsg" 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%2Fmmbiz.qpic.cn%2Fmmbiz_png%2F8tmqJgfFV7d2vjmibV11w4NvOXsfKYXanLalL3UT4ZKSPC34CSOwOmIeg0V7hLs3eT0nqKeMvJyOXY4zS3dyeVgicQcSBLtvyPh8P6gJL7CX4%2F0%3Ffrom%3Dappmsg" alt="Series roadmap: 01 Tencent memory, 02 DSH architecture, 03 memory integration, 04 plugins and hot reload, leading to 05 project continuity" width="1440" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1: This post continues the memory thread left open in 04 and pushes toward "how does a project get handed over."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Post 03 had a moment I keep thinking about: the code graph was already built, but allocation reported the resource didn't exist — asset registration hadn't been completed. Finished content doesn't mean the downstream consumption path works.&lt;/p&gt;

&lt;p&gt;So the "integration" road has moved forward. But in day-to-day work another need kept getting sharper:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When I switch sessions — or switch agents — can work continue along the path the project already walked?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Take this article. A successor should at least know which problems the earlier posts covered and which step this one moves forward; which conclusions came from my own hands-on work and which from official documentation; what the reference articles inspired, and which narratives must not be passed off as my experience.&lt;/p&gt;

&lt;p&gt;Knowing that I've "been researching memory lately" is nowhere near enough. It has to take that context and produce the right next action.&lt;/p&gt;

&lt;p&gt;To be clear: this off-target draft was not a controlled comparison of memory products. I'm not claiming anything about which system recalls better. It just forced me to say my own requirement out loud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tencent line: integrated. Mnemosyne OS: still an open question
&lt;/h2&gt;

&lt;p&gt;Keep the two tracks separate.&lt;/p&gt;

&lt;p&gt;TencentDB Agent Memory — I've done real integration and debugging with it in the DSH scenario. Its official documentation explicitly puts memory storage, retrieval, and the access relationships between users, teams, and agents at the center of the product. [1]&lt;/p&gt;

&lt;p&gt;Mnemosyne OS — so far I've only read GCat's article about it. I have not independently verified it on the same kind of task.&lt;/p&gt;

&lt;p&gt;What caught my attention in that write-up is that it stores raw material and the index of descriptive material separately, and exposes precise location lookup, category lookup, and semantic search. [2] Whether the implementation actually delivers on that description in practice is something I have not yet verified on my side.&lt;/p&gt;

&lt;p&gt;The description maps onto something I need when writing: a summary helps me recall a conclusion quickly, but when I actually quote a number or confirm an operation, I need to get back to the material. A findable summary should also be able to walk me back to where the evidence lives.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmbiz.qpic.cn%2Fsz_mmbiz_png%2F8tmqJgfFV7fIjTHGBcIdX1r2xmKT62edtnRXkG0pAJPLpGbSkuUK9jNSESpeUVKSibibjMFIIabufEWNthgoO3wVqgjN2vmkSKXDrx6mkz0Qk%2F0%3Ffrom%3Dappmsg" 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%2Fmmbiz.qpic.cn%2Fsz_mmbiz_png%2F8tmqJgfFV7fIjTHGBcIdX1r2xmKT62edtnRXkG0pAJPLpGbSkuUK9jNSESpeUVKSibibjMFIIabufEWNthgoO3wVqgjN2vmkSKXDrx6mkz0Qk%2F0%3Ffrom%3Dappmsg" alt="Verification flow: from a question to summaries and indexes, back to source code, docs or execution records, checking source, time and scope before deciding the next action" width="1440" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2: the verification path I want a successor to walk. This is a usage flow I'm proposing, not an architecture diagram of any product.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Reading this, I added one more question for myself:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When the project has already changed, is the memory the AI retrieves still fit to guide today's action?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Found relevant content" is only the starting point. Next comes judging which project and which phase it applies to, and whether it has since been corrected.&lt;/p&gt;

&lt;p&gt;I once used a certain development approach and later changed it. The old record still has value — it explains why things were done that way. But if a new session acts directly on the old record, it drags the project backwards.&lt;/p&gt;

&lt;p&gt;That's why I'm still reading about Mnemosyne OS: I want to follow the thread of how memory is organized and retrieved, and make my own handoff problem concrete.&lt;/p&gt;

&lt;h2&gt;
  
  
  I already hit this need while building my workbench
&lt;/h2&gt;

&lt;p&gt;Recently I've been turning my content workbench into a platform that can run on its own.&lt;/p&gt;

&lt;p&gt;When I first wrote the requirements, one thing mattered a lot: it must own its own flow, be easy to operate, run independently, and not depend on any single agent.&lt;/p&gt;

&lt;p&gt;Concretely: articles, revisions, publish tasks and execution results live in the application. When a scheduled task runs and what state it's in is managed by the service. A model can help with writing, but if I turn that off, content management and publishing should still work.&lt;/p&gt;

&lt;p&gt;Finishing that let me separate two questions I'd been conflating:&lt;/p&gt;

&lt;p&gt;One: &lt;strong&gt;does the program persist enough business state?&lt;/strong&gt;&lt;br&gt;
Two: &lt;strong&gt;can a later agent understand that state — and the reasons it came to be?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The workbench already persists articles, tasks and logs. That is not the same thing as "cross-agent long-term memory achieved." Whether a successor can read, understand, and correctly use those records still has to be verified on its own. That gap is what I want to close.&lt;/p&gt;

&lt;h2&gt;
  
  
  A recycle bin makes "remembered" concrete
&lt;/h2&gt;

&lt;p&gt;The workbench recently got delete, restore, and batch cleanup for its publish queue. Small feature, but a good probe for whether memory is actually useful.&lt;/p&gt;

&lt;p&gt;Say I move a not-yet-executed publish schedule into the recycle bin, then restore it.&lt;/p&gt;

&lt;p&gt;An agent that only retrieves "the user once asked to publish this article" may conclude the task isn't finished and should be completed.&lt;/p&gt;

&lt;p&gt;But the rule in the current workbench is: &lt;strong&gt;moving a pending schedule into the recycle bin cancels it; restoring the record keeps it cancelled and does not trigger a re-publish.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So at least three things have to be read together:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2F8tmqJgfFV7d38Fd3KlEs1l7X7fDHS0grnJG1QibDs0rCHLhbz8G76IBvIQCWs2IeEsDsqxngbtdXyrFkmUImMsH7owVxWzF4yEPHSyc3FPsQ%2F0%3Ffrom%3Dappmsg" 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%2Fmmbiz.qpic.cn%2Fmmbiz_png%2F8tmqJgfFV7d38Fd3KlEs1l7X7fDHS0grnJG1QibDs0rCHLhbz8G76IBvIQCWs2IeEsDsqxngbtdXyrFkmUImMsH7owVxWzF4yEPHSyc3FPsQ%2F0%3Ffrom%3Dappmsg" alt="Workbench rule: a pending schedule is cancelled when moved to the recycle bin, stays cancelled after restore, and does not auto-publish; clearing the local queue does not retract a published article" width="1440" height="1140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3: the workbench's current behavior, illustrated with an example task. "Restoring a record" and "publishing again" are two different actions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Created a schedule, later cancelled, later restored — all three can be true at once. The question is which statement describes the past and which one governs the present.&lt;/p&gt;

&lt;p&gt;Same with published content: clearing a local queue record does not mean the article was retracted from the platform.&lt;/p&gt;

&lt;p&gt;If an agent reads "record deleted" as "article not yet published" and helpfully re-publishes it, then a more eager memory produces a bigger mess.&lt;/p&gt;

&lt;p&gt;So for stateful business objects like publish tasks, I want a successor to check the current record first, then the execution receipts and history. Background material helps understanding; the final action still has to obey the state rules in the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;History should explain the present. Current state should also be able to block the wrong action.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That tells me more about whether a system works than memorizing another set of memory terminology.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements docs set direction; project memory has to carry the changes
&lt;/h2&gt;

&lt;p&gt;I also read a WorkBuddy development tutorial by Eric (网瘾中年 Eric), which walks from rapid prototyping to clarifying requirements, splitting tasks, and writing acceptance criteria. [3] The method I took from it is plain: write down what to build and how far to take it before letting the AI start.&lt;/p&gt;

&lt;p&gt;Applied to my own projects, I want to add one step: when a requirement changes, record the change and the reason.&lt;/p&gt;

&lt;p&gt;A project might start out allowing automation inside the dev tool; later I explicitly require standalone operation. Hand the next agent only the earliest plan and it may happily reinstall a dependency I deliberately removed.&lt;/p&gt;

&lt;p&gt;So I'm going to write a short handoff page for each ongoing project. For the workbench it would read something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What it should become:&lt;/strong&gt; a personal content platform that runs standalone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to protect while continuing development:&lt;/strong&gt; the model powers optional writing assistance; the core flow belongs to the application. Restoring a cancelled publish record must not resend it; clearing queue records must not delete drafts or published content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where to find the evidence:&lt;/strong&gt; the current implementation, acceptance records, and the corresponding change notes; go back to the raw material when something needs checking.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2F8tmqJgfFV7e9EypIONO04OeGwcJpIczvc8sHLViaVuv73Xm5GgutpY1g6j7f3K43SjBpnK07ydDpA73SHjaDCy9PdYH4bFy9WF0UVoowB73c%2F0%3Ffrom%3Dappmsg" 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%2Fmmbiz.qpic.cn%2Fmmbiz_png%2F8tmqJgfFV7e9EypIONO04OeGwcJpIczvc8sHLViaVuv73Xm5GgutpY1g6j7f3K43SjBpnK07ydDpA73SHjaDCy9PdYH4bFy9WF0UVoowB73c%2F0%3Ffrom%3Dappmsg" alt="Proposed handoff approach: project goal, rules to protect, and lookup entry points given to a new agent, then verify current state in the app before continuing" width="1440" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 4: the handoff method I plan to validate next. It is not wired up automatically yet — the page supplies context; current task state still has to be confirmed inside the application.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This handoff page is something I intend to try; it has not been wired into an automatic memory flow yet.&lt;/p&gt;

&lt;p&gt;It also shouldn't become another chat log stuffed with detail. Task state that changes often should be queried from the application. Only settled constraints, important trade-offs, and lookup entry points belong on the page.&lt;/p&gt;

&lt;p&gt;Writing works the same way. The handoff for post 05 should include the through-line of 01–04, this post's question, where the source material came from, and which facts are verified. With that in hand, reference articles go back to being material for me to think with.&lt;/p&gt;

&lt;h2&gt;
  
  
  N+EN was suggestive, but decisions still need evidence
&lt;/h2&gt;

&lt;p&gt;The N+EN idea in GCat's article caught my attention on one point: a business agent's output can first be submitted as a candidate, then organized and reviewed before becoming the conclusion everyone builds on. [2]&lt;/p&gt;

&lt;p&gt;Worth borrowing. But dropping it into my workbench can't mean "add another model and it will naturally know who's right."&lt;/p&gt;

&lt;p&gt;Whether a publish task actually executed is answered by records and receipts. Whether a restored task may re-publish is answered by product rules and the user's decision. Whether a passage is my own firsthand experience is answered by going back to the source material.&lt;/p&gt;

&lt;p&gt;A model can help organize that evidence. The hard part is writing down which evidence is sufficient and which cases need a human call.&lt;/p&gt;

&lt;p&gt;The bad draft gave me a ready-made counter-example: it presented official PersonaMem benchmark results as "my own testing." Even if the numbers had been copied correctly, the attribution would already be wrong.&lt;/p&gt;

&lt;p&gt;And if that sentence gets summarized into long-term memory, every future article may cite it. What eventually sticks is an error record that grows more familiar the more it's used.&lt;/p&gt;

&lt;p&gt;So what I want to preserve isn't just conclusions — it's where they came from and under what conditions they hold.&lt;/p&gt;

&lt;p&gt;On the engineering side, review conclusions and database transactions also have to stay separate concerns. Judging which material is trustworthy does not replace concrete mechanisms like preventing duplicate writes and keeping versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next: three small handoff experiments
&lt;/h2&gt;

&lt;p&gt;I'm not ready to call any of this solved. I want to test it with three small tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First: continue writing in a fresh session.&lt;/strong&gt; Give the new session the same series handoff material and see whether it can state where the earlier posts left off and pose the question this one advances. The draft should also correctly separate firsthand experience, described material, and unverified ideas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second: continue building the workbench with a different agent.&lt;/strong&gt; Give it the current code and the handoff page, add one small feature. Acceptance checks: did it reintroduce a dependency I removed, and did it break an existing business rule?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third: deliberately feed it a stale record.&lt;/strong&gt; Let it see both "a publish schedule was created" and "it was later cancelled," then watch whether it finds the current state, explains the change, and stops the action that shouldn't continue.&lt;/p&gt;

&lt;p&gt;These three experiments are the homework I want to turn in next. After running them I can talk about what worked and where the handoff still breaks.&lt;/p&gt;

&lt;p&gt;From post 01 to here, my question has been getting more specific. It started as: how does AI memory get stored. Then it moved to wiring tools, reading source, studying plugins. Now I want these capabilities to preserve a project's full backstory — so that next time, in a new session, I can start from "here's where I left off."&lt;/p&gt;

&lt;p&gt;More notes from this series live on my blog: &lt;a href="https://blog.aiclawonline.website/" rel="noopener noreferrer"&gt;https://blog.aiclawonline.website/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;p&gt;[1] TencentDB Agent Memory, official MemoryCore documentation — used to check the stated responsibilities around memory, retrieval, assets, and access relationships. My DSH integration experience continues from post 03 of this series.&lt;br&gt;
&lt;a href="https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/feat/server_team/MemoryCore/README_CN.md" rel="noopener noreferrer"&gt;https://github.com/TencentCloud/TencentDB-Agent-Memory/blob/feat/server_team/MemoryCore/README_CN.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2] GCat, "Multi-agent memory shouldn't be one shared brain: N+EN distributed transmission sync + a ready-to-use cognitive prompt." My understanding of Mnemosyne OS comes from this article; I have not independently verified its implementation. N+EN is cited as a reference idea.&lt;br&gt;
&lt;a href="https://my.g-cat.cn/experience/n-en-sync-memory.html" rel="noopener noreferrer"&gt;https://my.g-cat.cn/experience/n-en-sync-memory.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[3] Eric (网瘾中年 Eric), "A WorkBuddy product development tutorial even AI beginners can follow" (course material, 2026-09-11). I borrowed the method of clarifying requirements, splitting tasks, and setting acceptance criteria; I have not adopted any demo results from the material as outcomes of my own project. No public download address is included here.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Why Do DSH Plugins Need a Restart? Bundles, Patches, and Hot Reloading Explained</title>
      <dc:creator>liuyuyan6100</dc:creator>
      <pubDate>Sun, 13 Sep 2026 15:05:03 +0000</pubDate>
      <link>https://dev.to/liuyuyan6100/why-do-dsh-plugins-need-a-restart-bundles-patches-and-hot-reloading-explained-5ep5</link>
      <guid>https://dev.to/liuyuyan6100/why-do-dsh-plugins-need-a-restart-bundles-patches-and-hot-reloading-explained-5ep5</guid>
      <description>&lt;p&gt;At the end of my previous article about connecting DSH to Agent Memory, I left myself two directions to explore: work through DSH's plugin loading and development process, or take the memory integration further. Whichever produced something first would become the next article.&lt;/p&gt;

&lt;p&gt;This week, it is the plugins' turn.&lt;/p&gt;

&lt;p&gt;Here is the answer I found in the source code: &lt;strong&gt;in DSH, installing a plugin and editing configuration are different operations.&lt;/strong&gt; In the normal bundle installation path, a newly installed plugin belongs to a bundle layer. It will not be baked into the plugin list until the next startup, which is why a restart is required. The “save to apply” behavior described in the documentation reloads the user patch layer at the top of the stack. The two statements describe different layers. This article follows how I pulled those two mechanisms apart.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;DSH is an extensible Agent tool runtime built on Cordis. System capabilities are split into pluggable plugins, and profiles, bundles, and patches determine the effective plugin list. If you are unfamiliar with it, article 02 in this series provides some background.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The reason for this detour was simple: I was still gathering material for the memory integration, while my plugin experiments had already produced a pile of questions. In the previous article, I described DSH as an “everything is a plugin” architecture. I believed it, and I had successfully installed and run a third-party plugin. But several things that happened during installation still did not make sense to me. This week, I went through the source to work them out.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installing a real plugin raised questions before I could celebrate
&lt;/h2&gt;

&lt;p&gt;The normal path for installing a third-party DSH plugin takes two steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dsh plugin &lt;span class="nt"&gt;--profile&lt;/span&gt; web add @tt-a1i/archify-dsh@0.1.0
&lt;span class="c"&gt;# Press Ctrl+C, then restart&lt;/span&gt;
pnpm dsh web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Archify is a skill-only plugin for drawing architecture diagrams. After installing and restarting, I asked it to draw a runtime architecture diagram in a new session, and it worked. Its built-in &lt;code&gt;doctor&lt;/code&gt; check also passed all thirteen checks.&lt;/p&gt;

&lt;p&gt;So far, so good. But two things bothered me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, why was a restart mandatory?&lt;/strong&gt; Without restarting, the plugin did not take effect. It was 2026, and a modern plugin system still needed a process restart after an installation?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second, the documentation said that the web profile supported live reloading: edit the configuration file, save it, and the change takes effect.&lt;/strong&gt; A mandatory restart on one hand, immediate changes on the other. Those statements seemed contradictory unless they referred to different things.&lt;/p&gt;

&lt;p&gt;Following those two clues into the source led to a more elegant answer than I expected.&lt;/p&gt;




&lt;h2&gt;
  
  
  What “everything is a plugin” really means: the plugin list is baked
&lt;/h2&gt;

&lt;p&gt;DSH is built on Cordis. I used to interpret “everything is a plugin” as “all functional modules are plugins.” That is true, but it misses a deeper part of the design.&lt;/p&gt;

&lt;p&gt;The source shows that &lt;strong&gt;the plugin list DSH actually starts is not any single handwritten configuration file. It is the result of starting with an empty configuration and applying patches, layer by layer, in order.&lt;/strong&gt; The layer-loading sequence is in &lt;code&gt;packages/boot/app-boot/src/index.ts&lt;/code&gt;; that code also establishes the reloading boundary discussed later.&lt;/p&gt;

&lt;p&gt;There are three concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Entry&lt;/strong&gt;: a plugin instance to start, represented as &lt;code&gt;{ id, name, config }&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layer&lt;/strong&gt;: a YAML patch file that adds entries to the list or modifies existing entries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bundle&lt;/strong&gt;: an npm package that declares &lt;code&gt;dsh.bundle&lt;/code&gt;. &lt;strong&gt;The patch file supplied by that package is the layer it contributes.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The layers baked into the web profile on my machine look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Empty root configuration: []
  -&amp;gt; dsh-base
  -&amp;gt; dsh-web-app
  -&amp;gt; archify
  -&amp;gt; super-injector
  -&amp;gt; user patch: cordis.patch.yml (live HMR)
  -&amp;gt; --patch overlays (one-shot)
  -&amp;gt; final plugin entries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I had thought of as “the plugin configuration” was actually a view produced by applying patch layers to an empty configuration in order. An official command can print this layered view directly. Each section includes a &lt;code&gt;# == package-name&lt;/code&gt; source comment, making it clear which layer contributed which entries.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three patch operations, with rules that matter
&lt;/h2&gt;

&lt;p&gt;A layer is an increment, not a complete configuration. Patches from all layers are flattened into a list and applied in order. There are only three operations. The patch algorithm lives in &lt;code&gt;applyEntryPatches&lt;/code&gt; in &lt;code&gt;vendor/include/src/index.ts&lt;/code&gt;; the four rules below come from its code comments.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;{ insert: [...] }&lt;/code&gt;: append new entries. This is the operation used when installing plugins.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{ id: target, ... }&lt;/code&gt;: find an existing entry by ID, then replace its configuration or disable it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{ id: group, insert: [...] }&lt;/code&gt;: insert child entries into a group-type plugin.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Four rules are especially important.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Later layers can modify earlier layers.&lt;/strong&gt; Patches run in order. An entry inserted by an earlier layer can be modified by ID in a later layer, so &lt;strong&gt;order determines precedence&lt;/strong&gt;. The user layer sits above the bundle layers, which is why it can override a plugin package's default behavior. The one-shot &lt;code&gt;--patch&lt;/code&gt; overlays shown in the diagram come after it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inserted entries enter the index immediately.&lt;/strong&gt; This is easy to overlook but essential: a later patch in the same batch can target an entry that was just inserted. Without this behavior, entries inserted by bundles would be black boxes that the user layer could never configure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A deep-copy snapshot is taken before each layer is applied.&lt;/strong&gt; Layers do not share objects. Removing a patch can therefore restore the configuration below it cleanly. Rollback is part of the design, rather than an afterthought.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Malformed patches fail loudly; unmatched entries only produce warnings.&lt;/strong&gt; A broken patch file causes a startup failure instead of letting the process continue with bad configuration. But a patch that does not match an entry only produces a warning and is skipped. One overlay may be shared by several environments, so it does not need to match something everywhere.&lt;/p&gt;

&lt;p&gt;I like this distinction: &lt;strong&gt;fail as early as possible on invalid configuration, but be permissive about unmatched targets.&lt;/strong&gt; One is an error; the other can be normal.&lt;/p&gt;




&lt;h2&gt;
  
  
  The earlier questions finally fit together
&lt;/h2&gt;

&lt;p&gt;Once I understood the mechanism, I could explain the behavior that had confused me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does installing a plugin require a restart?&lt;/strong&gt; In the normal assembly path, layers are baked into the plugin list at startup. Once the process is running, it does not reread the contents of the bundle layers. Installing a new package or changing its version therefore requires a restart so that the list can be baked again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does the documentation also say “save to apply”?&lt;/strong&gt; Live reloading watches the two patch files in the &lt;strong&gt;user layer&lt;/strong&gt;, above the bundle layers in the diagram. It uses transactional replay through Cordis HMR: &lt;code&gt;hmr.registerConfig&lt;/code&gt; registers the watch; when a file changes, the configuration is reread and the entire layer is replayed. If that fails, the previous generation is retained. Only the user layer is reloaded. The bundle layers underneath remain unchanged. &lt;strong&gt;The two descriptions refer to different layers.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Install or upgrade a bundle through normal assembly&lt;/th&gt;
&lt;th&gt;Edit a user patch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Change&lt;/td&gt;
&lt;td&gt;Install or upgrade the package&lt;/td&gt;
&lt;td&gt;Edit the profile or home user patch file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Registration&lt;/td&gt;
&lt;td&gt;Reconcile the layer catalog&lt;/td&gt;
&lt;td&gt;The watch is registered through &lt;code&gt;hmr.registerConfig&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apply&lt;/td&gt;
&lt;td&gt;Restart the process and bake the layers again&lt;/td&gt;
&lt;td&gt;Transactionally replay the changed layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result&lt;/td&gt;
&lt;td&gt;The rebuilt plugin list takes effect after startup&lt;/td&gt;
&lt;td&gt;Apply the new layer, or retain the previous generation on failure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This comparison concerns normal bundle assembly and user-configuration reloading. Runtime injection through super-injector is a separate path, described below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does running &lt;code&gt;dsh plugin&lt;/code&gt; automatically pick up a package installed manually with &lt;code&gt;pnpm add&lt;/code&gt;?&lt;/strong&gt; Reconciliation detects the package's &lt;code&gt;dsh.bundle&lt;/code&gt; declaration and appends it to the layer catalog. This is deliberate behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does an installed package sometimes produce only a warning and never activate?&lt;/strong&gt; It has not declared &lt;code&gt;dsh.bundle&lt;/code&gt;. Without contributing a layer, it is just an ordinary dependency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does a restart produce &lt;code&gt;duplicate loader entry id&lt;/code&gt;?&lt;/strong&gt; Two layers have inserted the same entry. A typical case is a bundle registering its own entry while the user has also copied that entry into a user patch. Both layers have inserted exactly what they were told to insert.&lt;/p&gt;

&lt;p&gt;This mechanism explained six of the seven pitfalls I had encountered. It felt good to have one model that explained the behavior instead of a list of conclusions to memorize.&lt;/p&gt;




&lt;h2&gt;
  
  
  Two development paths, depending on how often the code changes
&lt;/h2&gt;

&lt;p&gt;With the mechanism clear, I returned to my original ambition: moving from using plugins to developing them. I found two development paths in the DSH ecosystem. The choice depends on how frequently the source changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first is the official assembly path.&lt;/strong&gt; A plugin is a TypeScript module with &lt;code&gt;export const name&lt;/code&gt; and an &lt;code&gt;apply(ctx)&lt;/code&gt; function that registers tools, events, and services on the context. For local development, link the package into the profile's &lt;code&gt;node_modules&lt;/code&gt; using &lt;code&gt;link:&lt;/code&gt;, then attach it with a patch file. Updates follow the installation method: change the version for a registry package, or replace the directory contents for a linked package. One detail matters when replacing a directory: remove the old directory before extracting the new one. Extracting over it can leave behind files that the new package removed, and those stale files may be loaded accidentally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The second is runtime injection.&lt;/strong&gt; On my machine, the resident super-injector makes this a hot-swapping workflow: generate a plugin skeleton, build a tgz, inject it into the running process, and hot-reload it without restarting. Failed hot reloads automatically roll back to preserve the old version. Unloading cleans up the fibers, and there is even a dedicated recovery command for orphaned routes left behind by hot reloads.&lt;/p&gt;

&lt;p&gt;My choice is to use official assembly for finished plugins that I update with upstream releases, such as archify and super-injector itself. Upgrading takes one command. When I start writing my own plugin and reach the edit-and-try-again stage, I will switch to injection. Restarting after every change would quickly become exhausting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;This time, the result was not an impressive end-to-end demo. It was a model: &lt;strong&gt;the plugin list is baked, layer order determines precedence, and live configuration reloading happens only in the user layer.&lt;/strong&gt; When I install another plugin or change configuration, I now have that layered diagram in mind.&lt;/p&gt;

&lt;p&gt;As for developing a plugin myself, I have worked out the skeleton and the build process. What I still need is a real requirement to aim at. The next article will start with that plugin: registering the first tool through &lt;code&gt;apply(ctx)&lt;/code&gt;, declaring &lt;code&gt;dsh.bundle&lt;/code&gt;, building a tgz, and injecting it into the runtime. I want to walk the whole path from source code to a plugin running inside DSH and report back whether it succeeds or fails. The memory integration is also moving forward. The rule remains the same: whichever direction produces something first gets the next article.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Environment used for this article: Windows 11 + WSL2, running DSH from a source checkout with &lt;code&gt;pnpm dsh web&lt;/code&gt;, using the web profile. Relevant source locations: &lt;code&gt;packages/boot/app-boot&lt;/code&gt; for layer loading, and &lt;code&gt;vendor/include&lt;/code&gt; for the patch algorithm.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>deepseek</category>
      <category>architecture</category>
      <category>plugins</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Wiring DSH Into TencentDB Agent Memory: Five Traps, Each With a Different Root Cause</title>
      <dc:creator>liuyuyan6100</dc:creator>
      <pubDate>Mon, 31 Aug 2026 15:19:44 +0000</pubDate>
      <link>https://dev.to/liuyuyan6100/wiring-dsh-into-tencentdb-agent-memory-five-traps-each-with-a-different-root-cause-3a6d</link>
      <guid>https://dev.to/liuyuyan6100/wiring-dsh-into-tencentdb-agent-memory-five-traps-each-with-a-different-root-cause-3a6d</guid>
      <description>&lt;p&gt;I ended the last DSH piece with a promise:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Next I want to wire DSH up to the local TencentDB Agent Memory as a callable tool and get a real end-to-end scenario running. I'll write it up once it works.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It works now. Time to pay that debt.&lt;/p&gt;

&lt;p&gt;It was not a smooth plug-in. It was five problems stacked on top of each other: a wrong model id, a network address that didn't exist, a proxy injecting an unreachable gateway, a model hallucinating an empty tool name, and a "knowledge base resource does not exist" ghost story. Every one of them taught me something about a different layer of the stack. Below is the order I actually debugged them in, not a tidy textbook order.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I was trying to build
&lt;/h2&gt;

&lt;p&gt;Let me set the target first so the rest makes sense.&lt;/p&gt;

&lt;p&gt;I run DSH (DeepSeek Harness) from source on Windows. I wanted it to call a set of &lt;strong&gt;external memory tools&lt;/strong&gt; during a conversation — searching past sessions, writing knowledge, querying a code graph. That memory system is TencentDB Agent Memory, running in Docker under WSL2, split across three containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;tdai-proxy&lt;/code&gt; — model proxy plus the skill/memory injection gateway, port 8096&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tdai-memory-hub&lt;/code&gt; — admin panel, 8125 / 8424&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tdai-memory-core&lt;/code&gt; — identity, assets, memory storage, 8420&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The integration mode is &lt;strong&gt;proxy mode&lt;/strong&gt;: dsh doesn't talk to OpenAI or DeepSeek directly. It sends LLM requests to &lt;code&gt;http://127.0.0.1:8096/dsh/default&lt;/code&gt;. Before and after forwarding upstream to a relay, the proxy injects &lt;code&gt;&amp;lt;skill_tools&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;tdai_memory_tools&amp;gt;&lt;/code&gt; blocks into the system prompt so the model knows these tools exist; the actual calls then go back to the proxy's bridge endpoint.&lt;/p&gt;

&lt;p&gt;Sounds like a one-line change to the API base URL, right? That's exactly what I thought, too.&lt;/p&gt;




&lt;h2&gt;
  
  
  Trap 1: the SSE stream just died
&lt;/h2&gt;

&lt;p&gt;Right after changing the address, every turn failed with &lt;code&gt;SSE stream ended without [DONE]&lt;/code&gt;, error code &lt;code&gt;STREAM_CLOSED&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My first instinct was that the proxy had crashed, but the container logs said it was healthy. So I hit the relay directly with curl and got this back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"model_not_found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"No available channel for model deepseek-v4-pro under group 百炼国模"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alibaba's Bailian platform doesn't expose &lt;strong&gt;bare model aliases&lt;/strong&gt;. Every snapshot is an independent id with a date suffix, something like &lt;code&gt;deepseek-v4-pro-0813&lt;/code&gt;. The relay had no channel configured for &lt;code&gt;deepseek-v4-pro&lt;/code&gt;, so it returned 503. dsh's SSE parser never received &lt;code&gt;[DONE]&lt;/code&gt; before EOF and treated it as a truncated stream — that's by design, not a bug.&lt;/p&gt;

&lt;p&gt;Editing &lt;code&gt;~/.dsh/settings.yaml&lt;/code&gt; fixed it: I swapped the id declared under &lt;code&gt;llm-deepseek.models&lt;/code&gt; for &lt;code&gt;deepseek-v4-pro-0813&lt;/code&gt;, pointed &lt;code&gt;agent-default-model.model&lt;/code&gt; at the same thing, and the first turn went through.&lt;/p&gt;

&lt;p&gt;That was only the first domino.&lt;/p&gt;




&lt;h2&gt;
  
  
  Trap 2: not enough balance, reported as "invalid API key"
&lt;/h2&gt;

&lt;p&gt;With the model id correct, longer sessions started failing again. The dsh UI said "this turn failed: invalid API key", error code &lt;code&gt;AUTH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That wording is dangerously misleading. I nearly went off to rotate my key. The proxy logs told a different story — upstream had returned 403:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"预扣费额度失败, 用户剩余额度: ＄56.639376, 需要预扣费额度: ＄59.112000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"insufficient_user_quota"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;new-api freezes the worst case up front: &lt;code&gt;(input tokens + output cap) × model multiplier × group multiplier&lt;/code&gt;. On this relay that works out to roughly &lt;code&gt;$0.46 per 1K tokens of context&lt;/code&gt;, so a single 128K-context turn wanted close to $60 pre-authorized. Not enough balance, hence 403. dsh maps both 401 and 403 onto &lt;code&gt;AUTH&lt;/code&gt;, which is how a quota problem turned into "invalid API key".&lt;/p&gt;

&lt;p&gt;Worth noting: this isn't the real charge. After the request completes it settles against actual usage — a 128K turn really costs about &lt;code&gt;$2.3&lt;/code&gt; — but you still have to clear the pre-auth threshold first.&lt;/p&gt;

&lt;p&gt;My stopgap was to keep context under 100K, which dropped the pre-auth below the threshold and let sessions continue. Longer term it's either top up, switch to a lower-multiplier channel, or actively manage session length.&lt;/p&gt;

&lt;p&gt;The lesson I took from this: &lt;strong&gt;dsh's error text is only a local classification. For the precise cause, always read the proxy log or the raw upstream response.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Trap 3: the proxy injected an address Windows can't reach
&lt;/h2&gt;

&lt;p&gt;With the LLM path working, I moved on to testing skill calls. The bridge address written into the &lt;code&gt;&amp;lt;skill_tools&amp;gt;&lt;/code&gt; block was &lt;code&gt;http://172.18.0.4:8096&lt;/code&gt;, and calls timed out immediately.&lt;/p&gt;

&lt;p&gt;From the Windows side, &lt;code&gt;curl 127.0.0.1:8096/skill/search&lt;/code&gt; returned 401 — a business response, meaning the service was alive — but &lt;code&gt;curl 172.18.0.4:8096&lt;/code&gt; gave me a flat &lt;code&gt;curl 52 Empty reply from server&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is the classic WSL2 half-open situation: the Docker bridge subnet &lt;code&gt;172.18.0.0/16&lt;/code&gt; has no working return route to Windows. The TCP handshake appears to succeed, but the response never comes back. A container IP shouldn't be referenced from Windows in the first place, and it drifts on every restart anyway.&lt;/p&gt;

&lt;p&gt;So the real question became: who writes that address into the injected block?&lt;/p&gt;

&lt;p&gt;Reading the proxy source, the base URL decision in &lt;code&gt;MemoryProxy/src/injection/index.ts&lt;/code&gt; is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if &lt;code&gt;injection.externalGatewayUrl&lt;/code&gt; is configured → use it&lt;/li&gt;
&lt;li&gt;otherwise → fall back to the first non-internal IPv4 from &lt;code&gt;os.networkInterfaces()&lt;/code&gt;, which inside a container is the Docker bridge IP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That fallback was designed for single-machine local development. Under Windows + WSL2 it picks an address that is unreachable from the side dsh runs on.&lt;/p&gt;

&lt;p&gt;The worse part: &lt;code&gt;start-proxy.sh&lt;/code&gt; &lt;strong&gt;unconditionally regenerates&lt;/strong&gt; &lt;code&gt;.proxy-config/config.yaml&lt;/code&gt; on every start, so hand-editing the output is pointless. The fix has to go into the generation template:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a line to the &lt;code&gt;injection:&lt;/code&gt; section of the &lt;code&gt;start-proxy.sh&lt;/code&gt; template: &lt;code&gt;externalGatewayUrl: "${PROXY_EXTERNAL_GATEWAY_URL:-}"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Append to &lt;code&gt;.env&lt;/code&gt;: &lt;code&gt;PROXY_EXTERNAL_GATEWAY_URL=http://127.0.0.1:8096&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Re-run the startup script and verify the generated file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This one re-taught me something I keep re-learning: &lt;strong&gt;when a config file is generated by a script, the fix belongs in the generation chain. Hand-editing generated output is planting a landmine.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Trap 4: the model called a nameless tool, and the parser believed OpenAI's format
&lt;/h2&gt;

&lt;p&gt;Once the bridge address was &lt;code&gt;127.0.0.1:8096&lt;/code&gt;, skill calls went out — and dsh immediately reported &lt;code&gt;unknown tool ""&lt;/code&gt;. The tool name was empty.&lt;/p&gt;

&lt;p&gt;This was the most instructive failure of the five. I bypassed dsh entirely, curl'd the proxy with the same headers, and looked at the raw SSE bytes coming back upstream:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;chunk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;.function.name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get_weather"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;arguments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;chunk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;.function.name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;arguments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"'
chunk 3: tool_calls[0].function.name = "&lt;/span&gt;&lt;span class="nl"&gt;",            arguments = 'city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OpenAI's streaming convention puts the tool name only in the first chunk; continuation chunks leave the name field present but empty as a placeholder. dsh's parser in &lt;code&gt;packages/llm/llm-deepseek/src/translate.ts&lt;/code&gt;, however, was written as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An empty string is not &lt;code&gt;undefined&lt;/code&gt;, so the correctly accumulated name got overwritten with &lt;code&gt;""&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The official DeepSeek API simply omits the &lt;code&gt;name&lt;/code&gt; field on continuation chunks, which is why this loose check never caused trouble before. But my upstream is an OpenAI-format relay, where the field exists and is empty — so it triggered instantly.&lt;/p&gt;

&lt;p&gt;The fix is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added a unit test alongside it, pinning the behavior with the exact data shape I captured.&lt;/p&gt;

&lt;p&gt;This reinforces what I said last time: &lt;strong&gt;for streaming or protocol mismatches across services, always capture the raw bytes before you read the code.&lt;/strong&gt; There's a proxy, an adapter, and a translation layer in between, and the logs at any one of them are already somebody's interpretation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Trap 5: the graph is ready, but allocation says "resource does not exist"
&lt;/h2&gt;

&lt;p&gt;The last one felt like a haunting. Memory Hub's Code_Graph page showed both repositories as &lt;strong&gt;Ready&lt;/strong&gt;, but clicking "Assign to Agent" failed with &lt;code&gt;知识库资源不存在或已被删除&lt;/code&gt; ("knowledge base resource does not exist or has been deleted") and a request_id.&lt;/p&gt;

&lt;p&gt;I traced the call chain by request_id through the &lt;code&gt;tdai-memory-hub&lt;/code&gt; and &lt;code&gt;tdai-memory-core&lt;/code&gt; logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/v1/knowledge/allocate
  → /v3/meta/auth/verify            200
  → /v3/meta/team-member/get        200
  → /v3/meta/asset/get  cg-x7dg44vu → 404 asset_not_found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the code graph itself lives in the knowledge service's own storage, and "Ready" on the page only means the graph was built successfully. But "Assign to Agent" validates against &lt;strong&gt;memory-core's asset registry, &lt;code&gt;meta_asset&lt;/code&gt;&lt;/strong&gt;. By design, once the graph finishes building, KS should call back into Panel, which then back-fills the asset registration — and that callback is best-effort. It doesn't block the build when it fails, which leaves you in a middle state where the graph exists but the registration doesn't.&lt;/p&gt;

&lt;p&gt;Panel ships an idempotent backfill endpoint for exactly this. One call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://127.0.0.1:8125/api/v1/knowledge/code-graph/register-meta &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-tdai-service-id: default"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-tdai-user-key: &amp;lt;sk-mem-...&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"team_id":"team-a5bu20zsn8","code_graph_id":"cg-x7dg44vu"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once per graph, returning &lt;code&gt;registered: true&lt;/code&gt;, and allocation started working immediately.&lt;/p&gt;

&lt;p&gt;The general lesson: &lt;strong&gt;when a component's "displayed state" and its "authoritative state" live in two different stores, a healthy UI doesn't mean validation will pass.&lt;/strong&gt; Best-effort async registration needs an explicit compensation entry point.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it feels like now
&lt;/h2&gt;

&lt;p&gt;With all five resolved, dsh can finally call the memory tools mid-conversation. Not the cheap thrill of "changed a config and it worked" — more like the solid feeling you get when every layer's root cause lines up with the symptom above it.&lt;/p&gt;

&lt;p&gt;The single move I relied on most across this whole debugging session: &lt;strong&gt;split the error into layers, then go read raw data one layer down.&lt;/strong&gt; dsh's UI text, the proxy's log, the upstream response, the source code's conditionals — where those three agree, there's no bug; where they disagree, that's your bug.&lt;/p&gt;

&lt;p&gt;I already wrote about DSH's "if the model can see it, it's recorded" principle and Cordis's plugin composition in the previous article. Wiring in a real external memory system made the value concrete for me: plugin architecture isn't just about swapping models, it's about plugging an entire memory system in as another Provider. And traceability isn't just logging — it's being able to peel the onion layer by layer instead of circling in front of a UI error message.&lt;/p&gt;

&lt;p&gt;What's next? Two directions on the desk. One is walking DSH's plugin loading and development flow myself — last time I said everything is a plugin, and now I want to move from being a plugin consumer to a plugin developer, using a real requirement as the exercise. The other is pushing the Agent Memory integration deeper, so retrieval and writes become routine moves in a conversation rather than a single demo path. Whichever produces results first gets written up first.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;原文发表于 &lt;a href="https://blog.aiclawonline.website/posts/dsh-tencentdb-memory-integration" rel="noopener noreferrer"&gt;沐沐ai专题&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&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%2Fjir2f4rd6uyyye1debyp.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%2Fjir2f4rd6uyyye1debyp.png" alt="关注「沐沐ai专题」公众号，获取更多 AI 实战干货"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;关注「沐沐ai专题」公众号，获取更多 AI 实战干货&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>deepseek</category>
      <category>dsh</category>
      <category>agentmemory</category>
      <category>debugging</category>
    </item>
    <item>
      <title>A Week Reading DSH's Source: The Two Rules That Won Me Over</title>
      <dc:creator>liuyuyan6100</dc:creator>
      <pubDate>Sat, 29 Aug 2026 15:17:26 +0000</pubDate>
      <link>https://dev.to/liuyuyan6100/a-week-reading-dshs-source-the-two-rules-that-won-me-over-22bg</link>
      <guid>https://dev.to/liuyuyan6100/a-week-reading-dshs-source-the-two-rules-that-won-me-over-22bg</guid>
      <description>&lt;p&gt;Let me start with context, so this doesn't read like another piece of marketing copy.&lt;/p&gt;

&lt;p&gt;I've been hacking on an agent project for a while, and while shopping for a foundation I went through several frameworks — some so opaque you can't debug them, others where swapping a model adapter means editing core code. Last month I needed something whose capabilities come apart cleanly and where &lt;em&gt;every step can be replayed&lt;/em&gt;, so I dove head-first into DeepSeek's open-source &lt;strong&gt;DSH (DeepSeek Harness)&lt;/strong&gt; and spent a week chewing through the source plus its &lt;code&gt;docs/architecture.md&lt;/code&gt; and &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This isn't a restatement of the official intro. It's my own notes after reading, running, and face-planting. Here's DSH's one-line philosophy up front:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Agent = Model + Harness&lt;/strong&gt;&lt;br&gt;
The model is the soul; the harness is the shell that actually plugs it into the environment — files, shell, search, skills, sessions, sandbox, scheduling, UI, all hanging off of it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What actually won me over wasn't that one-liner, though. It was the two rules baked into its bones: &lt;strong&gt;everything is a plugin&lt;/strong&gt; and &lt;strong&gt;every run is traceable&lt;/strong&gt;. More on both below — everything here is verified by me.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it actually is: an agent shell, not a chatbot wrapper
&lt;/h2&gt;

&lt;p&gt;Start with what it is &lt;em&gt;not&lt;/em&gt;: DSH is not another chatbot skin. It's a harness that wires a model into a real environment.&lt;/p&gt;

&lt;p&gt;Underneath it runs on the &lt;strong&gt;Cordis&lt;/strong&gt; kernel (&lt;code&gt;github.com/cordiverse/cordis&lt;/code&gt;), and every capability lives on a node of a "plugin tree." I assumed that was just fancy talk for "everything is a component" — until I read the source and confirmed that the model, tools, skills, sessions, sandbox, storage, loop, scheduling, and UI are &lt;strong&gt;none of them hardcoded in the core&lt;/strong&gt;. They're all composed from plugins and swappable from config, without touching DSH itself.&lt;/p&gt;

&lt;p&gt;By capability count, DSH ships four shapes. Day to day I live in Standard and Code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard&lt;/strong&gt; — the full toolset: file editing, shell, search, skills, planning, subagents, workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code&lt;/strong&gt; — built on Standard, but tools are exposed through the Code Mode SDK, so the model can orchestrate multi-step operations inside a single TypeScript program. Delicious for coding agents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal&lt;/strong&gt; — the bare-bones shape with only persistent bash and &lt;code&gt;str_replace_editor&lt;/code&gt;. This is for model benchmarks, not for your daily driver.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creator&lt;/strong&gt; — a workbench for authoring custom agent presets, with runtime checks and preset-writing guidance.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The two rules I only believed after reading the source
&lt;/h2&gt;

&lt;p&gt;Both of these are shouted from the website and the README. But you only know they're real once you've tripped over them and gone digging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rule one: everything is a plugin
&lt;/h3&gt;

&lt;p&gt;This is the most counterintuitive and the most elegant part. In other frameworks, swapping a tool provider or a model adapter usually means going spelunking through core code. DSH's core design is: &lt;strong&gt;capability seam = Service Definition + Service Provider + Consumer&lt;/strong&gt;, and you need all three.&lt;/p&gt;

&lt;p&gt;I specifically went to verify a claim that sounded exaggerated — that &lt;em&gt;swapping out one fs provider moves the entire Bash / PTY / LSP stack into a remote sandbox&lt;/em&gt;. After reading how the plugin tree mounts things, it really is just: provide another Provider, point at it in config, and the capability moves. That's what "everything is a plugin" looks like as engineering: changing model, sandbox, or storage is a matter of changing one Provider, not a rewrite.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rule two: every run is traceable
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Model-visible ⟺ logged&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Meaning: anything that reaches a model request — system prompts, reasoning, tool calls and results, subagent dispatch, context injection — must be reconstructible from a single append-only session log. In the Trajectory view you can audit each entry by source; resume, fork, search, and replay all ride on that same event stream.&lt;/p&gt;

&lt;p&gt;In plain words: &lt;strong&gt;debugging an agent finally stops being guesswork&lt;/strong&gt;. What it saw at each step, which tool it called, what came back — all of it is on the record. For an agent that's expected to keep working in a real environment, traceability isn't a nice-to-have; it's the engineering seatbelt. Without it you're stuck forever in the black box of "why did it get it wrong &lt;em&gt;this&lt;/em&gt; time."&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting started: two traps cost me half a day
&lt;/h2&gt;

&lt;p&gt;Regular users need one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @deepseek-ai/dsh &lt;span class="nt"&gt;--profile&lt;/span&gt; web
&lt;span class="c"&gt;# equivalent&lt;/span&gt;
npx @deepseek-ai/dsh web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to modify source, clone it. I went the source route and tripped on step one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/deepseek-ai/deepseek-harness
&lt;span class="nb"&gt;cd &lt;/span&gt;deepseek-harness
pnpm &lt;span class="nb"&gt;install
&lt;/span&gt;pnpm dsh web          &lt;span class="c"&gt;# web is an alias for --profile web; source runs directly via tsx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's no global &lt;code&gt;dsh&lt;/code&gt; command in the source repo — you launch it with &lt;code&gt;pnpm dsh &amp;lt;subcommand&amp;gt;&lt;/code&gt;. &lt;code&gt;pnpm dsh web&lt;/code&gt; is equivalent to &lt;code&gt;pnpm dsh --profile web&lt;/code&gt;, and it's currently the only profile with a dedicated alias.&lt;/p&gt;

&lt;p&gt;Two traps I personally ran into, so you don't have to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Cannot find module 'node:sqlite'&lt;/code&gt;, or pnpm complaining it "requires at least Node.js v22.13" — &lt;strong&gt;your Node is too old&lt;/strong&gt;. Use v22.19+ or ≥24; v20 is simply not enough, since it relies on newer modules.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Cannot find module '.../typert.host.js'&lt;/code&gt;, or a startup error of &lt;code&gt;client bundles not found&lt;/code&gt; — I had to run &lt;code&gt;pnpm run build&lt;/code&gt; once (tsc emits lib/types, tsdown bundles the runtime) before it would come up. In theory &lt;code&gt;pnpm dsh web&lt;/code&gt; runs source directly through tsx, but on a first run with missing client bundles, a build saves the day.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it — two traps, half a day gone.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installing plugins: the "small thing" that wrecked me
&lt;/h2&gt;

&lt;p&gt;DSH has &lt;strong&gt;no hot reload&lt;/strong&gt; — &lt;code&gt;plugin add&lt;/code&gt; only writes the plugin layer into the profile's on-disk config; a running server won't pick it up. So installing a plugin is a fixed sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1) Ctrl+C the server first&lt;/span&gt;

&lt;span class="c"&gt;# 2) install the plugin (works while the server runs, but nothing hot-loads)&lt;/span&gt;
pnpm dsh plugin &lt;span class="nt"&gt;--profile&lt;/span&gt; web add &lt;span class="s2"&gt;"github:NanmiCoder/dsh-auto-mode#main"&lt;/span&gt;
pnpm dsh plugin &lt;span class="nt"&gt;--profile&lt;/span&gt; web add &lt;span class="s2"&gt;"github:zhu1090093659/dsh-web-ui#main"&lt;/span&gt;

&lt;span class="c"&gt;# 3) verify the bundle layer got mounted&lt;/span&gt;
pnpm dsh &lt;span class="nt"&gt;--profile&lt;/span&gt; web &lt;span class="nt"&gt;--dump-config&lt;/span&gt;

&lt;span class="c"&gt;# 4) start it, with --patch (mandatory!)&lt;/span&gt;
pnpm dsh web &lt;span class="nt"&gt;--patch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two hard-won lessons, learn them by heart:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Installing a plugin edits the on-disk config; a running server won't load it&lt;/strong&gt; — you must restart. The first time I installed one, I went looking for the new feature in the UI and found absolutely nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You must start with &lt;code&gt;--patch&lt;/code&gt;.&lt;/strong&gt; This is the one that really bites: &lt;code&gt;plugin add&lt;/code&gt; reports success, but if you start without &lt;code&gt;--patch&lt;/code&gt;, a bunch of plugins and skills simply won't activate — while everything still &lt;em&gt;looks&lt;/em&gt; installed. Wasted effort.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To try a layer temporarily without installing it: &lt;code&gt;npx @deepseek-ai/dsh web --patch ./cordis.patch.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A few truths hiding behind those commands&lt;/strong&gt; (confirmed by reading source and actually doing it):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dsh plugin --profile web add xxx&lt;/code&gt; isn't DSH installing a package itself — it &lt;strong&gt;forwards the remaining arguments verbatim to pnpm&lt;/strong&gt;, equivalent to running &lt;code&gt;pnpm add xxx&lt;/code&gt; inside the profile directory.&lt;/li&gt;
&lt;li&gt;The profile directory defaults to &lt;code&gt;~/.dsh/profiles/&amp;lt;name&amp;gt;/&lt;/code&gt; (on Windows: &lt;code&gt;C:\Users\&amp;lt;user&amp;gt;\.dsh\profiles\&amp;lt;name&amp;gt;\&lt;/code&gt;). Using &lt;code&gt;web&lt;/code&gt; as an example: after installing a plugin, new packages appear under &lt;code&gt;dependencies&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt;, and &lt;code&gt;dsh.profile.bundles&lt;/code&gt; registers a layer automatically — which is why &lt;code&gt;--dump-config&lt;/code&gt; can see it.&lt;/li&gt;
&lt;li&gt;If you install a marketplace UI entry (&lt;code&gt;dshmarket&lt;/code&gt;), the entry isn't a standalone page — it lives under &lt;strong&gt;Settings → Plugin Marketplace&lt;/strong&gt;. The host needs to be ≥ 0.1.0-rc.6 for it to show up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Command cheat sheet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install / remove / inspect dependencies&lt;/span&gt;
pnpm dsh plugin &lt;span class="nt"&gt;--profile&lt;/span&gt; web add &amp;lt;package&amp;gt;
pnpm dsh plugin &lt;span class="nt"&gt;--profile&lt;/span&gt; web remove &amp;lt;package&amp;gt;
pnpm dsh plugin &lt;span class="nt"&gt;--profile&lt;/span&gt; web why &amp;lt;package&amp;gt;

&lt;span class="c"&gt;# start different profiles&lt;/span&gt;
pnpm dsh web                              &lt;span class="c"&gt;# alias for the web profile&lt;/span&gt;
pnpm dsh &lt;span class="nt"&gt;--profile&lt;/span&gt; headless &lt;span class="s2"&gt;"your task"&lt;/span&gt;   &lt;span class="c"&gt;# headless, one-shot task&lt;/span&gt;

&lt;span class="c"&gt;# inspect config&lt;/span&gt;
pnpm dsh web &lt;span class="nt"&gt;--dump-config&lt;/span&gt;                &lt;span class="c"&gt;# fully composed config tree&lt;/span&gt;
pnpm dsh web &lt;span class="nt"&gt;--dump-default-config&lt;/span&gt;        &lt;span class="c"&gt;# defaults, without your overrides&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The plugin ecosystem: what I installed and how it feels
&lt;/h2&gt;

&lt;p&gt;The marketplace is already busy. After installing &lt;code&gt;dshmarket&lt;/code&gt; locally, the real marketplace shows up in DSH Local Builder's settings: &lt;code&gt;dsh-market v1.24.0&lt;/code&gt;, with tabs for Discover / Themes / Installed / Advanced, and filters covering UI enhancement, themes &amp;amp; appearance, model &amp;amp; account access, tools &amp;amp; capabilities. The cards list things like &lt;code&gt;dsh-web-ui-all&lt;/code&gt;, &lt;code&gt;DSH-better-sidebar&lt;/code&gt;, &lt;code&gt;modlens&lt;/code&gt;, &lt;code&gt;dsh-vision-router&lt;/code&gt;, and &lt;code&gt;dsh-vision-toolkit&lt;/code&gt; — all genuinely installable, not just names from docs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Community plugins worth installing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;dsh-web-ui / dsh-web-ui-all&lt;/strong&gt; — the full UI bundle: task board, Git graph, sidebar, terminal, token stats, skins. Installing this is a step change in how it feels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dsh-auto-mode&lt;/strong&gt; (strongly recommended) — manages &lt;strong&gt;safe automatic permissions&lt;/strong&gt; for you, handling shell and file approvals far more safely than hand-editing &lt;code&gt;global.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;modlens&lt;/strong&gt; (900+ stars) — gives plain-text models "eyes" by turning images into structured text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dsh-agent-teams&lt;/strong&gt; (898) — multi-agent collaboration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dsh-memory&lt;/strong&gt; (73) — cross-session memory; this is where an agent's recall comes from.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then there are &lt;strong&gt;ZhijiangTang's 14 zero-config tool plugins&lt;/strong&gt;, which I'd call the best textbook on plugin development around — each 100–500 lines, pure JS with no native dependencies, usable right after &lt;code&gt;npm i&lt;/code&gt;. A few high-frequency ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dsh-safeguard&lt;/code&gt; — intercepts dangerous commands (&lt;code&gt;rm -rf&lt;/code&gt;, &lt;code&gt;push --force&lt;/code&gt;) and secret leaks (&lt;code&gt;AKIA&lt;/code&gt; / &lt;code&gt;sk-&lt;/code&gt; / &lt;code&gt;ghp_&lt;/code&gt;) before execution. A life saver.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dsh-http&lt;/code&gt; — structured HTTP: status code, latency, size, Bearer/Basic auth, automatic JSON parsing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dsh-fmt&lt;/code&gt; — JSON/YAML/TOML/SQL formatting and validation, with errors reported down to line and column.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dsh-handoff&lt;/code&gt; — one-click session export (decisions / done / not done / next steps).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dsh-password&lt;/code&gt; — strong password and diceware passphrase generation, with entropy annotated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install the whole bundle in one go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dsh plugin &lt;span class="nt"&gt;--profile&lt;/span&gt; web add dsh-safeguard dsh-http dsh-case dsh-fmt dsh-clipboard &lt;span class="se"&gt;\&lt;/span&gt;
  dsh-fetch-file dsh-jwt dsh-cron-parse dsh-pkg-info dsh-dead-links dsh-when &lt;span class="se"&gt;\&lt;/span&gt;
  dsh-handoff dsh-url-tools dsh-password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also &lt;strong&gt;vendor bridge plugins&lt;/strong&gt;, such as &lt;code&gt;@wxg-prc-cpg/dsh-weknora&lt;/code&gt; (Tencent WeKnora knowledge retrieval: semantic search, document reading, RAG). Those wire an existing product into DSH — high star counts, but they're "bridges," not capabilities built from scratch. Don't confuse the two in your head.&lt;/p&gt;




&lt;h2&gt;
  
  
  Permissions: one line of advice — don't hand-edit
&lt;/h2&gt;

&lt;p&gt;I can't state DSH's permission config file path with any confidence (sources online contradict each other, and I'm not going to invent it). But in my testing, &lt;strong&gt;just installing the &lt;code&gt;dsh-auto-mode&lt;/code&gt; plugin&lt;/strong&gt; is by far the least painful route — it manages shell and file approvals for you, far more reliably than hand-editing config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dsh plugin &lt;span class="nt"&gt;--profile&lt;/span&gt; web add &lt;span class="s2"&gt;"github:NanmiCoder/dsh-auto-mode#main"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Want to write your own? Here's the shortest path
&lt;/h2&gt;

&lt;p&gt;I'm planning one myself, so here's the route I noted down:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Grab the scaffold&lt;/strong&gt; — &lt;code&gt;ZhijiangTang/dsh-plugin-template&lt;/code&gt;; &lt;code&gt;scaffold.sh&lt;/code&gt; generates the skeleton in one command.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the minimal example&lt;/strong&gt; — &lt;code&gt;superfly/sprites-deepseek-plugin&lt;/code&gt; (a 169-line README that explains exactly how &lt;code&gt;cordis.patch.yml&lt;/code&gt; and &lt;code&gt;index.js&lt;/code&gt; combine an MCP client with a skill).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read all 14 tiny zero-config plugins closely&lt;/strong&gt; — you can finish one in an afternoon, which beats reading docs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A standard bundle looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-dsh-plugin/
├── cordis.patch.yml   # declares which dsh packages to mount + config
├── index.js           # entry: resolves assets via import.meta.url, mounts dsh services
├── package.json
└── skills/            # optional: bundled skill resources
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few conventions the community agrees on: &lt;strong&gt;zero config&lt;/strong&gt;, &lt;strong&gt;small single-package surface&lt;/strong&gt;, &lt;strong&gt;normalized failure values&lt;/strong&gt; (a failing tool returns a human-readable error instead of throwing), and &lt;strong&gt;every package self-checks&lt;/strong&gt;. Tag your repo &lt;code&gt;#dsh&lt;/code&gt; and the community directory picks it up automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before you touch the source, etch these in
&lt;/h2&gt;

&lt;p&gt;If you plan to modify DSH itself (as I do, since I want to fold it into the agent project I'm building), here are the conventions you'll hit constantly. The full list lives in the repo's &lt;code&gt;AGENTS.md&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every npm package is named &lt;code&gt;@deepseek-ai/dsh-&amp;lt;name&amp;gt;&lt;/code&gt;; ESM everywhere (&lt;code&gt;"type": "module"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registrations are effects&lt;/strong&gt; — every contribution goes through &lt;code&gt;ctx.effect()&lt;/code&gt; / &lt;code&gt;ctx.on()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Waterfall listeners MUST call &lt;code&gt;next()&lt;/code&gt;&lt;/strong&gt; — skip it and you short-circuit the whole chain. Reading the docs didn't make this click; crashing did.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Misconfiguration fails loud&lt;/strong&gt; — if it can be self-contained, fail at load time, never silently skip.&lt;/li&gt;
&lt;li&gt;Exactly one trailing newline at end of file; &lt;code&gt;git diff --cached --check&lt;/code&gt; is the pre-commit gate.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;What made DSH click for me wasn't that it has the most features. It's that it treats &lt;strong&gt;composability&lt;/strong&gt; and &lt;strong&gt;traceability&lt;/strong&gt; as first-class citizens rather than retrofitted patches.&lt;/p&gt;

&lt;p&gt;Whether a system can grow depends on how clean its seams are. DSH's Service Definition / Provider / Consumer trio leaves a whole rack of standard slots for an agent — swapping model, sandbox, or storage is a one-Provider change. Add the "model-visible means logged" seatbelt on top, and together they're what move it from "yet another framework" to "something I'd bet my project's foundation on."&lt;/p&gt;

&lt;p&gt;Next step: I want to wire that local TencentDB Agent Memory into DSH as a callable tool and run a real end-to-end scenario. I'll write it up once it actually works.&lt;/p&gt;




&lt;p&gt;Entry points if you want to keep digging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Official repo: &lt;a href="https://github.com/deepseek-ai/deepseek-harness" rel="noopener noreferrer"&gt;https://github.com/deepseek-ai/deepseek-harness&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Website: &lt;a href="https://deepseek.com/harness/en/" rel="noopener noreferrer"&gt;https://deepseek.com/harness/en/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Developer docs: &lt;a href="https://deepseek-harness.github.io/deepseek-harness/en/guide/quickstart" rel="noopener noreferrer"&gt;https://deepseek-harness.github.io/deepseek-harness/en/guide/quickstart&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Plugin marketplace: &lt;a href="https://dshhub.org/" rel="noopener noreferrer"&gt;https://dshhub.org/&lt;/a&gt; (9,400+ plugins, continuously scanned)&lt;/li&gt;
&lt;li&gt;Cordis kernel: &lt;a href="https://github.com/cordiverse/cordis" rel="noopener noreferrer"&gt;https://github.com/cordiverse/cordis&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;原文发表于 &lt;a href="https://blog.aiclawonline.website/posts/dsh-deepseek-harness" rel="noopener noreferrer"&gt;沐沐ai专题&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&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%2Fjir2f4rd6uyyye1debyp.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%2Fjir2f4rd6uyyye1debyp.png" alt="关注「沐沐ai专题」公众号，获取更多 AI 实战干货"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;关注「沐沐ai专题」公众号，获取更多 AI 实战干货&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>deepseek</category>
      <category>dsh</category>
      <category>aiagents</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Hands-on with Tencent Cloud Agent Memory: Giving AI a Real Long-Term Memory</title>
      <dc:creator>liuyuyan6100</dc:creator>
      <pubDate>Sun, 23 Aug 2026 11:30:42 +0000</pubDate>
      <link>https://dev.to/liuyuyan6100/hands-on-with-tencent-cloud-agent-memory-giving-ai-a-real-long-term-memory-125m</link>
      <guid>https://dev.to/liuyuyan6100/hands-on-with-tencent-cloud-agent-memory-giving-ai-a-real-long-term-memory-125m</guid>
      <description>&lt;p&gt;I've been following an open-source project lately: Tencent Cloud's &lt;strong&gt;TencentDB Agent Memory&lt;/strong&gt; (repo &lt;code&gt;TencentCloud/tencentdb-agent-memory&lt;/code&gt;). The problem it tackles is simple but, until now, nobody had turned it into an engineering-grade system — &lt;strong&gt;how to stop an AI Agent from starting from scratch every single time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This article is my learning notes written while reading the source code — not a sponsored post. I'll walk through its positioning, architecture, how memory "grows" layer by layer, how it retrieves and governs memory, and how to run it locally. At the end, I'll share why, as someone who works on both embedded systems and Agents, this project caught my attention.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. What problem does it actually solve?
&lt;/h2&gt;

&lt;p&gt;Anyone using Agents to write code or do ops has hit this: for the same project, you have to re-explain the context every time the conversation resets; the same document gets re-read by every Agent; a pitfall one workflow already hit gets hit all over again by the next Agent.&lt;/p&gt;

&lt;p&gt;Re-explaining context, re-reading docs, re-discovering workflows — these three kinds of "repetition" are exactly what Agent Memory wants to eliminate.&lt;/p&gt;

&lt;p&gt;Its own positioning, verbatim: &lt;strong&gt;team-level memory hub for AI Agents&lt;/strong&gt; — turning conversations, documents, and code into four reusable memory assets (Chat Memory / Skill / LLM-Wiki / Code-Graph), then doing &lt;strong&gt;governance, sharing, and equipping&lt;/strong&gt; between Agents and frameworks.&lt;/p&gt;

&lt;p&gt;One distinction the project keeps stressing: &lt;strong&gt;it is not a chat-log warehouse&lt;/strong&gt;. RAG only answers "can we find it"; Team Memory also has to answer "who can use it, which version is valid, which Agent should get it." The former is retrieval; the latter is governance.&lt;/p&gt;

&lt;p&gt;Its slogan is blunt: &lt;em&gt;Agents remember. Humans innovate.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. How the four modules break down
&lt;/h2&gt;

&lt;p&gt;The repo splits into four pieces with clear responsibilities:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;A detail I noted&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MemoryCore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Memory &amp;amp; metadata core&lt;/td&gt;
&lt;td&gt;Stores L0–L3 memory and asset metadata; exposed via HTTP Gateway (default &lt;code&gt;:8420&lt;/code&gt;); SQLite + local files, BM25 recall by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MemoryKnowledge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Knowledge parsing / indexing / retrieval&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Stores only metadata, not the knowledge content itself&lt;/strong&gt;; Wiki parsing, CodeGraph building, and index retrieval all happen here&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MemoryPanel&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Team memory panel (frontend)&lt;/td&gt;
&lt;td&gt;Human-controllable console: Team Up / Asset Library / Agent Loadout / Knowledge Workshop / Access Control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MemoryProxy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Proxy layer&lt;/td&gt;
&lt;td&gt;Zero-code integration via a stable protocol; Agents discover capabilities via &lt;code&gt;/v3/tools/list&lt;/code&gt; and read pages/source/impact paths via &lt;code&gt;/v3/tools/call&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An easy-to-confuse point: &lt;strong&gt;MemoryCore does not store knowledge content&lt;/strong&gt;. For example, when you import a product doc, MemoryCore only registers "which knowledge source, what type, status, relationships, service address" — the actual parsing and retrieval are delegated to MemoryKnowledge. This "metadata/content separation" makes permissions and migration much easier later.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. How memory "grows": L0–L3
&lt;/h2&gt;

&lt;p&gt;This is the part I most wanted to understand. It layers memory into four levels: conversations first land in L0, then an async pipeline refines them into higher-level assets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L0 Conversation&lt;/strong&gt;: Raw conversations with full context, timestamps, and source. Used to verify original statements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L1 Atom&lt;/strong&gt;: Facts, preferences, constraints, and events extracted from conversations. Precise recall of actionable info.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L2 Scenario&lt;/strong&gt;: Knowledge blocks organized around a project/scenario. Quickly restore the working environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L3 Core / Persona&lt;/strong&gt;: Long-term profile, stable patterns, high-level cognition. Lets an Agent enter the user/team context from the start.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Refinement is &lt;strong&gt;asynchronous&lt;/strong&gt; — not real-time distillation every turn, but a pipeline that slowly aggregates L0 upward in the background. Documents and code take a different path: documents → Wiki pages (with a link graph you can drill into); code → CodeGraph (indexing symbols, files, call relationships, impact paths).&lt;/p&gt;

&lt;p&gt;The closed loop is the essence of this design. There's a line from the project I copied into my notes: &lt;strong&gt;"A loop without memory just repeats faster; a loop that inherits memory can beat the last iteration every time."&lt;/strong&gt; Valuable interactions are stored as Chat Memory → validated workflows are distilled into Skills → doc/code changes trigger Wiki ingest and CodeGraph sync.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Retrieval &amp;amp; governance: doing one more step beyond "can be found"
&lt;/h2&gt;

&lt;p&gt;On retrieval it's not picky: normally bootstrap quickly with L2/L3, fall back to L1/L0 when you need specific facts. Underneath is &lt;strong&gt;BM25 + vector retrieval + RRF (Reciprocal Rank Fusion)&lt;/strong&gt;. Results are also capped by three limits — item count, character budget, and timeout — to prevent the context from being stuffed in one shot. This detail is pragmatic; RAG usually fails by cramming too much into the prompt at once.&lt;/p&gt;

&lt;p&gt;Governance is where it pulls ahead of a plain vector DB:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Visibility semantics&lt;/strong&gt;: &lt;code&gt;private&lt;/code&gt; (owner only), &lt;code&gt;team&lt;/code&gt; (team-readable), &lt;code&gt;restricted&lt;/code&gt; (precise ACL by User/Role/Agent), &lt;code&gt;agent&lt;/code&gt; (targeted equipping within the team).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixed Binding + ACL&lt;/strong&gt;: first narrow asset permissions by Team/User/Agent/visibility, then retrieve by query. Which assets a given Agent can actually use is decided by this mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generation provenance&lt;/strong&gt;: the Prompt ID, version, source, and content SHA-256 actually used by L1/L2/L3 are all recorded, and you can pinpoint the generation log by Memory ID. But it doesn't store a snapshot of the Prompt body — guaranteeing traceability while avoiding hoarding the policy itself as data.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Cold start &amp;amp; team playbook
&lt;/h2&gt;

&lt;p&gt;"Cold-start friendly" isn't a slogan. It can import an existing codebase (CodeGraph auto-indexes), documents (Wiki auto-generates), and historical sessions (auto-extracts Skills and Chat Memory) — the project calls this &lt;strong&gt;"loading the save file."&lt;/strong&gt; A newly onboarded Agent team doesn't start from blank; it inherits existing experience.&lt;/p&gt;

&lt;p&gt;The team-playbook example is also down-to-earth: a small team called "Tiny but Serious Inc." with members You / Scout / Builder / Reviewer, plus Agent Memory. Different roles equip different assets — Scout carries Chat Memory from user interviews and the market Wiki; Builder carries the product Wiki and CodeGraph. Same memory substrate, sliced into different views by role.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. How to run it (local hands-on)
&lt;/h2&gt;

&lt;p&gt;I followed along on the &lt;code&gt;feat/server_team&lt;/code&gt; branch; spinning up MemoryCore locally is lightweight:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# MemoryCore is open-sourced as a Standalone Runtime&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;MemoryCore
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run build

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TDAI_LLM_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-api-key"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TDAI_LLM_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://api.openai.com/v1"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TDAI_LLM_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gpt-4o-mini"&lt;/span&gt;
node &lt;span class="nt"&gt;--import&lt;/span&gt; tsx src/gateway/server.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Gateway listens on &lt;code&gt;127.0.0.1:8420&lt;/code&gt; by default, using SQLite, local files, and in-process state. Other than the LLM API, there are no required external services, and it &lt;strong&gt;disables remote Embedding by default, using BM25&lt;/strong&gt; — very friendly for a local single machine. Data is written to &lt;code&gt;~/.memory-tencentdb/memory-tdai&lt;/code&gt; by default.&lt;/p&gt;

&lt;p&gt;To launch the full stack in one go (memory-core + memory-hub + proxy), use the deploy script in the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;deploy/global-images
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env      &lt;span class="c"&gt;# fill in two sets of LLM params (memory group + proxy group)&lt;/span&gt;
./start-all.sh            &lt;span class="c"&gt;# prints a one-liner you can paste into Claude/CodeBuddy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The panel is at &lt;code&gt;http://localhost:8125&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The Agent Adapter only needs to do three things, and the docs are refreshingly restrained: &lt;strong&gt;write L0 when the session ends or after each turn; recall L1/L2/L3 before constructing the Prompt; inject the recalled results into the Agent as bounded, identifiable context.&lt;/strong&gt; For OpenClaw there's a ready-made &lt;code&gt;openclaw-plugin/&lt;/code&gt;, for Hermes a &lt;code&gt;hermes-plugin/&lt;/code&gt;, and for custom Runtimes the TS/Python SDK under &lt;code&gt;sdk/memory-core/&lt;/code&gt;. The adapted Agent list includes DeepSeek Harness, Claude Code, Codex, CodeBuddy, WorkBuddy, Hermes, OpenClaw, with a Generic integration guide for anything not listed.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. A few pitfalls and judgments I noted
&lt;/h2&gt;

&lt;p&gt;A few points I hit (or predicted) while reading:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wiki / CodeGraph are built asynchronously&lt;/strong&gt; — after import you must wait for them to be &lt;code&gt;ready&lt;/code&gt;; they're not queryable immediately. Any automation pipeline must budget for this latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CodeGraph currently prioritizes public HTTPS repos&lt;/strong&gt;; private / SSH support is still maturing. On-prem codebases will have to wait.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;v2 → v3 data migration must run a script first&lt;/strong&gt;, and the docs explicitly say "back up the entire data directory before migrating." The data format goes from v2 to v3; before launching the new Gateway, probe with &lt;code&gt;python scripts/migrate-v2-to-v3/v2-to-v3-migrate.py &amp;lt;data-dir&amp;gt; --dry-run&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On security&lt;/strong&gt;: listening on a non-loopback address requires &lt;code&gt;TDAI_GATEWAY_API_KEY&lt;/code&gt;; CORS is off by default — don't use &lt;code&gt;*&lt;/code&gt;; all Secrets go through environment variables. These are the baseline.&lt;/li&gt;
&lt;li&gt;There's a benchmark figure that speaks to the value: on PersonaMem, a relative improvement of &lt;strong&gt;+59%&lt;/strong&gt; (48% → 76%) — long-term persona memory makes cross-session understanding of Agents markedly better.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Why am I watching this project? Half my energy goes into embedded systems like ESP32, the other half into Agents. On the embedded side I'm researching ESP-Claw — a framework that burns an Agent Runtime into silicon so devices can decide locally. Cloud Agents and edge Agents look like two ends, but &lt;strong&gt;the memory substrate can be the same&lt;/strong&gt;: during development, use an Agent to write firmware and store the pitfalls you hit as Skills; once the on-device Agent connects to sensors via MCP, it can also return to the central memory for context. TencentDB Agent Memory's idea of "memory/execution separation, governance across frameworks" is exactly the missing piece in that picture.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In the Agent industry, the first two years were about "who has the stronger model, who prompts better." Now it's slowly shifting to "who remembers" — not remembered in the chat box, but remembered as governable, shareable, cross-framework-reusable assets.&lt;/p&gt;

&lt;p&gt;Tencent Cloud's open-source project has engineered this: four layers of memory, four kinds of assets, a set of governance semantics, and a proxy layer with a stable protocol. &lt;em&gt;Agents remember. Humans innovate.&lt;/em&gt; — now I have a bit of a feel for that line.&lt;/p&gt;

&lt;p&gt;If you're also working on Agent memory, or have already integrated this system, drop a comment on the blog about your real-world experience — what's good, what's still awkward — that's worth more than the docs.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;原文发表于 &lt;a href="https://blog.aiclawonline.website" rel="noopener noreferrer"&gt;沐沐ai专题&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&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%2Fjir2f4rd6uyyye1debyp.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%2Fjir2f4rd6uyyye1debyp.png" alt="关注「沐沐ai专题」公众号，获取更多 AI 实战干货" width="344" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;关注「沐沐ai专题」公众号，获取更多 AI 实战干货&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>agentmemory</category>
      <category>aiagents</category>
      <category>tencentcloud</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
