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

推荐订阅源

F
Full Disclosure
博客园 - 聂微东
IT之家
IT之家
The Cloudflare Blog
L
LangChain Blog
Last Week in AI
Last Week in AI
T
Tailwind CSS Blog
P
Proofpoint News Feed
aimingoo的专栏
aimingoo的专栏
G
Google Developers Blog
T
The Blog of Author Tim Ferriss
博客园 - 叶小钗
I
Intezer
Martin Fowler
Martin Fowler
MongoDB | Blog
MongoDB | Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
ThreatConnect
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
小众软件
小众软件
T
The Exploit Database - CXSecurity.com
H
Help Net Security
T
Tenable Blog
WordPress大学
WordPress大学
F
Future of Privacy Forum
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
NISL@THU
NISL@THU
The Register - Security
The Register - Security
A
About on SuperTechFans
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
MyScale Blog
MyScale Blog
Malwarebytes
Malwarebytes
博客园_首页
T
Threatpost
C
CERT Recently Published Vulnerability Notes
Know Your Adversary
Know Your Adversary
T
Threat Research - Cisco Blogs
V
Vulnerabilities – Threatpost
C
CXSECURITY Database RSS Feed - CXSecurity.com
Blog — PlanetScale
Blog — PlanetScale
Recorded Future
Recorded Future
大猫的无限游戏
大猫的无限游戏
K
Kaspersky official blog
月光博客
月光博客
Jina AI
Jina AI
S
Securelist
Hugging Face - Blog
Hugging Face - Blog
G
GRAHAM CLULEY
腾讯CDC
S
Secure Thoughts
V
V2EX - 技术

Recent Commits to openclaw:main

