惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

IT之家
IT之家
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
A
About on SuperTechFans
博客园 - 聂微东
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
B
Blog RSS Feed
U
Unit 42
Stack Overflow Blog
Stack Overflow Blog
Recent Announcements
Recent Announcements
雷峰网
雷峰网
罗磊的独立博客
Microsoft Security Blog
Microsoft Security Blog
Hugging Face - Blog
Hugging Face - Blog
L
LangChain Blog
人人都是产品经理
人人都是产品经理
The GitHub Blog
The GitHub Blog
F
Fortinet All Blogs
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
H
Help Net Security
P
Proofpoint News Feed
The Cloudflare Blog
D
Docker
大猫的无限游戏
大猫的无限游戏

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
refactor(plugin-sdk): persist dedupe state in sqlite · op...
steipete · 2026-06-07 · via Recent Commits to openclaw:main

@@ -3,7 +3,8 @@

33

*/

44

import fs from "node:fs/promises";

55

import path from "node:path";

6-

import { describe, expect, it } from "vitest";

6+

import { afterEach, describe, expect, it } from "vitest";

7+

import { resetPluginStateStoreForTests } from "../plugin-state/plugin-state-store.js";

78

import {

89

appendMemoryHostEvent,

910

readMemoryHostEvents,

@@ -18,11 +19,17 @@ function createDedupe(root: string, overrides?: { ttlMs?: number }) {

1819

return createPersistentDedupe({

1920

ttlMs: overrides?.ttlMs ?? 24 * 60 * 60 * 1000,

2021

memoryMaxSize: 100,

21-

fileMaxEntries: 1000,

22-

resolveFilePath: (namespace) => path.join(root, `${namespace}.json`),

22+

pluginId: "test-persistent-dedupe",

23+

namespacePrefix: "test-dedupe",

24+

stateMaxEntries: 1000,

25+

env: { ...process.env, OPENCLAW_STATE_DIR: root },

2326

});

2427

}

252829+

afterEach(() => {

30+

resetPluginStateStoreForTests();

31+

});

32+2633

describe("memory host event journal helpers", () => {

2734

it("appends and reads typed workspace events", async () => {

2835

const workspaceDir = await createTempDir("memory-host-events-");

@@ -94,8 +101,10 @@ describe("createPersistentDedupe", () => {

94101

const dedupe = createPersistentDedupe({

95102

ttlMs: Number.NaN,

96103

memoryMaxSize: Number.NaN,

97-

fileMaxEntries: Number.NaN,

98-

resolveFilePath: (namespace) => path.join(root, `${namespace}.json`),

104+

pluginId: "test-persistent-dedupe",

105+

namespacePrefix: "test-bounds",

106+

stateMaxEntries: Number.NaN,

107+

env: { ...process.env, OPENCLAW_STATE_DIR: root },

99108

});

100109101110

expect(await dedupe.checkAndRecord("m1", { namespace: "a", now: 100 })).toBe(true);

@@ -104,33 +113,32 @@ describe("createPersistentDedupe", () => {

104113

expect(dedupe.memorySize()).toBe(0);

105114

});

106115107-

it("falls back to memory-only behavior on disk errors", async () => {

116+

it("uses legacy JSON paths only as SQLite namespace identifiers", async () => {

117+

const root = await createTempDir("openclaw-legacy-dedupe-");

118+

const legacyPath = path.join(root, "legacy.json");

108119

const dedupe = createPersistentDedupe({

109120

ttlMs: 10_000,

110121

memoryMaxSize: 100,

111122

fileMaxEntries: 1000,

112-

resolveFilePath: () => path.join("/dev/null", "dedupe.json"),

123+

resolveFilePath: () => legacyPath,

124+

env: { ...process.env, OPENCLAW_STATE_DIR: root },

113125

});

114126115-

expect(await dedupe.checkAndRecord("memory-only", { namespace: "x" })).toBe(true);

116-

expect(await dedupe.checkAndRecord("memory-only", { namespace: "x" })).toBe(false);

127+

expect(await dedupe.checkAndRecord("sqlite-only", { namespace: "x" })).toBe(true);

128+

expect(await dedupe.checkAndRecord("sqlite-only", { namespace: "x" })).toBe(false);

129+

await expect(fs.access(legacyPath)).rejects.toThrow();

117130

});

118131119-

it("warms empty namespaces and skips expired disk entries", async () => {

132+

it("warms empty namespaces and ignores retired JSON cache files", async () => {

120133

const root = await createTempDir("openclaw-dedupe-");

121134

const emptyReader = createDedupe(root, { ttlMs: 10_000 });

122135

expect(await emptyReader.warmup("nonexistent")).toBe(0);

123136124-

const oldNow = Date.now() - 2000;

125-

await fs.writeFile(

126-

path.join(root, "acct.json"),

127-

JSON.stringify({ "old-msg": oldNow, "new-msg": Date.now() }),

128-

);

137+

await fs.writeFile(path.join(root, "acct.json"), JSON.stringify({ "retired-msg": Date.now() }));

129138130139

const reader = createDedupe(root, { ttlMs: 1000 });

131-

expect(await reader.warmup("acct")).toBe(1);

132-

expect(await reader.checkAndRecord("old-msg", { namespace: "acct" })).toBe(true);

133-

expect(await reader.checkAndRecord("new-msg", { namespace: "acct" })).toBe(false);

140+

expect(await reader.warmup("acct")).toBe(0);

141+

expect(await reader.checkAndRecord("retired-msg", { namespace: "acct" })).toBe(true);

134142

});

135143

});

136144

@@ -189,8 +197,10 @@ describe("createClaimableDedupe", () => {

189197

const writer = createClaimableDedupe({

190198

ttlMs: 10_000,

191199

memoryMaxSize: 100,

192-

fileMaxEntries: 1000,

193-

resolveFilePath: (namespace) => path.join(root, `${namespace}.json`),

200+

pluginId: "test-claimable-dedupe",

201+

namespacePrefix: "test-claimable-dedupe",

202+

stateMaxEntries: 1000,

203+

env: { ...process.env, OPENCLAW_STATE_DIR: root },

194204

});

195205196206

await expect(writer.claim("m1", { namespace: "acct" })).resolves.toEqual({ kind: "claimed" });

@@ -199,8 +209,10 @@ describe("createClaimableDedupe", () => {

199209

const reader = createClaimableDedupe({

200210

ttlMs: 10_000,

201211

memoryMaxSize: 100,

202-

fileMaxEntries: 1000,

203-

resolveFilePath: (namespace) => path.join(root, `${namespace}.json`),

212+

pluginId: "test-claimable-dedupe",

213+

namespacePrefix: "test-claimable-dedupe",

214+

stateMaxEntries: 1000,

215+

env: { ...process.env, OPENCLAW_STATE_DIR: root },

204216

});

205217206218

expect(await reader.hasRecent("m1", { namespace: "acct" })).toBe(true);