fix(agents): preserve fresh workspace hatch (#111553)

This commit is contained in:
Peter Steinberger
2026-07-19 15:35:54 -07:00
committed by GitHub
parent 473962b7de
commit 7116ad6e28
3 changed files with 63 additions and 1 deletions
@@ -0,0 +1,40 @@
import fs from "node:fs/promises";
import path from "node:path";
import { expect, it } from "vitest";
import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js";
import { createOpenClawTestState } from "../test-utils/openclaw-test-state.js";
import { createAgent } from "./agent-create.js";
import {
DEFAULT_IDENTITY_FILENAME,
ensureAgentWorkspace,
isWorkspaceBootstrapPending,
} from "./workspace.js";
it("keeps a fresh named workspace pending through the first run setup", async () => {
const state = await createOpenClawTestState({
layout: "state-only",
scenario: "minimal",
label: "named-agent-hatch",
});
const workspace = state.path("named-workspace");
try {
const created = await createAgent({ name: "Researcher", workspace });
expect(created).toMatchObject({ status: "created", bootstrapPending: true });
expect(await isWorkspaceBootstrapPending(workspace)).toBe(true);
const firstRunWorkspace = await ensureAgentWorkspace({
dir: workspace,
ensureBootstrapFiles: true,
});
expect(firstRunWorkspace.bootstrapPending).toBe(true);
expect(await isWorkspaceBootstrapPending(workspace)).toBe(true);
expect(
await fs.readFile(path.join(workspace, DEFAULT_IDENTITY_FILENAME), "utf8"),
).not.toContain("Researcher");
} finally {
closeOpenClawStateDatabaseForTest();
await state.cleanup();
}
});
+18
View File
@@ -180,7 +180,25 @@ describe("createAgent", () => {
expect(mocks.ensureAgentWorkspace).toHaveBeenCalledOnce();
});
it("keeps the template identity while bootstrap is pending", async () => {
await createAgent({ name: "researcher" });
expect(mocks.rootRead).not.toHaveBeenCalled();
expect(mocks.rootWrite).not.toHaveBeenCalled();
expect(mocks.persisted).toMatchObject({
agents: {
list: expect.arrayContaining([
expect.objectContaining({ identity: { name: "researcher" } }),
]),
},
});
});
it("does not publish config when identity setup is unsafe", async () => {
mocks.ensureAgentWorkspace.mockImplementation(async ({ dir }: { dir: string }) => ({
dir,
bootstrapPending: false,
}));
mocks.rootRead.mockRejectedValue(new FsSafeError("invalid-path", "unsafe identity path"));
await expect(createAgent({ name: "researcher" })).resolves.toMatchObject({
+5 -1
View File
@@ -161,7 +161,11 @@ export async function createAgent(params: CreateAgentParams): Promise<CreateAgen
});
}
await fs.mkdir(resolveSessionTranscriptsDirForAgent(agentId), { recursive: true });
await writeIdentityFile({ workspaceDir: workspace.dir, identity });
// A creation-time name is config, not proof that the fresh workspace hatched.
// Keep IDENTITY.md templated until BOOTSTRAP completes its first-turn ceremony.
if (!workspace.bootstrapPending) {
await writeIdentityFile({ workspaceDir: workspace.dir, identity });
}
return {
nextConfig,