Skip to content

Commit b3c3dce

Browse files
Fix provider-specific agent system prompt identity (#1524)
## Summary - Add provider-specific system prompt rendering in `packages/agent/common.nix`. - Keep Claude Code on the Claude identity prompt and point Codex at a Codex identity prompt. - Parameterize the shared system prompt so runtime naming and AI-attribution text match the provider. - Add eval assertions for Claude and Codex prompt identity text. ## Validation - `nix fmt -- packages/agent/system-prompt.nix packages/agent/common.nix packages/agent/codex/default.nix tests/default.nix` - `nix eval --raw --impure --expr 'import ./packages/agent/system-prompt.nix { lib = (import <nixpkgs> {}).lib; }'` - `nix eval --json --impure --expr '<targeted common.nix provider prompt assertions>'` - `nix eval --json .#codex.specValue.soft --apply 'entries: builtins.elem "model_instructions_file" (map (entry: entry.key) entries)'` - `nix eval --raw .#codex.modelInstructionsFile` and verified the rendered file contains `You are Codex.` and `via Codex`, not `via Claude Code`. (sent by an AI agent via Claude Code) <!-- Macroscope's pull request summary starts here --> <!-- Macroscope will only edit the content between these invisible markers, and the markers themselves will not be visible in the GitHub rendered markdown. --> <!-- If you delete either of the start / end markers from your PR's description, Macroscope will append its summary at the bottom of the description. --> > [!NOTE] > ### Fix provider-specific agent identity in system prompts for Codex and Claude > - Introduces `systemPromptFor` in [common.nix](https://github.com/indexable-inc/index/pull/1524/files#diff-6e6ea9bcd37d43542775f46f4a2342510acf460cdaa9240e6d406c3cc3ec9419) that accepts a provider key and composes a system prompt with the correct agent name and a provider-specific identification paragraph. > - Parameterizes [system-prompt.nix](https://github.com/indexable-inc/index/pull/1524/files#diff-79e96dbc21d7175eddadc583fb80f58bab62dfa2273228a412e4cde2c4aed0df) with an `agentName` argument (defaulting to `"Claude Code"`), replacing hardcoded occurrences in the harness and disclosure paragraphs. > - Updates [codex/default.nix](https://github.com/indexable-inc/index/pull/1524/files#diff-f86191c841f2a39e7a59565f14860841e742d261b42ff0b9f4a5f0d95e7de9d5) to call `systemPromptFor "codex"` so the baked prompt identifies as Codex rather than Claude Code. > - Adds provider-prompt tests in [tests/default.nix](https://github.com/indexable-inc/index/pull/1524/files#diff-1cc580de297308d93d82f7b72446ae4b98832a8aae3378e9e134102519a0e33a) asserting each prompt contains the correct identity wording and excludes the other provider's wording. > > <!-- Macroscope's review summary starts here --> > > <sup><a href="https://app.macroscope.com">Macroscope</a> summarized 84bd041.</sup> > <!-- Macroscope's review summary ends here --> > <!-- macroscope-ui-refresh --> <!-- Macroscope's pull request summary ends here -->
1 parent 6249e8b commit b3c3dce

4 files changed

Lines changed: 63 additions & 7 deletions

File tree

packages/agent/codex/default.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ let
7474
if modelInstructionsFile != null then
7575
modelInstructionsFile
7676
else
77-
builtins.toFile "codex-system-prompt.txt" common.systemPrompt;
77+
builtins.toFile "codex-system-prompt.txt" (common.systemPromptFor "codex");
7878

7979
# The compiled Rust launcher (packages/config-launch): reads IX_LAUNCH_SPEC
8080
# (a baked JSON file describing the target binary, config path, forced flags,

packages/agent/common.nix

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,38 @@
1212
ix,
1313
repoPackages ? { },
1414
}:
15+
let
16+
providerNames = {
17+
claude = "Claude Code";
18+
codex = "Codex";
19+
};
20+
extraSystemPrompts = {
21+
claude = ''
22+
You are Claude Code. When naming the coding-agent runtime or disclosing AI
23+
authorship in outward-facing messages, say Claude Code.
24+
'';
25+
codex = ''
26+
You are Codex. When naming the coding-agent runtime or disclosing AI
27+
authorship in outward-facing messages, say Codex.
28+
'';
29+
};
30+
systemPromptFor =
31+
provider:
32+
lib.concatStringsSep "\n\n" [
33+
(import ./system-prompt.nix {
34+
inherit lib;
35+
agentName = providerNames.${provider};
36+
})
37+
extraSystemPrompts.${provider}
38+
];
39+
in
1540
{
41+
inherit extraSystemPrompts systemPromptFor;
42+
1643
# The house system prompt a wrapper bakes for its agent. One paragraph per
1744
# list element; see ./system-prompt.nix for the authored text and how
1845
# claude-code bakes it (`systemPrompt`, which REPLACES the stock prompt).
19-
systemPrompt = import ./system-prompt.nix { inherit lib; };
46+
systemPrompt = systemPromptFor "claude";
2047

2148
# The house MCP servers (the `index` kernel plus `exa` web search), rendered
2249
# from the shared `ix.mcp` registry with the kernel pointed at the `mcp`

packages/agent/system-prompt.nix

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
{ lib }:
2-
# The house system prompt Claude Code runs with, REPLACING the stock prompt
3-
# (see the `systemPrompt` argument in ./claude-code/default.nix). Each rule is a
1+
{
2+
lib,
3+
agentName ? "Claude Code",
4+
}:
5+
# The house system prompt the agent wrappers run with, REPLACING the stock prompt
6+
# where the upstream CLI supports replacement. Each rule is a
47
# named binding; `order` fixes how they read top-to-bottom, joined with blank
58
# lines so each reads as a self-contained paragraph.
69
#
@@ -159,7 +162,7 @@ let
159162
'';
160163

161164
harness = ''
162-
Know the Claude Code runtime. Text outside tools renders as GitHub-flavored
165+
Know the ${agentName} runtime. Text outside tools renders as GitHub-flavored
163166
Markdown. Cite code as `file_path:line_number`.
164167
165168
Batch independent native tool calls; `python_exec` calls serialize. Treat
@@ -312,7 +315,7 @@ let
312315
discloseAi = ''
313316
In messages another person will read, disclose AI authorship. Append the
314317
model and version when known, otherwise:
315-
`(sent by an AI agent via Claude Code)`
318+
`(sent by an AI agent via ${agentName})`
316319
317320
This does not apply to replies to the user you are working with.
318321
'';

tests/default.nix

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ let
144144
}
145145
);
146146
sampleCodexMcpEntry = key: lib.findFirst (entry: entry.key == key) null sampleCodexMcpEntries;
147+
agentCommon = import (paths.packagesRoot + "/agent/common.nix") { inherit lib ix repoPackages; };
148+
sampleClaudeSystemPrompt = agentCommon.systemPromptFor "claude";
149+
sampleCodexSystemPrompt = agentCommon.systemPromptFor "codex";
147150