fix(build): support Windows UI builds · openclaw/openclaw@0bb9b42 fix(ui): scope chat session picker to active agent (#85965) · openclaw/openclaw@70c7d6f [codex] improve iOS realtime talk mode (#86355) · openclaw/openclaw@9ca52ce fix(scripts): dedupe docker lane resources · openclaw/openclaw@5e94469 docs: add code size guidance · openclaw/openclaw@9a60fcf fix(test): avoid source gateway import in rpc walk · openclaw/openclaw@e9b8a6e docs: add bugfix changelog entries · openclaw/openclaw@f950132 Fix heartbeat response loop guard (#86324) (#86357) · openclaw/openclaw@e2c174e fix(memory-core): filter REM dreaming candidates to light-staged entr… · openclaw/openclaw@8b42771 fix(telegram): propagate forum topic names into agent context (#86299) fix(slack): keep downloaded files out of reply media (#86318) · openclaw/openclaw@2fcd481 fix(cron): accept plus durations for one-shot jobs (#86341) · openclaw/openclaw@9239f94 fix(plugins): clear metadata memo at lifecycle boundaries · openclaw/openclaw@e7c696a chore(skills): normalize release skill routing · openclaw/openclaw@4737e19 docs(release): require early performance regression check · openclaw/openclaw@0336938 fix(qa): capture Windows gateway metrics · openclaw/openclaw@9afbfc1 feat(qa): add coverage scenario matching · openclaw/openclaw@a1fe86a fix(perf): avoid duplicate docker package ui build build: enable modern TypeScript module syntax · openclaw/openclaw@bbc1772 ci: include performance evidence in release validation fix(providers): stream ordinary tool-like prose promptly fix(perf): harden gateway restart bench exits · openclaw/openclaw@82bbcf6 fix(gateway): gate talk secret bootstrap handoff (#85690) · openclaw/openclaw@c791e42 fix: suppress async media incomplete-turn errors (#85933) · openclaw/openclaw@35dcd42 migrate auth credentials · openclaw/openclaw@f036bac fix migrate auth lint · openclaw/openclaw@50e6cb0 fix migrate supported auth imports · openclaw/openclaw@44bb2be fix migrate auth opt-out precedence · openclaw/openclaw@2016a51 honor migrate auth opt-out in plan · openclaw/openclaw@17edec7 address migrate auth review comments · openclaw/openclaw@0a98c2d fix ci blockers for migrate auth docs: add migrate auth changelog (#85667) · openclaw/openclaw@f7fcbdb fix(scripts): avoid duplicate install smoke ui build · openclaw/openclaw@b1b2841 fix(telegram): preserve inbound text entities (#83873) · openclaw/openclaw@b552919 chore: ignore Python bytecode caches · openclaw/openclaw@b6b2755 fix: make autoreview progress visible · openclaw/openclaw@236edb2 ci(release): fix plugin prerelease extension batch invocation test(telegram): provide topic cache store in message context harness · openclaw/openclaw@ff1fde1 test(agents): complete provider runtime test mocks · openclaw/openclaw@be8cd12 test(telegram): type topic cache harness store · openclaw/openclaw@84ab206 test(agents): sync provider runtime mocks · openclaw/openclaw@a289dd9 refactor: keep plain text tool-call promotion private (#86374) · openclaw/openclaw@c3ab2de fix(discord): suppress self-reply prompt echoes (#86238) docs: clarify config migration policy · openclaw/openclaw@c44367f fix(perf): fail startup bench on early gateway exit · openclaw/openclaw@a8fc28c fix: prevent plain text tool call leaks (#86222) · openclaw/openclaw@cd62780 fix: handle npm min-release-age in installers · openclaw/openclaw@316d97c fix(scripts): include ui:build in build-all full and ciArtifacts prof… · openclaw/openclaw@6704d0a fix(e2e): sample Windows kitchen sink gateway RSS · openclaw/openclaw@73189e3 fix(cron): respect isolated target and error on missing remove id (#8… · openclaw/openclaw@6709f4e fix(pi-embedded-runner): propagate trigger-derived priority to the gl… · openclaw/openclaw@0580f57 fix(cli): suppress self-update version warnings · openclaw/openclaw@e2bd20f fix: preserve webchat source reply details · openclaw/openclaw@aa50c51 docs: replace OpenClaw docs skill and add plugin permissions guide · openclaw/openclaw@0dabb70 fix(codex): preserve source reply mode for active runs (#86325) · openclaw/openclaw@b962110 fix: make compaction reinjection opt-in · openclaw/openclaw@ab910f8 fix codex usage-limit recovery copy (#86305) · openclaw/openclaw@c3c8a65 feat(ui): add ephemeral Activity tab · openclaw/openclaw@3dd0e8e fix(tests): harden native macos plugin proof · openclaw/openclaw@a5d5604 fix(commitments): serialize load-modify-save with in-process queue + … · openclaw/openclaw@d3c293d Fail Codex compaction at the Codex boundary (#85958) · openclaw/openclaw@dd47e47 fix(docker): restore config parent ownership · openclaw/openclaw@908b894 docs: clarify config default review policy (#86329) · openclaw/openclaw@3a03dd5 docs: clean changelog script entries · openclaw/openclaw@0eead19 fix(scripts): budget restart benchmark timeouts · openclaw/openclaw@5bd5509 fix: align ui vitest config assertion · openclaw/openclaw@730fd19 fix: route unit ui vitest targets narrowly · openclaw/openclaw@777402e fix: route explicit ui vitest targets narrowly · openclaw/openclaw@56a383c fix(android): harden play media permission removal fix(webchat): stabilize live transcript run state · openclaw/openclaw@119a01c fix(scripts): fail restart benchmark regressions · openclaw/openclaw@95d1b39 fix(openai): scope external codex auth to realtime fix(openai): prefer codex auth for GPT realtime · openclaw/openclaw@48c4f57 fix(openai): discover codex cli auth for provider checks · openclaw/openclaw@4656275 fix(android): keep talk mode on realtime relay · openclaw/openclaw@70614f8 test(android): add gateway connect adb probe · openclaw/openclaw@d7aa1f3 fix(android): stabilize realtime talk connection state · openclaw/openclaw@ffb02a5 test(android): add voice mode adb e2e harness · openclaw/openclaw@e52a3b3 fix(ci): stabilize deadcode and catalog checks · openclaw/openclaw@3db1508 fix(scripts): prebuild gateway cpu bench · openclaw/openclaw@ca70015 fix(e2e): harden bundled lifecycle probe on Windows · openclaw/openclaw@4798264 test(e2e): sample kitchen sink rpc peak rss · openclaw/openclaw@60c0f24 fix(scripts): remove stale deadcode allowlist entries · openclaw/openclaw@ea3bb92 fix(telegram): route polling diagnostics away from errors · openclaw/openclaw@b5c1199 fix(plugins): support linked source checkouts on Windows · openclaw/openclaw@793e300 fix(gateway): back off session tool mirrors under pressure (#84846) · openclaw/openclaw@42bdc94 fix(config): skip shell env fallback on Windows (#85739) · openclaw/openclaw@06bf302 fix(gateway): avoid duplicate session message broadcasts · openclaw/openclaw@1459044 fix: repair anchorless iMessage watch payloads · openclaw/openclaw@f37fbc9 fix(cli): route node status hints to stdout (#85780) · openclaw/openclaw@749692e fix(oc-path): support deep config edits (#86060) · openclaw/openclaw@3a72a30 fix(config): quiet benign metadata anomaly output · openclaw/openclaw@f3f4f29 fix(test): fail multi-node update regressions · openclaw/openclaw@732cf54 fix(google-vertex): support production ADC modes (#83971) · openclaw/openclaw@f09b4eb test(e2e): expose corrupt plugin deps smoke · openclaw/openclaw@fa3ff4d fix(codex): log app-server approval promotion trigger · openclaw/openclaw@d9af23f test(e2e): harden multi-node update smoke Clean up browser MCP subprocess tree (#85832) · openclaw/openclaw@8dc6b4d fix(agents): log warnings instead of swallowing subagent errors (#82943) · openclaw/openclaw@907bc03 fix(compaction): preserve partial summary on mid-chain chunk failure … · openclaw/openclaw@f0061dd
Fix local embedding worker safety (#85348) · openclaw/openclaw@7ff29a9
osolmaz · 2026-05-25 · via Recent Commits to openclaw:main

@@ -12,6 +12,7 @@ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi }

1212

import "./test-runtime-mocks.js";

1313

import type { MemoryIndexManager } from "./index.js";

1414

import { closeAllMemorySearchManagers, getMemorySearchManager } from "./index.js";

15+

import { LOCAL_EMBEDDING_WORKER_ERROR_CODES } from "./manager-local-worker-errors.js";

1516

import { closeMemoryIndexManagersForAgent, EMBEDDING_PROBE_CACHE_TTL_MS } from "./manager.js";

1617

import {

1718

DEFAULT_LOCAL_MODEL,

@@ -34,6 +35,14 @@ let providerCloseGate: Promise<void> | null = null;

3435

let providerCalls: Array<{ provider?: string; model?: string; outputDimensionality?: number }> = [];

3536

let forceNoProvider = false;

363738+

function createLocalWorkerExitError(): Error {

39+

return Object.assign(new Error("Local embedding worker exited unexpectedly (exit code 134)"), {

40+

code: LOCAL_EMBEDDING_WORKER_ERROR_CODES.exited,

41+

reason: "exit",

42+

exitCode: 134,

43+

});

44+

}

45+3746

vi.mock("./embeddings.js", () => {

3847

const embedText = (text: string) => {

3948

const lower = text.toLowerCase();

@@ -44,6 +53,10 @@ vi.mock("./embeddings.js", () => {

4453

return [alpha, beta, image, audio];

4554

};

4655

return {

56+

resolveEmbeddingProviderFallbackModel: (providerId: string, fallbackSourceModel: string) =>

57+

providerId === "gemini" || providerId === "fallback-provider"

58+

? `${providerId}-embed`

59+

: fallbackSourceModel,

4760

createEmbeddingProvider: async (options: {

4861

provider?: string;

4962

model?: string;

@@ -61,7 +74,10 @@ vi.mock("./embeddings.js", () => {

6174

providerUnavailableReason: "No API key found for provider",

6275

};

6376

}

64-

const providerId = options.provider === "gemini" ? "gemini" : "mock";

77+

const providerId =

78+

options.provider === "gemini" || options.provider === "fallback-provider"

79+

? options.provider

80+

: "mock";

6581

const model = options.model ?? "mock-embed";

6682

return {

6783

requestedProvider: options.provider ?? "openai",

@@ -81,7 +97,7 @@ vi.mock("./embeddings.js", () => {

8197

embedBatchCalls += 1;

8298

return texts.map(embedText);

8399

},

84-

...(providerId === "gemini"

100+

...(providerId === "gemini" || providerId === "fallback-provider"

85101

? {

86102

embedBatchInputs: async (

87103

inputs: Array<{

@@ -112,12 +128,12 @@ vi.mock("./embeddings.js", () => {

112128

}

113129

: {}),

114130

},

115-

...(providerId === "gemini"

131+

...(providerId === "gemini" || providerId === "fallback-provider"

116132

? {

117133

runtime: {

118-

id: "gemini",

134+

id: providerId,

119135

cacheKeyData: {

120-

provider: "gemini",

136+

provider: providerId,

121137

baseUrl: "https://generativelanguage.googleapis.com/v1beta",

122138

model,

123139

outputDimensionality: options.outputDimensionality,

@@ -242,7 +258,8 @@ describe("memory index", () => {

242258

extraPaths?: string[];

243259

sources?: Array<"memory" | "sessions">;

244260

sessionMemory?: boolean;

245-

provider?: "openai" | "gemini";

261+

provider?: "openai" | "gemini" | "fallback-provider";

262+

fallback?: "none" | "gemini" | "fallback-provider";

246263

model?: string;

247264

outputDimensionality?: number;

248265

multimodal?: {

@@ -263,6 +280,7 @@ describe("memory index", () => {

263280

memorySearch: {

264281

provider: params.provider ?? "openai",

265282

model: params.model ?? "mock-embed",

283+

fallback: params.fallback,

266284

outputDimensionality: params.outputDimensionality,

267285

store: { path: params.storePath, vector: { enabled: params.vectorEnabled ?? false } },

268286

// Perf: keep test indexes to a single chunk to reduce sqlite work.

@@ -577,6 +595,144 @@ describe("memory index", () => {

577595

);

578596

});

579597598+

it("clears cached embedding probe readiness when local embeddings degrade", async () => {

599+

const cfg = createCfg({ storePath: path.join(workspaceDir, "index-probe-degraded.sqlite") });

600+

const manager = await getPersistentManager(cfg);

601+602+

await expect(manager.probeEmbeddingAvailability()).resolves.toEqual({ ok: true });

603+

expect(manager.getCachedEmbeddingAvailability()?.ok).toBe(true);

604+

(

605+

manager as unknown as {

606+

provider: {

607+

id: string;

608+

model: string;

609+

embedQuery: (text: string) => Promise<number[]>;

610+

embedBatch: (texts: string[]) => Promise<number[][]>;

611+

close: () => Promise<void>;

612+

};

613+

}

614+

).provider = {

615+

id: "local",

616+

model: "local-model",

617+

embedQuery: async () => [1, 0],

618+

embedBatch: async (texts: string[]) => texts.map(() => [1, 0]),

619+

close: async () => {},

620+

};

621+622+

(

623+

manager as unknown as {

624+

markLocalEmbeddingProviderDegraded: (err: unknown) => void;

625+

}

626+

).markLocalEmbeddingProviderDegraded(createLocalWorkerExitError());

627+628+

expect(manager.getCachedEmbeddingAvailability()).toBeNull();

629+

await expect(manager.probeEmbeddingAvailability()).resolves.toMatchObject({

630+

ok: false,

631+

error: expect.stringContaining("Local embeddings degraded"),

632+

});

633+

});

634+635+

it("activates configured fallback when local embeddings degrade during search", async () => {

636+

const cfg = createCfg({

637+

storePath: path.join(workspaceDir, "index-search-degraded-fallback.sqlite"),

638+

fallback: "fallback-provider",

639+

hybrid: { enabled: true, vectorWeight: 0.5, textWeight: 0.5 },

640+

});

641+

const manager = await getPersistentManager(cfg);

642+643+

await manager.sync({ reason: "test" });

644+

const callsBeforeSearch = providerCalls.length;

645+

(

646+

manager as unknown as {

647+

provider: {

648+

id: string;

649+

model: string;

650+

embedQuery: () => Promise<number[]>;

651+

embedBatch: (texts: string[]) => Promise<number[][]>;

652+

close: () => Promise<void>;

653+

};

654+

}

655+

).provider = {

656+

id: "local",

657+

model: "mock-embed",

658+

embedQuery: async () => {

659+

throw createLocalWorkerExitError();

660+

},

661+

embedBatch: async (texts: string[]) => texts.map(() => [1, 0, 0, 0]),

662+

close: async () => {},

663+

};

664+665+

const results = await manager.search("alpha");

666+667+

expect(results.length).toBeGreaterThan(0);

668+

const resultKeys = results.map(

669+

(result) => `${result.source}:${result.path}:${result.startLine}:${result.endLine}`,

670+

);

671+

expect(new Set(resultKeys).size).toBe(resultKeys.length);

672+

expect(providerCalls.slice(callsBeforeSearch).map((call) => call.provider)).toContain(

673+

"fallback-provider",

674+

);

675+

expect(

676+

(

677+

manager as unknown as {

678+

provider: { id: string } | null;

679+

}

680+

).provider?.id,

681+

).toBe("fallback-provider");

682+

});

683+684+

it("activates configured fallback after probe-time local degradation", async () => {

685+

const cfg = createCfg({

686+

storePath: path.join(workspaceDir, "index-probe-degraded-fallback.sqlite"),

687+

fallback: "fallback-provider",

688+

hybrid: { enabled: true, vectorWeight: 0.5, textWeight: 0.5 },

689+

});

690+

const manager = await getPersistentManager(cfg);

691+692+

await manager.sync({ reason: "test" });

693+

(

694+

manager as unknown as {

695+

provider: {

696+

id: string;

697+

model: string;

698+

embedQuery: () => Promise<number[]>;

699+

embedBatch: () => Promise<number[][]>;

700+

close: () => Promise<void>;

701+

};

702+

}

703+

).provider = {

704+

id: "local",

705+

model: "mock-embed",

706+

embedQuery: async () => {

707+

throw createLocalWorkerExitError();

708+

},

709+

embedBatch: async () => {

710+

throw createLocalWorkerExitError();

711+

},

712+

close: async () => {},

713+

};

714+

const callsBeforeSearch = providerCalls.length;

715+716+

await expect(manager.probeEmbeddingAvailability()).resolves.toMatchObject({

717+

ok: false,

718+

error: expect.stringContaining("Local embedding worker exited"),

719+

});

720+721+

const results = await manager.search("alpha");

722+723+

expect(results.length).toBeGreaterThan(0);

724+

expect(providerCalls.slice(callsBeforeSearch).map((call) => call.provider)).toContain(

725+

"fallback-provider",

726+

);

727+

expect(

728+

(

729+

manager as unknown as {

730+

provider: { id: string } | null;

731+

}

732+

).provider?.id,

733+

).toBe("fallback-provider");

734+

});

735+580736

it("streams embedding cache rows during safe reindex", async () => {

581737

vi.stubEnv("OPENCLAW_TEST_MEMORY_UNSAFE_REINDEX", "0");

582738

type EmbeddingCacheRow = {