This repository was archived by the owner on Aug 6, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 64
feat(agent): instruction-level rtk adoption for codex sessions #3491
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| import * as fs from "node:fs"; | ||
| import * as os from "node:os"; | ||
| import * as path from "node:path"; | ||
| import { afterAll, beforeAll, describe, expect, test } from "vitest"; | ||
| import { | ||
| GIT_COMPRESSIBLE_SUBCOMMANDS, | ||
| RTK_PLAIN_COMMANDS, | ||
| } from "./claude/session/rtk"; | ||
| import { appendRtkGuidanceForCodex, buildRtkGuidance } from "./rtk-guidance"; | ||
|
|
||
| describe("rtk guidance for codex", () => { | ||
| let dir: string; | ||
| let binary: string; | ||
|
|
||
| beforeAll(() => { | ||
| dir = fs.mkdtempSync(path.join(os.tmpdir(), "rtk-guidance-test-")); | ||
| binary = path.join(dir, "rtk"); | ||
| fs.writeFileSync(binary, "#!/bin/sh\n"); | ||
| }); | ||
|
|
||
| afterAll(() => { | ||
| fs.rmSync(dir, { recursive: true, force: true }); | ||
| }); | ||
|
|
||
| describe("buildRtkGuidance", () => { | ||
| // The guidance must advertise exactly the Claude hook's eligibility sets, | ||
| // so the token-usage cohorts stay comparable across adapters. | ||
| test("advertises every command the Claude hook rewrites", () => { | ||
| const guidance = buildRtkGuidance("/usr/local/bin/rtk"); | ||
| for (const command of RTK_PLAIN_COMMANDS) { | ||
| expect(guidance).toContain(command); | ||
| } | ||
| for (const sub of GIT_COMPRESSIBLE_SUBCOMMANDS) { | ||
| expect(guidance).toContain(sub); | ||
| } | ||
| }); | ||
|
|
||
| test("uses the resolved binary path in the examples", () => { | ||
| const guidance = buildRtkGuidance("/usr/local/bin/rtk"); | ||
| expect(guidance).toContain("`/usr/local/bin/rtk git status`"); | ||
| }); | ||
|
|
||
| // A desktop install can resolve a path with spaces; unquoted it would | ||
| // split into multiple shell tokens and every guided command would fail. | ||
| test("shell-quotes a binary path containing spaces", () => { | ||
| const guidance = buildRtkGuidance("/Apps/PostHog Code/rtk"); | ||
| expect(guidance).toContain("`'/Apps/PostHog Code/rtk' git status`"); | ||
| expect(guidance).not.toContain("`/Apps/PostHog Code/rtk git status`"); | ||
| }); | ||
|
|
||
| // Parity with the Claude hook's exclusion: prefixing commit/push would | ||
| // hide the leading `git` token from the cloud signed-commit guard. | ||
| test("forbids prefixing git commit and git push", () => { | ||
| const guidance = buildRtkGuidance("rtk"); | ||
| expect(guidance).toContain("Never prefix `git commit`, `git push`"); | ||
| }); | ||
| }); | ||
|
|
||
| describe("appendRtkGuidanceForCodex", () => { | ||
| test("appends guidance when rtk is on PATH", () => { | ||
| const result = appendRtkGuidanceForCodex("base instructions", { | ||
| PATH: dir, | ||
| }); | ||
| expect(result.startsWith("base instructions\n\n")).toBe(true); | ||
| expect(result).toContain("rtk command-output compression"); | ||
| expect(result).toContain(binary); | ||
| }); | ||
|
|
||
| // POSTHOG_RTK=0 is set per run from the cloud kill-switch flag; it must | ||
| // silence the guidance too, which is why the gate is resolveRtkPrefix | ||
| // rather than detectRtkBinary. | ||
| test.each([["0"], ["false"]])( | ||
| "returns instructions unchanged when POSTHOG_RTK is %s", | ||
| (value) => { | ||
| expect( | ||
| appendRtkGuidanceForCodex("base instructions", { | ||
| POSTHOG_RTK: value, | ||
| PATH: dir, | ||
| }), | ||
| ).toBe("base instructions"); | ||
| }, | ||
| ); | ||
|
|
||
| test("returns instructions unchanged when rtk is not installed", () => { | ||
| expect( | ||
| appendRtkGuidanceForCodex("base instructions", { | ||
| PATH: "/nonexistent", | ||
| }), | ||
| ).toBe("base instructions"); | ||
| }); | ||
|
|
||
| test("does not leave a leading separator when instructions are empty", () => { | ||
| const result = appendRtkGuidanceForCodex("", { PATH: dir }); | ||
| expect(result.startsWith("## rtk")).toBe(true); | ||
| }); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import { | ||
| GIT_COMPRESSIBLE_SUBCOMMANDS, | ||
| RTK_PLAIN_COMMANDS, | ||
| resolveRtkPrefix, | ||
| shQuote, | ||
| } from "./claude/session/rtk"; | ||
|
|
||
| /** | ||
| * Instruction-level RTK integration for Codex sessions. | ||
| * | ||
| * The Claude adapter routes eligible commands through RTK deterministically | ||
| * with a PreToolUse hook that rewrites the Bash input. Codex executes shell | ||
| * commands internally over JSON-RPC and its app-server protocol has no | ||
| * command-rewrite channel — the adapter can only approve or deny — so the | ||
| * only integration point is the developer instructions: tell the model to | ||
| * prefix eligible commands itself. | ||
| * | ||
| * The advertised command set and rules mirror the Claude hook exactly | ||
| * (RTK_PLAIN_COMMANDS + GIT_COMPRESSIBLE_SUBCOMMANDS, bare invocations only, | ||
| * never commit/push), so token-usage cohorts stay comparable across adapters. | ||
| */ | ||
| export function buildRtkGuidance(rtkPrefix: string): string { | ||
| // Same quoting as the Claude rewrite hook: a resolved path containing | ||
| // spaces must stay one shell token in the commands the model copies. | ||
| const prefix = shQuote(rtkPrefix); | ||
| const plainCommands = [...RTK_PLAIN_COMMANDS].join("`, `"); | ||
| const gitSubcommands = [...GIT_COMPRESSIBLE_SUBCOMMANDS].join(", "); | ||
|
|
||
| return `## rtk command-output compression | ||
|
|
||
| \`${prefix}\` is installed. It runs a command unchanged and compresses its output before you read it, so prefixed commands cost far less context. When you execute one of these as a single, bare command, prefix it with \`${prefix}\`: | ||
|
|
||
| - \`${plainCommands}\` | ||
| - these git subcommands: ${gitSubcommands} | ||
|
|
||
| Examples: \`${prefix} git status\`, \`${prefix} grep -rn "foo" src\`, \`${prefix} ls -la\`. | ||
|
|
||
| Rules: | ||
| - Only prefix a single bare invocation. Never use it when the command is part of a pipe, uses \`&&\`, \`;\`, or redirection, or when another program parses the output — compression would corrupt what the consumer reads. | ||
| - Never prefix \`git commit\`, \`git push\`, or any other command not listed above. | ||
| - Skip the prefix when you need the exact, complete output (for example, copying a diff verbatim).`; | ||
| } | ||
|
|
||
| /** | ||
| * Appends the RTK guidance to Codex developer instructions when an RTK binary | ||
| * is usable. Gated on `resolveRtkPrefix` — not `detectRtkBinary` — so the | ||
| * per-run `POSTHOG_RTK=0` opt-out (the cloud kill-switch flag) disables the | ||
| * guidance along with everything else. | ||
| */ | ||
| export function appendRtkGuidanceForCodex( | ||
| instructions: string, | ||
| env: NodeJS.ProcessEnv = process.env, | ||
| ): string { | ||
| const rtkPrefix = resolveRtkPrefix(env); | ||
| if (!rtkPrefix) return instructions; | ||
| return [instructions, buildRtkGuidance(rtkPrefix)] | ||
| .filter(Boolean) | ||
| .join("\n\n"); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.