148151
minecraft =
149152
let
@@ -2468,6 +2471,29 @@ let
24682471
}
24692472
];
24702473

2474+
provider-prompts = [
2475+
{
2476+
assertion = lib.hasInfix "You are Claude Code." sampleClaudeSystemPrompt;
2477+
message = "Claude wrapper prompt should identify Claude Code";
2478+
}
2479+
{
2480+
assertion = lib.hasInfix "You are Codex." sampleCodexSystemPrompt;
2481+
message = "Codex wrapper prompt should identify Codex";
2482+
}
2483+
{
2484+
assertion =
2485+
lib.hasInfix "via Codex" sampleCodexSystemPrompt
2486+
&& !(lib.hasInfix "via Claude Code" sampleCodexSystemPrompt);
2487+
message = "Codex prompt should disclose outward messages as Codex, not Claude Code";
2488+
}
2489+
{
2490+
assertion =
2491+
lib.hasInfix "via Claude Code" sampleClaudeSystemPrompt
2492+
&& !(lib.hasInfix "via Codex" sampleClaudeSystemPrompt);
2493+
message = "Claude prompt should disclose outward messages as Claude Code, not Codex";
2494+
}
2495+
];
2496+
24712497
ix-ray = [
24722498
{
24732499
# The head runs both daemons (Ray GCS + the ix-mcp engine that drives it).

0 commit comments

Comments
 (0)