From 8a98bfe79f358a3282f37ac98678452c50dc877a Mon Sep 17 00:00:00 2001 From: cynarAI <155274210+cynarAI@users.noreply.github.com> Date: Thu, 22 Oct 2026 23:51:31 +0200 Subject: [PATCH] fix(adapters,sandboxes): tell agents where git works and make the refusal explicit (#223) * fix(adapters,sandboxes): tell agents where git works and make the refusal explicit Agents searched for the project repository in mounted host folders and read the broker refusal as a missing broker. The prompt now names the folder where git and gh work, and the refusal says to cd there instead of looking elsewhere. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ * test(web): wait for the automatic voice start before switching speaking on Clicking the switch while the app was still turning the voice on by itself could turn it off again, which failed the voice settings e2e twice in a row. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ --------- Co-authored-by: Claude --- apps/web/e2e/voice.spec.ts | 9 ++++++++- infra/sandboxes/supervisor/src/github-broker.test.ts | 3 ++- infra/sandboxes/supervisor/src/github-broker.ts | 7 ++++++- packages/adapters/src/executor.ts | 2 +- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/apps/web/e2e/voice.spec.ts b/apps/web/e2e/voice.spec.ts index 9aff10f..b54be0f 100644 --- a/apps/web/e2e/voice.spec.ts +++ b/apps/web/e2e/voice.spec.ts @@ -9,7 +9,14 @@ async function turnOnSpeaking(page: Page) { await expect(voice.getByLabel("API key", { exact: true })).toHaveCount(0); // The app turns the local voice on by itself the first time it starts. const speaking = voice.getByRole("switch", { name: "Speaking on or off" }); - if ((await speaking.getAttribute("aria-checked")) !== "true") await speaking.click(); + // Clicking before the automatic start has settled would turn the voice off again. + const startedByItself = await expect(speaking) + .toHaveAttribute("aria-checked", "true", { timeout: 5_000 }) + .then( + () => true, + () => false, + ); + if (!startedByItself) await speaking.click(); await expect(speaking).toHaveAttribute("aria-checked", "true"); await expect(voice.getByTestId("voice-carousel")).toBeVisible(); return voice; diff --git a/infra/sandboxes/supervisor/src/github-broker.test.ts b/infra/sandboxes/supervisor/src/github-broker.test.ts index f3b4ff9..acf86a5 100644 --- a/infra/sandboxes/supervisor/src/github-broker.test.ts +++ b/infra/sandboxes/supervisor/src/github-broker.test.ts @@ -13,6 +13,7 @@ import { gitProcessIdentity, hostGitInvocation, hostPathFor, + outsideWorkspaceMessage, } from "./github-broker.js"; import { demuxDockerLogs, @@ -76,7 +77,7 @@ describe("github broker host git", () => { it("refuses paths outside the mounted workspace", () => { expect(hostGitInvocation(session, ["git", "clone", "https://x/y.git"], "/tmp")).toEqual({ - error: "Run git inside /home/rakazo.", + error: outsideWorkspaceMessage("/home/rakazo"), }); expect( hostGitInvocation(session, ["git", "clone", "https://x/y.git", "/etc/y"], "/home/rakazo"), diff --git a/infra/sandboxes/supervisor/src/github-broker.ts b/infra/sandboxes/supervisor/src/github-broker.ts index 5f73024..f91cfb7 100644 --- a/infra/sandboxes/supervisor/src/github-broker.ts +++ b/infra/sandboxes/supervisor/src/github-broker.ts @@ -60,6 +60,11 @@ export function explainGitFailure(stderr: string): string { return `${stderr.trimEnd()}\nGitHub rejected the token evOo stores for this user. Tell the user to reconnect GitHub under Settings, Integrations. Do not ask for a token or set one up yourself.\n`; } +/** Names the one place git works, so a model does not read the refusal as a missing broker or hunt for the repository elsewhere. */ +export function outsideWorkspaceMessage(root: string): string { + return `git only works inside ${root}, which holds this project's repository. Run: cd ${root}. Other folders, mounted host folders and ssh are not part of it. Do not ask for a token.`; +} + /** Host argv and cwd for a git call made in the computer, or an error when it leaves the workspace. */ export function hostGitInvocation( session: GithubBrokerSession, @@ -67,7 +72,7 @@ export function hostGitInvocation( containerCwd: string | undefined, ): { argv: string[]; cwd: string } | { error: string } { const cwd = containerCwd ? hostPathFor(session, containerCwd) : session.workspacePath; - if (!cwd) return { error: `Run git inside ${session.containerRoot}.` }; + if (!cwd) return { error: outsideWorkspaceMessage(session.containerRoot) }; const mapped: string[] = []; for (const part of argv.slice(1)) { if (!part.startsWith("/")) { diff --git a/packages/adapters/src/executor.ts b/packages/adapters/src/executor.ts index 3e83b47..ce1d79b 100644 --- a/packages/adapters/src/executor.ts +++ b/packages/adapters/src/executor.ts @@ -1720,7 +1720,7 @@ export function createRunExecutor(deps: ExecutorDeps) { teamComputer: computerMode === "team", }); const gitBrokerPolicySuffix = githubIntegrationToken - ? " Git push and opening pull requests require the user's approval once per run; never force-push or push to the default branch." + ? ` git and gh work only inside ${projectWorkspacePath ? "/home/rakazo/project" : "/home/rakazo"}${projectWorkspacePath ? ", the space's repository" : ""}; look for the repository and its branches there, not in other folders, and never over ssh. Git push and opening pull requests require the user's approval once per run; never force-push or push to the default branch.` : ""; const hostAccessNote = process.env.RAKAZO_HOST_ACCESS_NOTE?.trim(); const desktopHostSuffix = isDesktopHost