






























@@ -7,6 +7,9 @@ type TestSessionStore = {
77save(record: Record<string, unknown>): Promise<void>;
88};
9910+const DOCUMENTED_OPENCLAW_BRIDGE_COMMAND =
11+"env OPENCLAW_HIDE_BANNER=1 OPENCLAW_SUPPRESS_NOTES=1 openclaw acp --url ws://127.0.0.1:18789 --token-file ~/.openclaw/gateway.token --session agent:main:main";
12+1013function makeRuntime(
1114baseStore: TestSessionStore,
1215options: Partial<ConstructorParameters<typeof AcpxRuntime>[0]> = {},
@@ -17,11 +20,15 @@ function makeRuntime(
1720close: AcpRuntime["close"];
1821ensureSession: AcpRuntime["ensureSession"];
1922getStatus: NonNullable<AcpRuntime["getStatus"]>;
23+isHealthy(): boolean;
24+probeAvailability(): Promise<void>;
2025};
2126bridgeSafeDelegate: {
2227close: AcpRuntime["close"];
2328ensureSession: AcpRuntime["ensureSession"];
2429getStatus: NonNullable<AcpRuntime["getStatus"]>;
30+isHealthy(): boolean;
31+probeAvailability(): Promise<void>;
2532};
2633} {
2734const runtime = new AcpxRuntime({
@@ -48,6 +55,8 @@ function makeRuntime(
4855close: AcpRuntime["close"];
4956ensureSession: AcpRuntime["ensureSession"];
5057getStatus: NonNullable<AcpRuntime["getStatus"]>;
58+isHealthy(): boolean;
59+probeAvailability(): Promise<void>;
5160};
5261}
5362).delegate,
@@ -57,6 +66,8 @@ function makeRuntime(
5766close: AcpRuntime["close"];
5867ensureSession: AcpRuntime["ensureSession"];
5968getStatus: NonNullable<AcpRuntime["getStatus"]>;
69+isHealthy(): boolean;
70+probeAvailability(): Promise<void>;
6071};
6172}
6273).bridgeSafeDelegate,
@@ -130,7 +141,7 @@ describe("AcpxRuntime fresh reset wrapper", () => {
130141discardPersistentState: true,
131142});
132143expect(await wrappedStore.load("agent:codex:acp:binding:test")).toBeUndefined();
133-expect(baseStore.load).not.toHaveBeenCalled();
144+expect(baseStore.load).toHaveBeenCalledOnce();
134145});
135146136147it("routes openclaw ensureSession through the bridge-safe delegate when MCP servers are configured", async () => {
@@ -292,4 +303,141 @@ describe("AcpxRuntime fresh reset wrapper", () => {
292303expect(bridgeEnsure).toHaveBeenCalledOnce();
293304expect(defaultEnsure).not.toHaveBeenCalled();
294305});
306+307+it("uses the bridge-safe delegate for documented env-wrapped openclaw bridge commands", async () => {
308+const baseStore: TestSessionStore = {
309+load: vi.fn(async () => undefined),
310+save: vi.fn(async () => {}),
311+};
312+313+const { runtime, delegate, bridgeSafeDelegate } = makeRuntime(baseStore, {
314+mcpServers: [{ name: "tools", command: "mcp-tools" }] as never,
315+agentRegistry: {
316+resolve: (agentName: string) =>
317+agentName === "openclaw" ? DOCUMENTED_OPENCLAW_BRIDGE_COMMAND : agentName,
318+list: () => ["codex", "openclaw"],
319+},
320+});
321+const defaultEnsure = vi.spyOn(delegate, "ensureSession").mockResolvedValue({
322+sessionKey: "agent:openclaw:acp:test",
323+backend: "acpx",
324+runtimeSessionName: "default",
325+});
326+const bridgeEnsure = vi.spyOn(bridgeSafeDelegate, "ensureSession").mockResolvedValue({
327+sessionKey: "agent:openclaw:acp:test",
328+backend: "acpx",
329+runtimeSessionName: "bridge",
330+});
331+332+const result = await runtime.ensureSession({
333+sessionKey: "agent:openclaw:acp:test",
334+agent: "openclaw",
335+mode: "persistent",
336+});
337+338+expect(result.runtimeSessionName).toBe("bridge");
339+expect(bridgeEnsure).toHaveBeenCalledOnce();
340+expect(defaultEnsure).not.toHaveBeenCalled();
341+});
342+343+it("uses the bridge-safe delegate for local node openclaw entrypoints", async () => {
344+const baseStore: TestSessionStore = {
345+load: vi.fn(async () => undefined),
346+save: vi.fn(async () => {}),
347+};
348+349+const { runtime, delegate, bridgeSafeDelegate } = makeRuntime(baseStore, {
350+mcpServers: [{ name: "tools", command: "mcp-tools" }] as never,
351+agentRegistry: {
352+resolve: (agentName: string) =>
353+agentName === "openclaw" ? "env OPENCLAW_HIDE_BANNER=1 node openclaw.mjs acp" : agentName,
354+list: () => ["codex", "openclaw"],
355+},
356+});
357+const defaultEnsure = vi.spyOn(delegate, "ensureSession").mockResolvedValue({
358+sessionKey: "agent:openclaw:acp:test",
359+backend: "acpx",
360+runtimeSessionName: "default",
361+});
362+const bridgeEnsure = vi.spyOn(bridgeSafeDelegate, "ensureSession").mockResolvedValue({
363+sessionKey: "agent:openclaw:acp:test",
364+backend: "acpx",
365+runtimeSessionName: "bridge",
366+});
367+368+const result = await runtime.ensureSession({
369+sessionKey: "agent:openclaw:acp:test",
370+agent: "openclaw",
371+mode: "persistent",
372+});
373+374+expect(result.runtimeSessionName).toBe("bridge");
375+expect(bridgeEnsure).toHaveBeenCalledOnce();
376+expect(defaultEnsure).not.toHaveBeenCalled();
377+});
378+379+it("routes follow-up calls by persisted agent command before current config", async () => {
380+const baseStore: TestSessionStore = {
381+load: vi.fn(async () => ({
382+acpxRecordId: "agent:openclaw:acp:test",
383+agentCommand: DOCUMENTED_OPENCLAW_BRIDGE_COMMAND,
384+})),
385+save: vi.fn(async () => {}),
386+};
387+388+const { runtime, delegate, bridgeSafeDelegate } = makeRuntime(baseStore, {
389+mcpServers: [{ name: "tools", command: "mcp-tools" }] as never,
390+agentRegistry: {
391+resolve: (agentName: string) => (agentName === "openclaw" ? "codex" : agentName),
392+list: () => ["codex", "openclaw"],
393+},
394+});
395+const defaultStatus = vi.spyOn(delegate, "getStatus").mockResolvedValue({
396+summary: "default",
397+});
398+const bridgeStatus = vi.spyOn(bridgeSafeDelegate, "getStatus").mockResolvedValue({
399+summary: "bridge",
400+});
401+402+const status = await runtime.getStatus({
403+handle: {
404+sessionKey: "agent:openclaw:acp:test",
405+backend: "acpx",
406+runtimeSessionName: "agent:openclaw:acp:test",
407+},
408+});
409+410+expect(status.summary).toBe("bridge");
411+expect(bridgeStatus).toHaveBeenCalledOnce();
412+expect(defaultStatus).not.toHaveBeenCalled();
413+});
414+415+it("probes through the bridge-safe delegate when probeAgent resolves to openclaw bridge", async () => {
416+const baseStore: TestSessionStore = {
417+load: vi.fn(async () => undefined),
418+save: vi.fn(async () => {}),
419+};
420+421+const { runtime, delegate, bridgeSafeDelegate } = makeRuntime(baseStore, {
422+mcpServers: [{ name: "tools", command: "mcp-tools" }] as never,
423+probeAgent: "openclaw",
424+agentRegistry: {
425+resolve: (agentName: string) =>
426+agentName === "openclaw" ? DOCUMENTED_OPENCLAW_BRIDGE_COMMAND : agentName,
427+list: () => ["codex", "openclaw"],
428+},
429+});
430+const defaultProbe = vi.spyOn(delegate, "probeAvailability").mockResolvedValue(undefined);
431+const bridgeProbe = vi
432+.spyOn(bridgeSafeDelegate, "probeAvailability")
433+.mockResolvedValue(undefined);
434+vi.spyOn(delegate, "isHealthy").mockReturnValue(false);
435+vi.spyOn(bridgeSafeDelegate, "isHealthy").mockReturnValue(true);
436+437+await runtime.probeAvailability();
438+439+expect(runtime.isHealthy()).toBe(true);
440+expect(bridgeProbe).toHaveBeenCalledOnce();
441+expect(defaultProbe).not.toHaveBeenCalled();
442+});
295443});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。