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

推荐订阅源

V
Visual Studio Blog
Y
Y Combinator Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hugging Face - Blog
Hugging Face - Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
L
LangChain Blog
美团技术团队
N
Netflix TechBlog - Medium
量子位
酷 壳 – CoolShell
酷 壳 – CoolShell
B
Blog
博客园 - 司徒正美
爱范儿
爱范儿
D
DataBreaches.Net
月光博客
月光博客
U
Unit 42
B
Blog RSS Feed
Engineering at Meta
Engineering at Meta
Apple Machine Learning Research
Apple Machine Learning Research
Jina AI
Jina AI
MongoDB | Blog
MongoDB | Blog
腾讯CDC

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
fix(acpx): avoid per-session MCP on openclaw bridge · ope...
alexlomt · 2026-04-23 · via Recent Commits to openclaw:main

@@ -7,19 +7,32 @@ type TestSessionStore = {

77

save(record: Record<string, unknown>): Promise<void>;

88

};

9910-

function makeRuntime(baseStore: TestSessionStore): {

10+

function makeRuntime(

11+

baseStore: TestSessionStore,

12+

options: Partial<ConstructorParameters<typeof AcpxRuntime>[0]> = {},

13+

): {

1114

runtime: AcpxRuntime;

1215

wrappedStore: TestSessionStore & { markFresh: (sessionKey: string) => void };

13-

delegate: { close: AcpRuntime["close"] };

16+

delegate: {

17+

close: AcpRuntime["close"];

18+

ensureSession: AcpRuntime["ensureSession"];

19+

getStatus: NonNullable<AcpRuntime["getStatus"]>;

20+

};

21+

bridgeSafeDelegate: {

22+

close: AcpRuntime["close"];

23+

ensureSession: AcpRuntime["ensureSession"];

24+

getStatus: NonNullable<AcpRuntime["getStatus"]>;

25+

};

1426

} {

1527

const runtime = new AcpxRuntime({

1628

cwd: "/tmp",

1729

sessionStore: baseStore,

1830

agentRegistry: {

19-

resolve: () => "codex",

20-

list: () => ["codex"],

31+

resolve: (agentName: string) => (agentName === "openclaw" ? "openclaw acp" : agentName),

32+

list: () => ["codex", "openclaw"],

2133

},

2234

permissionMode: "approve-reads",

35+

...options,

2336

});

24372538

return {

@@ -29,7 +42,24 @@ function makeRuntime(baseStore: TestSessionStore): {

2942

sessionStore: TestSessionStore & { markFresh: (sessionKey: string) => void };

3043

}

3144

).sessionStore,

32-

delegate: (runtime as unknown as { delegate: { close: AcpRuntime["close"] } }).delegate,

45+

delegate: (

46+

runtime as unknown as {

47+

delegate: {

48+

close: AcpRuntime["close"];

49+

ensureSession: AcpRuntime["ensureSession"];

50+

getStatus: NonNullable<AcpRuntime["getStatus"]>;

51+

};

52+

}

53+

).delegate,

54+

bridgeSafeDelegate: (

55+

runtime as unknown as {

56+

bridgeSafeDelegate: {

57+

close: AcpRuntime["close"];

58+

ensureSession: AcpRuntime["ensureSession"];

59+

getStatus: NonNullable<AcpRuntime["getStatus"]>;

60+

};

61+

}

62+

).bridgeSafeDelegate,

3363

};

3464

}

3565

@@ -102,4 +132,164 @@ describe("AcpxRuntime fresh reset wrapper", () => {

102132

expect(await wrappedStore.load("agent:codex:acp:binding:test")).toBeUndefined();

103133

expect(baseStore.load).not.toHaveBeenCalled();

104134

});

135+136+

it("routes openclaw ensureSession through the bridge-safe delegate when MCP servers are configured", async () => {

137+

const baseStore: TestSessionStore = {

138+

load: vi.fn(async () => undefined),

139+

save: vi.fn(async () => {}),

140+

};

141+142+

const { runtime, delegate, bridgeSafeDelegate } = makeRuntime(baseStore, {

143+

mcpServers: [{ name: "tools", command: "mcp-tools" }] as never,

144+

});

145+

const defaultEnsure = vi.spyOn(delegate, "ensureSession").mockResolvedValue({

146+

sessionKey: "agent:codex:acp:test",

147+

backend: "acpx",

148+

runtimeSessionName: "default",

149+

});

150+

const bridgeEnsure = vi.spyOn(bridgeSafeDelegate, "ensureSession").mockResolvedValue({

151+

sessionKey: "agent:openclaw:acp:test",

152+

backend: "acpx",

153+

runtimeSessionName: "bridge",

154+

});

155+156+

const result = await runtime.ensureSession({

157+

sessionKey: "agent:openclaw:acp:test",

158+

agent: "openclaw",

159+

mode: "persistent",

160+

});

161+162+

expect(result.runtimeSessionName).toBe("bridge");

163+

expect(bridgeEnsure).toHaveBeenCalledOnce();

164+

expect(defaultEnsure).not.toHaveBeenCalled();

165+

});

166+167+

