

























@@ -1,6 +1,9 @@
11import { beforeEach, describe, expect, it, vi } from "vitest";
22import type { OpenClawConfig } from "../config/config.js";
3-import { buildConfiguredAcpSessionKey } from "./persistent-bindings.types.js";
3+import {
4+buildConfiguredAcpSessionKey,
5+type ConfiguredAcpBindingSpec,
6+} from "./persistent-bindings.types.js";
4758const managerMocks = vi.hoisted(() => ({
69resolveSession: vi.fn(),
@@ -41,6 +44,7 @@ const baseCfg = {
4144},
4245} satisfies OpenClawConfig;
434647+let ensureConfiguredAcpBindingSession: typeof import("./persistent-bindings.lifecycle.js").ensureConfiguredAcpBindingSession;
4448let resetAcpSessionInPlace: typeof import("./persistent-bindings.lifecycle.js").resetAcpSessionInPlace;
45494650beforeEach(async () => {
@@ -54,7 +58,127 @@ beforeEach(async () => {
5458managerMocks.updateSessionRuntimeOptions.mockReset().mockResolvedValue(undefined);
5559sessionMetaMocks.readAcpSessionEntry.mockReset().mockReturnValue(undefined);
5660resolveMocks.resolveConfiguredAcpBindingSpecBySessionKey.mockReset().mockReturnValue(null);
57-({ resetAcpSessionInPlace } = await import("./persistent-bindings.lifecycle.js"));
61+({ ensureConfiguredAcpBindingSession, resetAcpSessionInPlace } =
62+await import("./persistent-bindings.lifecycle.js"));
63+});
64+65+function createPersistentSpec(
66+overrides: Partial<ConfiguredAcpBindingSpec> = {},
67+): ConfiguredAcpBindingSpec {
68+return {
69+channel: "discord",
70+accountId: "default",
71+conversationId: "1478836151241412759",
72+agentId: "codex",
73+mode: "persistent",
74+ ...overrides,
75+};
76+}
77+78+function mockReadySession(params: {
79+spec: ConfiguredAcpBindingSpec;
80+cwd: string;
81+state?: "idle" | "running" | "error";
82+}) {
83+const sessionKey = buildConfiguredAcpSessionKey(params.spec);
84+managerMocks.resolveSession.mockReturnValue({
85+kind: "ready",
86+ sessionKey,
87+meta: {
88+backend: "acpx",
89+agent: params.spec.acpAgentId ?? params.spec.agentId,
90+runtimeSessionName: "existing",
91+mode: params.spec.mode,
92+runtimeOptions: { cwd: params.cwd },
93+state: params.state ?? "idle",
94+lastActivityAt: Date.now(),
95+},
96+});
97+return sessionKey;
98+}
99+100+describe("ensureConfiguredAcpBindingSession", () => {
101+it("keeps an existing ready session when configured binding omits cwd", async () => {
102+const spec = createPersistentSpec();
103+const sessionKey = mockReadySession({
104+ spec,
105+cwd: "/workspace/openclaw",
106+});
107+108+const ensured = await ensureConfiguredAcpBindingSession({
109+cfg: baseCfg,
110+ spec,
111+});
112+113+expect(ensured).toEqual({ ok: true, sessionKey });
114+expect(managerMocks.closeSession).not.toHaveBeenCalled();
115+expect(managerMocks.initializeSession).not.toHaveBeenCalled();
116+});
117+118+it("reinitializes a ready session when binding config explicitly sets mismatched cwd", async () => {
119+const spec = createPersistentSpec({
120+cwd: "/workspace/repo-a",
121+});
122+const sessionKey = mockReadySession({
123+ spec,
124+cwd: "/workspace/other-repo",
125+});
126+127+const ensured = await ensureConfiguredAcpBindingSession({
128+cfg: baseCfg,
129+ spec,
130+});
131+132+expect(ensured).toEqual({ ok: true, sessionKey });
133+expect(managerMocks.closeSession).toHaveBeenCalledTimes(1);
134+expect(managerMocks.closeSession).toHaveBeenCalledWith(
135+expect.objectContaining({
136+ sessionKey,
137+clearMeta: false,
138+}),
139+);
140+expect(managerMocks.initializeSession).toHaveBeenCalledTimes(1);
141+});
142+143+it("reinitializes a matching session when the stored ACP session is in error state", async () => {
144+const spec = createPersistentSpec({
145+cwd: "/home/bob/clawd",
146+});
147+const sessionKey = mockReadySession({
148+ spec,
149+cwd: "/home/bob/clawd",
150+state: "error",
151+});
152+153+const ensured = await ensureConfiguredAcpBindingSession({
154+cfg: baseCfg,
155+ spec,
156+});
157+158+expect(ensured).toEqual({ ok: true, sessionKey });
159+expect(managerMocks.closeSession).toHaveBeenCalledTimes(1);
160+expect(managerMocks.initializeSession).toHaveBeenCalledTimes(1);
161+});
162+163+it("initializes ACP session with runtime agent override when provided", async () => {
164+const spec = createPersistentSpec({
165+agentId: "coding",
166+acpAgentId: "codex",
167+});
168+managerMocks.resolveSession.mockReturnValue({ kind: "none" });
169+170+const ensured = await ensureConfiguredAcpBindingSession({
171+cfg: baseCfg,
172+ spec,
173+});
174+175+expect(ensured.ok).toBe(true);
176+expect(managerMocks.initializeSession).toHaveBeenCalledWith(
177+expect.objectContaining({
178+agent: "codex",
179+}),
180+);
181+});
58182});
5918360184describe("resetAcpSessionInPlace", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。