



























1+import fs from "node:fs/promises";
2+import os from "node:os";
3+import path from "node:path";
14import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
25import { AcpRuntimeError } from "../../acp/runtime/errors.js";
36import type { OpenClawConfig } from "../../config/config.js";
@@ -95,6 +98,27 @@ vi.mock("../../agents/acp-spawn.js", () => ({
9598params.cfg?.agents?.defaults?.sandbox?.mode === "all"
9699 ? 'Sandboxed sessions cannot spawn ACP sessions because runtime="acp" runs on the host. Use runtime="subagent" from sandboxed sessions.'
97100 : undefined,
101+resolveRuntimeCwdForAcpSpawn: async (params: {
102+explicitCwd?: string;
103+resolvedCwd?: string;
104+}) => {
105+if (params.explicitCwd) {
106+return params.resolvedCwd;
107+}
108+if (!params.resolvedCwd) {
109+return undefined;
110+}
111+try {
112+await fs.access(params.resolvedCwd);
113+return params.resolvedCwd;
114+} catch (error) {
115+const code = error instanceof Error ? (error as NodeJS.ErrnoException).code : undefined;
116+if (code === "ENOENT" || code === "ENOTDIR") {
117+return undefined;
118+}
119+throw error;
120+}
121+},
98122}));
99123100124vi.mock("../../config/sessions.js", async () => {
@@ -1162,6 +1186,73 @@ describe("/acp command", () => {
11621186expect(seededWithoutEntry?.runtimeSessionName).toContain(":runtime");
11631187});
116411881189+it("inherits the target agent workspace when /acp spawn omits --cwd", async () => {
1190+hoisted.ensureSessionMock.mockResolvedValueOnce({
1191+sessionKey: "agent:codex:acp:s2",
1192+backend: "acpx",
1193+runtimeSessionName: "agent:codex:acp:s2:runtime",
1194+agentSessionId: "codex-inner-2",
1195+backendSessionId: "acpx-2",
1196+});
1197+1198+const workspace = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-"));
1199+try {
1200+const cfg = {
1201+ ...baseCfg,
1202+agents: {
1203+list: [
1204+{
1205+id: "codex",
1206+ workspace,
1207+},
1208+],
1209+},
1210+} satisfies OpenClawConfig;
1211+1212+const result = await runDiscordAcpCommand("/acp spawn codex", cfg);
1213+1214+expect(result?.reply?.text).toContain("Spawned ACP session agent:codex:acp:");
1215+expectMockCallFields(hoisted.ensureSessionMock, {
1216+agent: "codex",
1217+mode: "persistent",
1218+cwd: workspace,
1219+});
1220+} finally {
1221+await fs.rm(workspace, { recursive: true, force: true });
1222+}
1223+});
1224+1225+it("falls back to the backend default cwd when the inherited target workspace is missing", async () => {
1226+hoisted.ensureSessionMock.mockResolvedValueOnce({
1227+sessionKey: "agent:codex:acp:s3",
1228+backend: "acpx",
1229+runtimeSessionName: "agent:codex:acp:s3:runtime",
1230+agentSessionId: "codex-inner-3",
1231+backendSessionId: "acpx-3",
1232+});
1233+1234+const cfg = {
1235+ ...baseCfg,
1236+agents: {
1237+list: [
1238+{
1239+id: "codex",
1240+workspace: "/home/bob/codex-workspace-missing",
1241+},
1242+],
1243+},
1244+} satisfies OpenClawConfig;
1245+1246+const result = await runDiscordAcpCommand("/acp spawn codex", cfg);
1247+1248+expect(result?.reply?.text).toContain("Spawned ACP session agent:codex:acp:");
1249+expectMockCallFields(hoisted.ensureSessionMock, {
1250+agent: "codex",
1251+mode: "persistent",
1252+cwd: undefined,
1253+});
1254+});
1255+11651256it("persists ACP spawn labels without a nested gateway self-call", async () => {
11661257const params = createDiscordParams("/acp spawn codex --bind here --label inbox");
11671258此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。