fix(gateway): stop listing IDENTITY.md as a core file editor tab (#114470)

This commit is contained in:
Peter Steinberger
2026-07-27 05:34:44 -04:00
committed by GitHub
parent 84a4ff30d5
commit 95bac0129d
2 changed files with 31 additions and 6 deletions

View File

@@ -2947,19 +2947,36 @@ describe("agents.files.list", () => {
// Pins the derivation: a private copy of this list in the gateway is what let a
// retired file linger in the Control UI as a permanently-missing tab.
it("lists exactly the canonical workspace bootstrap filenames", async () => {
it("lists the canonical workspace filenames except IDENTITY.md", async () => {
const names = await listAgentFileNames();
expect(names).toStrictEqual([...WORKSPACE_BOOTSTRAP_FILENAMES]);
expect(names).toStrictEqual(
WORKSPACE_BOOTSTRAP_FILENAMES.filter((name) => name !== "IDENTITY.md"),
);
});
it("lists the canonical filenames without BOOTSTRAP.md once setup completed", async () => {
mocks.isWorkspaceSetupCompleted.mockResolvedValue(true);
const names = await listAgentFileNames();
expect(names).toStrictEqual(
WORKSPACE_BOOTSTRAP_FILENAMES.filter((name) => name !== "BOOTSTRAP.md"),
WORKSPACE_BOOTSTRAP_FILENAMES.filter(
(name) => name !== "IDENTITY.md" && name !== "BOOTSTRAP.md",
),
);
});
// The identity form owns this file via agents.update; raw writes stay available
// so removing the editor tab does not remove the capability.
it("still accepts direct IDENTITY.md writes even though it is not listed", async () => {
const { respond, promise } = makeCall("agents.files.set", {
agentId: "main",
name: "IDENTITY.md",
content: "- Name: Ada\n",
});
await promise;
expectRespondOk(respond, { ok: true });
});
it("rejects writes to retired HEARTBEAT.md workspace files", async () => {
const { respond, promise } = makeCall("agents.files.set", {
agentId: "main",

View File

@@ -101,7 +101,13 @@ import type { GatewayRequestHandlers, RespondFn } from "./types.js";
// Derived from the canonical workspace list so retiring a bootstrap file cannot
// leave the Control UI advertising a file the runtime no longer reads.
const CORE_FILE_NAMES = WORKSPACE_BOOTSTRAP_FILENAMES;
// IDENTITY.md is excluded: it is a parsed record that `agents.update` rewrites via
// mergeIdentityMarkdownContent, so a second freeform editor would clobber fields
// (Creature/Vibe, unfilled placeholders) that the identity form round-trips.
// It stays writable through agents.files.set for clients that want raw access.
const CORE_FILE_NAMES = WORKSPACE_BOOTSTRAP_FILENAMES.filter(
(name) => name !== DEFAULT_IDENTITY_FILENAME,
);
const CORE_FILE_NAMES_POST_ONBOARDING = CORE_FILE_NAMES.filter(
(name) => name !== DEFAULT_BOOTSTRAP_FILENAME,
);
@@ -131,8 +137,10 @@ export const testing = {
},
};
// Gateway file mutations are intentionally capped to the workspace files the UI owns.
const ALLOWED_FILE_NAMES = new Set<string>(CORE_FILE_NAMES);
// Writes stay capped to canonical workspace files, and deliberately remain wider
// than the listed core files: IDENTITY.md is not offered as an editor tab but is
// still writable for clients that manage it directly.
const ALLOWED_FILE_NAMES = new Set<string>(WORKSPACE_BOOTSTRAP_FILENAMES);
function resolveAgentWorkspaceFileOrRespondError(
params: Record<string, unknown>,