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

推荐订阅源

Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
Vercel News
Vercel News
F
Fortinet All Blogs
月光博客
月光博客
G
Google Developers Blog
博客园 - Franky
GbyAI
GbyAI
The Cloudflare Blog
I
InfoQ
雷峰网
雷峰网
WordPress大学
WordPress大学
罗磊的独立博客
大猫的无限游戏
大猫的无限游戏
T
The Blog of Author Tim Ferriss
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 聂微东
小众软件
小众软件
腾讯CDC
B
Blog
量子位
V
V2EX
S
SegmentFault 最新的问题
Google DeepMind News
Google DeepMind News

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
perf: reuse gateway session and plugin metadata paths · o...
steipete · 2026-05-30 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -160,6 +160,7 @@ export function loadEnabledBundleAgentSettingsSnapshot(params: {

160160

const embeddedAgentMcp = loadEmbeddedAgentMcpConfig({

161161

workspaceDir,

162162

cfg: config,

163+

manifestRegistry: metadataSnapshot.manifestRegistry,

163164

});

164165

for (const diagnostic of embeddedAgentMcp.diagnostics) {

165166

log.warn(`bundle MCP skipped for ${diagnostic.pluginId}: ${diagnostic.message}`);

Original file line numberDiff line numberDiff line change

@@ -6,6 +6,7 @@ import {

66

type BundleMcpDiagnostic,

77

type BundleMcpServerConfig,

88

} from "../plugins/bundle-mcp.js";

9+

import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";

910
1011

type MergedBundleMcpConfig = {

1112

config: BundleMcpConfig;

@@ -46,11 +47,13 @@ export function toCliBundleMcpServerConfig(server: BundleMcpServerConfig): Bundl

4647

export function loadMergedBundleMcpConfig(params: {

4748

workspaceDir: string;

4849

cfg?: OpenClawConfig;

50+

manifestRegistry?: Pick<PluginManifestRegistry, "plugins">;

4951

mapConfiguredServer?: BundleMcpServerMapper;

5052

}): MergedBundleMcpConfig {

5153

const bundleMcp = loadEnabledBundleMcpConfig({

5254

workspaceDir: params.workspaceDir,

5355

cfg: params.cfg,

56+

manifestRegistry: params.manifestRegistry,

5457

});

5558

const configuredMcp = normalizeConfiguredMcpServers(params.cfg?.mcp?.servers);

5659

const mapConfiguredServer = params.mapConfiguredServer ?? ((server) => server);

Original file line numberDiff line numberDiff line change

@@ -1,5 +1,6 @@

11

import type { OpenClawConfig } from "../config/types.openclaw.js";

22

import type { BundleMcpDiagnostic, BundleMcpServerConfig } from "../plugins/bundle-mcp.js";

3+

import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";

34

import { loadMergedBundleMcpConfig } from "./bundle-mcp-config.js";

45
56

type EmbeddedAgentMcpConfig = {

@@ -10,10 +11,12 @@ type EmbeddedAgentMcpConfig = {

1011

export function loadEmbeddedAgentMcpConfig(params: {

1112

workspaceDir: string;

1213

cfg?: OpenClawConfig;

14+

manifestRegistry?: Pick<PluginManifestRegistry, "plugins">;

1315

}): EmbeddedAgentMcpConfig {

1416

const bundleMcp = loadMergedBundleMcpConfig({

1517

workspaceDir: params.workspaceDir,

1618

cfg: params.cfg,

19+

manifestRegistry: params.manifestRegistry,

1720

});

1821
1922

return {

Original file line numberDiff line numberDiff line change

@@ -901,7 +901,9 @@ async function persistResolvedSessionEntry(params: {

901901

export async function updateSessionStoreEntry(params: {

902902

storePath: string;

903903

sessionKey: string;

904-

update: (entry: SessionEntry) => Promise<Partial<SessionEntry> | null>;

904+

update: (

905+

entry: SessionEntry,

906+

) => Promise<Partial<SessionEntry> | null> | Partial<SessionEntry> | null;

905907

skipMaintenance?: boolean;

906908

takeCacheOwnership?: boolean;

907909

}): Promise<SessionEntry | null> {

Original file line numberDiff line numberDiff line change

@@ -550,6 +550,62 @@ describe("gateway agent handler", () => {

550550

});

551551

});

552552
553+

it("uses single-entry persistence for ordinary gateway admission touches", async () => {

554+

mockMainSessionEntry({});

555+

let capturedOptions:

556+

| {

557+

resolveSingleEntryPersistence?: (result: unknown) => unknown;

558+

}

559+

| undefined;

560+

let persistedResult: unknown;

561+

mocks.updateSessionStore.mockImplementation(async (_path, updater, opts) => {

562+

const store: Record<string, Record<string, unknown>> = {

563+

"agent:main:main": buildExistingMainStoreEntry(),

564+

};

565+

persistedResult = await updater(store);

566+

capturedOptions = opts;

567+

return persistedResult;

568+

});

569+

mocks.agentCommand.mockResolvedValue({

570+

payloads: [{ text: "ok" }],

571+

meta: { durationMs: 100 },

572+

});

573+
574+

await runMainAgent("hi", "idem-single-entry-persist");

575+
576+

expect(capturedOptions?.resolveSingleEntryPersistence?.(persistedResult)).toMatchObject({

577+

sessionKey: "agent:main:main",

578+

entry: persistedResult,

579+

});

580+

});

581+
582+

it("disables single-entry persistence when admission prunes legacy store keys", async () => {

583+

mockMainSessionEntry({});

584+

let capturedOptions:

585+

| {

586+

resolveSingleEntryPersistence?: (result: unknown) => unknown;

587+

}

588+

| undefined;

589+

let persistedResult: unknown;

590+

mocks.updateSessionStore.mockImplementation(async (_path, updater, opts) => {

591+

const store: Record<string, Record<string, unknown>> = {

592+

"agent:main:main": buildExistingMainStoreEntry({ updatedAt: 100 }),

593+

"Agent:main:main": buildExistingMainStoreEntry({ updatedAt: 50 }),

594+

};

595+

persistedResult = await updater(store);

596+

capturedOptions = opts;

597+

return persistedResult;

598+

});

599+

mocks.agentCommand.mockResolvedValue({

600+

payloads: [{ text: "ok" }],

601+

meta: { durationMs: 100 },

602+

});

603+
604+

await runMainAgent("hi", "idem-single-entry-legacy-prune");

605+
606+

expect(capturedOptions?.resolveSingleEntryPersistence?.(persistedResult)).toBeUndefined();

607+

});

608+
553609

it("preserves ACP metadata from the current stored session entry", async () => {

554610

const existingAcpMeta = {

555611

backend: "acpx",

Original file line numberDiff line numberDiff line change

@@ -144,6 +144,7 @@ import {

144144

loadGatewaySessionRow,

145145

loadSessionEntry,

146146

migrateAndPruneGatewaySessionStoreKey,

147+

resolveGatewaySessionStoreTarget,

147148

resolveGatewayModelSupportsImages,

148149

resolveSessionModelRef,

149150

} from "../session-utils.js";

@@ -1569,14 +1570,34 @@ export const agentHandlers: GatewayRequestHandlers = {

15691570

if (storePath && !suppressVisibleSessionEffects) {

15701571

const requestedStoreKey = requestedSessionKey;

15711572

let deniedBySendPolicy = false;

1573+

let singleEntryPersistence:

1574+

| {

1575+

sessionKey: string;

1576+

entry: SessionEntry;

1577+

}

1578+

| undefined;

15721579

const persisted = await updateSessionStore(

15731580

storePath,

15741581

(store) => {

1575-

const { primaryKey } = migrateAndPruneGatewaySessionStoreKey({

1582+

const storeKeysBeforeMigration = new Set(Object.keys(store));

1583+

const preMigrationTarget = resolveGatewaySessionStoreTarget({

15761584

cfg,

15771585

key: requestedStoreKey,

15781586

store,

15791587

});

1588+

const hadLegacyStoreKey = preMigrationTarget.storeKeys.some(

1589+

(storeKey) =>

1590+

storeKey !== preMigrationTarget.canonicalKey &&

1591+

Object.prototype.hasOwnProperty.call(store, storeKey),

1592+

);

1593+

const { target, primaryKey } = migrateAndPruneGatewaySessionStoreKey({

1594+

cfg,

1595+

key: requestedStoreKey,

1596+

store,

1597+

});

1598+

const prunedStoreKey = [...storeKeysBeforeMigration].some(

1599+

(storeKey) => !Object.prototype.hasOwnProperty.call(store, storeKey),

1600+

);

15801601

const freshEntry = store[primaryKey];

15811602

patchBuild = buildSessionPatch(freshEntry);

15821603

const effectivePatch =

@@ -1601,9 +1622,21 @@ export const agentHandlers: GatewayRequestHandlers = {

16011622

return merged;

16021623

}

16031624

store[primaryKey] = merged;

1625+

const canonicalKeyChanged = target.canonicalKey !== preMigrationTarget.canonicalKey;

1626+

singleEntryPersistence =

1627+

freshEntry && !hadLegacyStoreKey && !canonicalKeyChanged && !prunedStoreKey

1628+

? {

1629+

sessionKey: primaryKey,

1630+

entry: merged,

1631+

}

1632+

: undefined;

16041633

return merged;

16051634

},

1606-

{ takeCacheOwnership: true, maintenanceConfig: sessionMaintenanceConfig },

1635+

{

1636+

takeCacheOwnership: true,

1637+

maintenanceConfig: sessionMaintenanceConfig,

1638+

resolveSingleEntryPersistence: () => singleEntryPersistence,

1639+

},

16071640

);

16081641

if (persisted) {

16091642

sessionEntry = persisted;

Original file line numberDiff line numberDiff line change

@@ -3,6 +3,7 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";

33

import { matchRootFileOpenFailure, type RootFileOpenFailure } from "../infra/boundary-file-read.js";

44

import { readRootJsonObjectSync } from "../infra/json-files.js";

55

import { normalizePluginsConfig, resolveEffectivePluginActivationState } from "./config-state.js";

6+

import type { PluginManifestRegistry } from "./manifest-registry.js";

67

import type { PluginBundleFormat } from "./manifest-types.js";

78

import { loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry.js";

89

@@ -82,6 +83,7 @@ export function inspectBundleServerRuntimeSupport<TConfig>(params: {

8283

export function loadEnabledBundleConfig<TConfig, TDiagnostic>(params: {

8384

workspaceDir: string;

8485

cfg?: OpenClawConfig;

86+

manifestRegistry?: Pick<PluginManifestRegistry, "plugins">;

8587

createEmptyConfig: () => TConfig;

8688

loadBundleConfig: (params: {

8789

pluginId: string;

@@ -95,11 +97,13 @@ export function loadEnabledBundleConfig<TConfig, TDiagnostic>(params: {

9597

return { config: params.createEmptyConfig(), diagnostics: [] };

9698

}

9799
98-

const registry = loadPluginManifestRegistryForPluginRegistry({

99-

workspaceDir: params.workspaceDir,

100-

config: params.cfg,

101-

includeDisabled: true,

102-

});

100+

const registry =

101+

params.manifestRegistry ??

102+

loadPluginManifestRegistryForPluginRegistry({

103+

workspaceDir: params.workspaceDir,

104+

config: params.cfg,

105+

includeDisabled: true,

106+

});

103107

const diagnostics: TDiagnostic[] = [];

104108

let merged = params.createEmptyConfig();

105109
Original file line numberDiff line numberDiff line change

@@ -126,6 +126,40 @@ describe("loadEnabledBundleMcpConfig", () => {

126126

);

127127

});

128128
129+

it("uses a provided manifest registry instead of rediscovering bundle plugins", async () => {

130+

const homeDir = await tempHarness.createTempDir("openclaw-bundle-mcp-home-");

131+

const workspaceDir = await tempHarness.createTempDir("openclaw-bundle-mcp-workspace-");

132+

const { pluginRoot } = await createBundleProbePlugin(homeDir);

133+
134+

const loaded = loadEnabledBundleMcpConfig({

135+

workspaceDir,

136+

cfg: createEnabledBundleConfig(["bundle-probe"]),

137+

manifestRegistry: {

138+

plugins: [

139+

{

140+

id: "bundle-probe",

141+

origin: "global",

142+

format: "bundle",

143+

bundleFormat: "claude",

144+

channels: [],

145+

providers: [],

146+

cliBackends: [],

147+

skills: [],

148+

hooks: [],

149+

rootDir: await fs.realpath(pluginRoot),

150+

source: "test",

151+

manifestPath: path.join(pluginRoot, ".claude-plugin", "plugin.json"),

152+

},

153+

],

154+

},

155+

});

156+
157+

expectNoDiagnostics(loaded.diagnostics);

158+

expect(loaded.config.mcpServers.bundleProbe).toMatchObject({

159+

command: "node",

160+

});

161+

});

162+
129163

it("merges inline bundle MCP servers and skips disabled bundles", async () => {

130164

await withBundleHomeEnv(

131165

tempHarness,

Original file line numberDiff line numberDiff line change

@@ -17,6 +17,7 @@ import {

1717

mergeBundlePathLists,

1818

normalizeBundlePathList,

1919

} from "./bundle-manifest.js";

20+

import type { PluginManifestRegistry } from "./manifest-registry.js";

2021

import type { PluginBundleFormat } from "./manifest-types.js";

2122
2223

export type BundleMcpServerConfig = Record<string, unknown>;

@@ -290,10 +291,12 @@ export function inspectBundleMcpRuntimeSupport(params: {

290291

export function loadEnabledBundleMcpConfig(params: {

291292

workspaceDir: string;

292293

cfg?: OpenClawConfig;

294+

manifestRegistry?: Pick<PluginManifestRegistry, "plugins">;

293295

}): EnabledBundleMcpConfigResult {

294296

return loadEnabledBundleConfig({

295297

workspaceDir: params.workspaceDir,

296298

cfg: params.cfg,

299+

manifestRegistry: params.manifestRegistry,

297300

createEmptyConfig: () => ({ mcpServers: {} }),

298301

loadBundleConfig: loadBundleMcpConfig,

299302

createDiagnostic: (pluginId, message) => ({ pluginId, message }),