it("routes non-openclaw sessions through the default delegate", async () => {

168+

const baseStore: TestSessionStore = {

169+

load: vi.fn(async () => undefined),

170+

save: vi.fn(async () => {}),

171+

};

172+173+

const { runtime, delegate, bridgeSafeDelegate } = makeRuntime(baseStore, {

174+

mcpServers: [{ name: "tools", command: "mcp-tools" }] as never,

175+

});

176+

const defaultEnsure = vi.spyOn(delegate, "ensureSession").mockResolvedValue({

177+

sessionKey: "agent:codex:acp:test",

178+

backend: "acpx",

179+

runtimeSessionName: "default",

180+

});

181+

const bridgeEnsure = vi.spyOn(bridgeSafeDelegate, "ensureSession").mockResolvedValue({

182+

sessionKey: "agent:openclaw:acp:test",

183+

backend: "acpx",

184+

runtimeSessionName: "bridge",

185+

});

186+187+

const result = await runtime.ensureSession({

188+

sessionKey: "agent:codex:acp:test",

189+

agent: "codex",

190+

mode: "persistent",

191+

});

192+193+

expect(result.runtimeSessionName).toBe("default");

194+

expect(defaultEnsure).toHaveBeenCalledOnce();

195+

expect(bridgeEnsure).not.toHaveBeenCalled();

196+

});

197+198+

it("routes handle-based follow-up calls for openclaw sessions through the bridge-safe delegate", async () => {

199+

const baseStore: TestSessionStore = {

200+

load: vi.fn(async () => undefined),

201+

save: vi.fn(async () => {}),

202+

};

203+204+

const { runtime, delegate, bridgeSafeDelegate } = makeRuntime(baseStore, {

205+

mcpServers: [{ name: "tools", command: "mcp-tools" }] as never,

206+

});

207+

const defaultStatus = vi.spyOn(delegate, "getStatus").mockResolvedValue({

208+

summary: "default",

209+

});

210+

const bridgeStatus = vi.spyOn(bridgeSafeDelegate, "getStatus").mockResolvedValue({

211+

summary: "bridge",

212+

});

213+

const handle: Parameters<NonNullable<AcpRuntime["getStatus"]>>[0]["handle"] = {

214+

sessionKey: "agent:openclaw:acp:test",

215+

backend: "acpx",

216+

runtimeSessionName: "openclaw-session-handle",

217+

};

218+219+

const status = await runtime.getStatus({ handle });

220+221+

expect(status.summary).toBe("bridge");

222+

expect(bridgeStatus).toHaveBeenCalledWith({ handle });

223+

expect(defaultStatus).not.toHaveBeenCalled();

224+

});

225+226+

it("keeps MCP-enabled routing when the openclaw agent is overridden to a non-bridge adapter", async () => {

227+

const baseStore: TestSessionStore = {

228+

load: vi.fn(async () => undefined),

229+

save: vi.fn(async () => {}),

230+

};

231+232+

const { runtime, delegate, bridgeSafeDelegate } = makeRuntime(baseStore, {

233+

mcpServers: [{ name: "tools", command: "mcp-tools" }] as never,

234+

agentRegistry: {

235+

resolve: (agentName: string) => (agentName === "openclaw" ? "codex" : agentName),

236+

list: () => ["codex", "openclaw"],

237+

},

238+

});

239+

const defaultEnsure = vi.spyOn(delegate, "ensureSession").mockResolvedValue({

240+

sessionKey: "agent:openclaw:acp:test",

241+

backend: "acpx",

242+

runtimeSessionName: "default",

243+

});

244+

const bridgeEnsure = vi.spyOn(bridgeSafeDelegate, "ensureSession").mockResolvedValue({

245+

sessionKey: "agent:openclaw:acp:test",

246+

backend: "acpx",

247+

runtimeSessionName: "bridge",

248+

});

249+250+

const result = await runtime.ensureSession({

251+

sessionKey: "agent:openclaw:acp:test",

252+

agent: "openclaw",

253+

mode: "persistent",

254+

});

255+256+

expect(result.runtimeSessionName).toBe("default");

257+

expect(defaultEnsure).toHaveBeenCalledOnce();

258+

expect(bridgeEnsure).not.toHaveBeenCalled();

259+

});

260+261+

it("uses the bridge-safe delegate for any agent mapped to the openclaw bridge command", async () => {

262+

const baseStore: TestSessionStore = {

263+

load: vi.fn(async () => undefined),

264+

save: vi.fn(async () => {}),

265+

};

266+267+

const { runtime, delegate, bridgeSafeDelegate } = makeRuntime(baseStore, {

268+

mcpServers: [{ name: "tools", command: "mcp-tools" }] as never,

269+

agentRegistry: {

270+

resolve: (agentName: string) => (agentName === "codex" ? "openclaw acp" : agentName),

271+

list: () => ["codex", "openclaw"],

272+

},

273+

});

274+

const defaultEnsure = vi.spyOn(delegate, "ensureSession").mockResolvedValue({

275+

sessionKey: "agent:codex:acp:test",

276+

backend: "acpx",

277+

runtimeSessionName: "default",

278+

});

279+

const bridgeEnsure = vi.spyOn(bridgeSafeDelegate, "ensureSession").mockResolvedValue({

280+

sessionKey: "agent:codex:acp:test",

281+

backend: "acpx",

282+

runtimeSessionName: "bridge",

283+

});

284+285+

const result = await runtime.ensureSession({

286+

sessionKey: "agent:codex:acp:test",

287+

agent: "codex",

288+

mode: "persistent",

289+

});

290+291+

expect(result.runtimeSessionName).toBe("bridge");

292+

expect(bridgeEnsure).toHaveBeenCalledOnce();

293+

expect(defaultEnsure).not.toHaveBeenCalled();

294+

});

105295

});