fix: guard resolveUserPath against undefined input (#10176)

* fix: guard resolveUserPath against undefined input

When subagent spawner omits workspaceDir, resolveUserPath receives
undefined and crashes on .trim().  Add a falsy guard that falls back
to process.cwd(), matching the behavior callers already expect.

Closes #10089

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: harden runner workspace fallback (#10176) (thanks @Yida-Dev)

* fix: harden workspace fallback scoping (#10176) (thanks @Yida-Dev)

* refactor: centralize workspace fallback classification and redaction (#10176) (thanks @Yida-Dev)

* test: remove explicit any from utils mock (#10176) (thanks @Yida-Dev)

* security: reject malformed agent session keys for workspace resolution (#10176) (thanks @Yida-Dev)

---------

Co-authored-by: Yida-Dev <reyifeijun@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
This commit is contained in:
Yida-Dev
2026-02-07 01:16:58 +07:00
committed by GitHub
parent 5842bcaaf7
commit 4216449405
22 changed files with 522 additions and 24 deletions

View File

@@ -219,6 +219,75 @@ describe("runEmbeddedPiAgent", () => {
await expect(fs.stat(path.join(agentDir, "models.json"))).resolves.toBeTruthy();
});
it("falls back to per-agent workspace when runtime workspaceDir is missing", async () => {
const sessionFile = nextSessionFile();
const fallbackWorkspace = path.join(tempRoot ?? os.tmpdir(), "workspace-fallback-main");
const cfg = {
...makeOpenAiConfig(["mock-1"]),
agents: {
defaults: {
workspace: fallbackWorkspace,
},
},
} satisfies OpenClawConfig;
await ensureModels(cfg);
const result = await runEmbeddedPiAgent({
sessionId: "session:test-fallback",
sessionKey: "agent:main:subagent:fallback-workspace",
sessionFile,
workspaceDir: undefined as unknown as string,
config: cfg,
prompt: "hello",
provider: "openai",
model: "mock-1",
timeoutMs: 5_000,
agentDir,
runId: "run-fallback-workspace",
enqueue: immediateEnqueue,
});
expect(result.payloads?.[0]?.text).toBe("ok");
await expect(fs.stat(fallbackWorkspace)).resolves.toBeTruthy();
});
it("throws when sessionKey is malformed", async () => {
const sessionFile = nextSessionFile();
const cfg = {
...makeOpenAiConfig(["mock-1"]),
agents: {
defaults: {
workspace: path.join(tempRoot ?? os.tmpdir(), "workspace-fallback-main"),
},
list: [
{
id: "research",
workspace: path.join(tempRoot ?? os.tmpdir(), "workspace-fallback-research"),
},
],
},
} satisfies OpenClawConfig;
await ensureModels(cfg);
await expect(
runEmbeddedPiAgent({
sessionId: "session:test-fallback-malformed",
sessionKey: "agent::broken",
agentId: "research",
sessionFile,
workspaceDir: undefined as unknown as string,
config: cfg,
prompt: "hello",
provider: "openai",
model: "mock-1",
timeoutMs: 5_000,
agentDir,
runId: "run-fallback-workspace-malformed",
enqueue: immediateEnqueue,
}),
).rejects.toThrow("Malformed agent session key");
});
itIfNotWin32(
"persists the first user message before assistant output",
{ timeout: 120_000 },