From f95f6dd052daf618bac6ed16bb4a8112a376d47d Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Sun, 15 Feb 2026 15:37:04 -0500 Subject: [PATCH] Gateway: refine BOOTSTRAP post-onboarding filtering --- src/agents/workspace.ts | 15 ++----------- .../server-methods/agents-mutate.test.ts | 22 +++++++++++++++++++ src/gateway/server-methods/agents.ts | 4 ++-- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/agents/workspace.ts b/src/agents/workspace.ts index 953a28ec300..9e1c081c7ec 100644 --- a/src/agents/workspace.ts +++ b/src/agents/workspace.ts @@ -102,11 +102,6 @@ type WorkspaceOnboardingState = { onboardingCompletedAt?: string; }; -export type WorkspaceOnboardingStateSnapshot = { - bootstrapSeededAt?: string; - onboardingCompletedAt?: string; -}; - /** Set of recognized bootstrap filenames for runtime validation */ const VALID_BOOTSTRAP_NAMES: ReadonlySet = new Set([ DEFAULT_AGENTS_FILENAME, @@ -189,15 +184,9 @@ async function readWorkspaceOnboardingState(statePath: string): Promise { +async function readWorkspaceOnboardingStateForDir(dir: string): Promise { const statePath = resolveWorkspaceStatePath(resolveUserPath(dir)); - const state = await readWorkspaceOnboardingState(statePath); - return { - bootstrapSeededAt: state.bootstrapSeededAt, - onboardingCompletedAt: state.onboardingCompletedAt, - }; + return await readWorkspaceOnboardingState(statePath); } export async function isWorkspaceOnboardingCompleted(dir: string): Promise { diff --git a/src/gateway/server-methods/agents-mutate.test.ts b/src/gateway/server-methods/agents-mutate.test.ts index c999f92def8..eb13cc1da85 100644 --- a/src/gateway/server-methods/agents-mutate.test.ts +++ b/src/gateway/server-methods/agents-mutate.test.ts @@ -119,6 +119,12 @@ function createEnoentError() { return err; } +function createErrnoError(code: string) { + const err = new Error(code) as NodeJS.ErrnoException; + err.code = code; + return err; +} + beforeEach(() => { mocks.fsReadFile.mockImplementation(async () => { throw createEnoentError(); @@ -423,4 +429,20 @@ describe("agents.files.list", () => { const files = (result as { files: Array<{ name: string }> }).files; expect(files.some((file) => file.name === "BOOTSTRAP.md")).toBe(false); }); + + it("falls back to showing BOOTSTRAP.md when workspace state cannot be read", async () => { + mocks.fsReadFile.mockImplementation(async (filePath: string | URL | number) => { + if (String(filePath).endsWith("workspace-state.json")) { + throw createErrnoError("EACCES"); + } + throw createEnoentError(); + }); + + const { respond, promise } = makeCall("agents.files.list", { agentId: "main" }); + await promise; + + const [, result] = respond.mock.calls[0] ?? []; + const files = (result as { files: Array<{ name: string }> }).files; + expect(files.some((file) => file.name === "BOOTSTRAP.md")).toBe(true); + }); }); diff --git a/src/gateway/server-methods/agents.ts b/src/gateway/server-methods/agents.ts index f1a1dc986af..2fbb07fd96c 100644 --- a/src/gateway/server-methods/agents.ts +++ b/src/gateway/server-methods/agents.ts @@ -53,7 +53,7 @@ const BOOTSTRAP_FILE_NAMES = [ DEFAULT_HEARTBEAT_FILENAME, DEFAULT_BOOTSTRAP_FILENAME, ] as const; -const BOOTSTRAP_FILE_NAMES_WITHOUT_ONBOARDING = BOOTSTRAP_FILE_NAMES.filter( +const BOOTSTRAP_FILE_NAMES_POST_ONBOARDING = BOOTSTRAP_FILE_NAMES.filter( (name) => name !== DEFAULT_BOOTSTRAP_FILENAME, ); @@ -122,7 +122,7 @@ async function listAgentFiles(workspaceDir: string, options?: { hideBootstrap?: }> = []; const bootstrapFileNames = options?.hideBootstrap - ? BOOTSTRAP_FILE_NAMES_WITHOUT_ONBOARDING + ? BOOTSTRAP_FILE_NAMES_POST_ONBOARDING : BOOTSTRAP_FILE_NAMES; for (const name of bootstrapFileNames) { const filePath = path.join(workspaceDir, name);