Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions packages/agent/src/utils/github-token.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,28 @@ describe("github-token", () => {
expect(resolveGithubToken(path)).toBe("ghs_fromfile");
});

it("falls back to the process env when the sandbox file is absent", () => {
it("prefers the github env file over the legacy env file", () => {
const githubPath = writeEnvFile("GH_TOKEN=ghs_github\0");
const legacyPath = writeEnvFile("GH_TOKEN=ghs_legacy\0");
expect(resolveGithubToken(githubPath, legacyPath)).toBe("ghs_github");
});

it("falls back to the legacy env file when the github env file is absent", () => {
vi.stubEnv("GH_TOKEN", "ghs_fromprocess");
expect(resolveGithubToken("/nonexistent/agent-env")).toBe(
"ghs_fromprocess",
);
const legacyPath = writeEnvFile("GH_TOKEN=ghs_legacy\0");
expect(
resolveGithubToken("/nonexistent/agent-github-env", legacyPath),
).toBe("ghs_legacy");
});

it("falls back to the process env when both sandbox files are absent", () => {
vi.stubEnv("GH_TOKEN", "ghs_fromprocess");
expect(
resolveGithubToken(
"/nonexistent/agent-github-env",
"/nonexistent/agent-env",
),
).toBe("ghs_fromprocess");
});
});
});
22 changes: 17 additions & 5 deletions packages/agent/src/utils/github-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import { readGithubTokenFromEnv } from "@posthog/git/signed-commit";
// this live file is how in-process tools pick up a refreshed token without a
// process restart.
const SANDBOX_GITHUB_ENV_FILE = "/tmp/agent-github-env";
// Legacy pre-split file. Before GitHub credentials got their own file, the
// backend refreshed them in place here. Kept as a fallback so that during a
// mixed-version rollout (new agent, old backend that only writes this file) we
// still read a live, refreshed token instead of the frozen process env.
const SANDBOX_LEGACY_ENV_FILE = "/tmp/agent-env";

export function readGithubTokenFromSandboxEnvFile(
envFilePath: string = SANDBOX_GITHUB_ENV_FILE,
Expand All @@ -31,14 +36,21 @@ export function readGithubTokenFromSandboxEnvFile(

/** The GitHub token available to the sandbox, if any.
*
* Prefers the live agentsh env file (refreshed in place mid-session) over the
* process env (frozen at launch) so long-running in-process tools — e.g. the
* signed-commit tool — pick up a refreshed token without a restart.
* Precedence: the dedicated live credential file, then the legacy live file,
* then the process env (frozen at launch). Reading a live file first is how
* long-running in-process tools — e.g. the signed-commit tool — pick up a
* refreshed token without a restart. The legacy fallback covers a mixed-version
* rollout where an older backend still refreshes credentials only into
* `/tmp/agent-env`; without it the reader would drop straight to the frozen
* process env and sign commits with an expired or previous actor's token.
*/
export function resolveGithubToken(
envFilePath: string = SANDBOX_GITHUB_ENV_FILE,
githubEnvFilePath: string = SANDBOX_GITHUB_ENV_FILE,
legacyEnvFilePath: string = SANDBOX_LEGACY_ENV_FILE,
): string | undefined {
return (
readGithubTokenFromSandboxEnvFile(envFilePath) ?? readGithubTokenFromEnv()
readGithubTokenFromSandboxEnvFile(githubEnvFilePath) ??
readGithubTokenFromSandboxEnvFile(legacyEnvFilePath) ??
readGithubTokenFromEnv()
);
}
Loading