Skip to content

Commit 72039e1

Browse files
Add fine-grained system prompt customization (customize mode)
Add a new 'customize' mode for systemMessage configuration, enabling SDK consumers to selectively override individual sections of the CLI system prompt while preserving the rest. This sits between the existing 'append' and 'replace' modes. 9 configurable sections: identity, tone, tool_efficiency, environment_context, code_change_rules, guidelines, safety, tool_instructions, custom_instructions. 4 override actions per section: replace, remove, append, prepend. Unknown section IDs are handled gracefully: content-bearing overrides are appended to additional instructions with a warning, and remove on unknown sections is silently ignored. Types and constants added to all 4 SDK languages (TypeScript, Python, Go, .NET). Documentation updated across all READMEs and getting-started guide. Companion runtime PR: github/copilot-agent-runtime#4751 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent b67e3e5 commit 72039e1

File tree

14 files changed

+371
-21
lines changed

14 files changed

+371
-21
lines changed

docs/getting-started.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1245,7 +1245,7 @@ const session = await client.createSession({
12451245
12461246
### Customize the System Message
12471247

1248-
Control the AI's behavior and personality:
1248+
Control the AI's behavior and personality by appending instructions:
12491249

12501250
```typescript
12511251
const session = await client.createSession({
@@ -1255,6 +1255,28 @@ const session = await client.createSession({
12551255
});
12561256
```
12571257

1258+
For more fine-grained control, use `mode: "customize"` to override individual sections of the system prompt while preserving the rest:
1259+
1260+
```typescript
1261+
const session = await client.createSession({
1262+
systemMessage: {
1263+
mode: "customize",
1264+
sections: {
1265+
tone: { action: "replace", content: "Respond in a warm, professional tone. Be thorough in explanations." },
1266+
code_change_rules: { action: "remove" },
1267+
guidelines: { action: "append", content: "\n* Always cite data sources" },
1268+
},
1269+
content: "Focus on financial analysis and reporting.",
1270+
},
1271+
});
1272+
```
1273+
1274+
Available section IDs: `identity`, `tone`, `tool_efficiency`, `environment_context`, `code_change_rules`, `guidelines`, `safety`, `tool_instructions`, `custom_instructions`.
1275+
1276+
Each override supports four actions: `replace`, `remove`, `append`, and `prepend`. Unknown section IDs are handled gracefully — content is appended to additional instructions and a warning is emitted; `remove` on unknown sections is silently ignored.
1277+
1278+
See the language-specific SDK READMEs for examples in [TypeScript](../nodejs/README.md), [Python](../python/README.md), [Go](../go/README.md), and [C#](../dotnet/README.md).
1279+
12581280
---
12591281

12601282
## Connecting to an External CLI Server

dotnet/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,34 @@ var session = await client.CreateSessionAsync(new SessionConfig
469469
});
470470
```
471471

472+
#### Customize Mode
473+
474+
Use `Mode = SystemMessageMode.Customize` to selectively override individual sections of the prompt while preserving the rest:
475+
476+
```csharp
477+
var session = await client.CreateSessionAsync(new SessionConfig
478+
{
479+
Model = "gpt-5",
480+
SystemMessage = new SystemMessageConfig
481+
{
482+
Mode = SystemMessageMode.Customize,
483+
Sections = new Dictionary<string, SectionOverride>
484+
{
485+
[SystemPromptSections.Tone] = new() { Action = SectionOverrideAction.Replace, Content = "Respond in a warm, professional tone. Be thorough in explanations." },
486+
[SystemPromptSections.CodeChangeRules] = new() { Action = SectionOverrideAction.Remove },
487+
[SystemPromptSections.Guidelines] = new() { Action = SectionOverrideAction.Append, Content = "\n* Always cite data sources" },
488+
},
489+
Content = "Focus on financial analysis and reporting."
490+
}
491+
});
492+
```
493+
494+
Available section IDs are defined as constants on `SystemPromptSections`: `Identity`, `Tone`, `ToolEfficiency`, `EnvironmentContext`, `CodeChangeRules`, `Guidelines`, `Safety`, `ToolInstructions`, `CustomInstructions`.
495+
496+
Each section override supports four actions: `Replace`, `Remove`, `Append`, and `Prepend`. Unknown section IDs are handled gracefully: content is appended to additional instructions, and `Remove` overrides are silently ignored.
497+
498+
#### Replace Mode
499+
472500
For full control (removes all guardrails), use `Mode = SystemMessageMode.Replace`:
473501

474502
```csharp

dotnet/src/Client.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,7 @@ private static LogLevel MapLevel(TraceEventType eventType)
16461646
[JsonSerializable(typeof(ProviderConfig))]
16471647
[JsonSerializable(typeof(ResumeSessionRequest))]
16481648
[JsonSerializable(typeof(ResumeSessionResponse))]
1649+
[JsonSerializable(typeof(SectionOverride))]
16491650
[JsonSerializable(typeof(SessionMetadata))]
16501651
[JsonSerializable(typeof(SystemMessageConfig))]
16511652
[JsonSerializable(typeof(ToolCallResponseV2))]

dotnet/src/SdkProtocolVersion.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,5 @@ internal static class SdkProtocolVersion
1616
/// <summary>
1717
/// Gets the SDK protocol version.
1818
/// </summary>
19-
public static int GetVersion()
20-
{
21-
return Version;
22-
}
19+
public static int GetVersion() => Version;
2320
}

dotnet/src/Types.cs

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,71 @@ public enum SystemMessageMode
912912
Append,
913913
/// <summary>Replace the default system message entirely.</summary>
914914
[JsonStringEnumMemberName("replace")]
915-
Replace
915+
Replace,
916+
/// <summary>Override individual sections of the system prompt.</summary>
917+
[JsonStringEnumMemberName("customize")]
918+
Customize
919+
}
920+
921+
/// <summary>
922+
/// Specifies the operation to perform on a system prompt section.
923+
/// </summary>
924+
[JsonConverter(typeof(JsonStringEnumConverter<SectionOverrideAction>))]
925+
public enum SectionOverrideAction
926+
{
927+
/// <summary>Replace the section content entirely.</summary>
928+
[JsonStringEnumMemberName("replace")]
929+
Replace,
930+
/// <summary>Remove the section from the prompt.</summary>
931+
[JsonStringEnumMemberName("remove")]
932+
Remove,
933+
/// <summary>Append content after the existing section.</summary>
934+
[JsonStringEnumMemberName("append")]
935+
Append,
936+
/// <summary>Prepend content before the existing section.</summary>
937+
[JsonStringEnumMemberName("prepend")]
938+
Prepend
939+
}
940+
941+
/// <summary>
942+
/// Override operation for a single system prompt section.
943+
/// </summary>
944+
public class SectionOverride
945+
{
946+
/// <summary>
947+
/// The operation to perform on this section.
948+
/// </summary>
949+
public SectionOverrideAction Action { get; set; }
950+
951+
/// <summary>
952+
/// Content for the override. Optional for all actions. Ignored for remove.
953+
/// </summary>
954+
public string? Content { get; set; }
955+
}
956+
957+
/// <summary>
958+
/// Known system prompt section identifiers for the "customize" mode.
959+
/// </summary>
960+
public static class SystemPromptSections
961+
{
962+
/// <summary>Agent identity preamble and mode statement.</summary>
963+
public const string Identity = "identity";
964+
/// <summary>Response style, conciseness rules, output formatting preferences.</summary>
965+
public const string Tone = "tone";
966+
/// <summary>Tool usage patterns, parallel calling, batching guidelines.</summary>
967+
public const string ToolEfficiency = "tool_efficiency";
968+
/// <summary>CWD, OS, git root, directory listing, available tools.</summary>
969+
public const string EnvironmentContext = "environment_context";
970+
/// <summary>Coding rules, linting/testing, ecosystem tools, style.</summary>
971+
public const string CodeChangeRules = "code_change_rules";
972+
/// <summary>Tips, behavioral best practices, behavioral guidelines.</summary>
973+
public const string Guidelines = "guidelines";
974+
/// <summary>Environment limitations, prohibited actions, security policies.</summary>
975+
public const string Safety = "safety";
976+
/// <summary>Per-tool usage instructions.</summary>
977+
public const string ToolInstructions = "tool_instructions";
978+
/// <summary>Repository and organization custom instructions.</summary>
979+
public const string CustomInstructions = "custom_instructions";
916980
}
917981

918982
/// <summary>
@@ -921,13 +985,21 @@ public enum SystemMessageMode
921985
public class SystemMessageConfig
922986
{
923987
/// <summary>
924-
/// How the system message is applied (append or replace).
988+
/// How the system message is applied (append, replace, or customize).
925989
/// </summary>
926990
public SystemMessageMode? Mode { get; set; }
991+
927992
/// <summary>
928-
/// Content of the system message.
993+
/// Content of the system message. Used by append and replace modes.
994+
/// In customize mode, additional content appended after all sections.
929995
/// </summary>
930996
public string? Content { get; set; }
997+
998+
/// <summary>
999+
/// Section-level overrides for customize mode.
1000+
/// Keys are section identifiers (see <see cref="SystemPromptSections"/>).
1001+
/// </summary>
1002+
public Dictionary<string, SectionOverride>? Sections { get; set; }
9311003
}
9321004

9331005
/// <summary>
@@ -2005,6 +2077,7 @@ public class SetForegroundSessionResponse
20052077
[JsonSerializable(typeof(SessionLifecycleEvent))]
20062078
[JsonSerializable(typeof(SessionLifecycleEventMetadata))]
20072079
[JsonSerializable(typeof(SessionListFilter))]
2080+
[JsonSerializable(typeof(SectionOverride))]
20082081
[JsonSerializable(typeof(SessionMetadata))]
20092082
[JsonSerializable(typeof(SetForegroundSessionResponse))]
20102083
[JsonSerializable(typeof(SystemMessageConfig))]

go/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,10 @@ Event types: `SessionLifecycleCreated`, `SessionLifecycleDeleted`, `SessionLifec
148148
- `ReasoningEffort` (string): Reasoning effort level for models that support it ("low", "medium", "high", "xhigh"). Use `ListModels()` to check which models support this option.
149149
- `SessionID` (string): Custom session ID
150150
- `Tools` ([]Tool): Custom tools exposed to the CLI
151-
- `SystemMessage` (\*SystemMessageConfig): System message configuration
151+
- `SystemMessage` (\*SystemMessageConfig): System message configuration. Supports three modes:
152+
- **append** (default): Appends `Content` after the SDK-managed prompt
153+
- **replace**: Replaces the entire prompt with `Content`
154+
- **customize**: Selectively override individual sections via `Sections` map (keys: `SectionIdentity`, `SectionTone`, `SectionToolEfficiency`, `SectionEnvironmentContext`, `SectionCodeChangeRules`, `SectionGuidelines`, `SectionSafety`, `SectionToolInstructions`, `SectionCustomInstructions`; values: `SectionOverride` with `Action` and optional `Content`)
152155
- `Provider` (\*ProviderConfig): Custom API provider configuration (BYOK). See [Custom Providers](#custom-providers) section.
153156
- `Streaming` (bool): Enable streaming delta events
154157
- `InfiniteSessions` (\*InfiniteSessionConfig): Automatic context compaction configuration

go/types.go

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,27 @@ func Float64(v float64) *float64 {
8181
return &v
8282
}
8383

84+
// Known system prompt section identifiers for the "customize" mode.
85+
const (
86+
SectionIdentity = "identity"
87+
SectionTone = "tone"
88+
SectionToolEfficiency = "tool_efficiency"
89+
SectionEnvironmentContext = "environment_context"
90+
SectionCodeChangeRules = "code_change_rules"
91+
SectionGuidelines = "guidelines"
92+
SectionSafety = "safety"
93+
SectionToolInstructions = "tool_instructions"
94+
SectionCustomInstructions = "custom_instructions"
95+
)
96+
97+
// SectionOverride defines an override operation for a single system prompt section.
98+
type SectionOverride struct {
99+
// Action is the operation to perform: "replace", "remove", "append", or "prepend".
100+
Action string `json:"action"`
101+
// Content for the override. Optional for all actions. Ignored for "remove".
102+
Content string `json:"content,omitempty"`
103+
}
104+
84105
// SystemMessageAppendConfig is append mode: use CLI foundation with optional appended content.
85106
type SystemMessageAppendConfig struct {
86107
// Mode is optional, defaults to "append"
@@ -99,11 +120,15 @@ type SystemMessageReplaceConfig struct {
99120
}
100121

101122
// SystemMessageConfig represents system message configuration for session creation.
102-
// Use SystemMessageAppendConfig for default behavior, SystemMessageReplaceConfig for full control.
103-
// In Go, use one struct or the other based on your needs.
123+
// - Append mode (default): SDK foundation + optional custom content
124+
// - Replace mode: Full control, caller provides entire system message
125+
// - Customize mode: Section-level overrides with graceful fallback
126+
//
127+
// In Go, use one struct and set fields appropriate for the desired mode.
104128
type SystemMessageConfig struct {
105-
Mode string `json:"mode,omitempty"`
106-
Content string `json:"content,omitempty"`
129+
Mode string `json:"mode,omitempty"`
130+
Content string `json:"content,omitempty"`
131+
Sections map[string]SectionOverride `json:"sections,omitempty"`
107132
}
108133

109134
// PermissionRequestResultKind represents the kind of a permission request result.

nodejs/README.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,45 @@ const session = await client.createSession({
443443
});
444444
```
445445

446-
The SDK auto-injects environment context, tool instructions, and security guardrails. The default CLI persona is preserved, and your `content` is appended after SDK-managed sections. To change the persona or fully redefine the prompt, use `mode: "replace"`.
446+
The SDK auto-injects environment context, tool instructions, and security guardrails. The default CLI persona is preserved, and your `content` is appended after SDK-managed sections. To change the persona or fully redefine the prompt, use `mode: "replace"` or `mode: "customize"`.
447+
448+
#### Customize Mode
449+
450+
Use `mode: "customize"` to selectively override individual sections of the prompt while preserving the rest:
451+
452+
```typescript
453+
import { SYSTEM_PROMPT_SECTIONS } from "@anthropic-ai/sdk";
454+
import type { SectionOverride, SystemPromptSection } from "@anthropic-ai/sdk";
455+
456+
const session = await client.createSession({
457+
model: "gpt-5",
458+
systemMessage: {
459+
mode: "customize",
460+
sections: {
461+
// Replace the tone/style section
462+
tone: { action: "replace", content: "Respond in a warm, professional tone. Be thorough in explanations." },
463+
// Remove coding-specific rules
464+
code_change_rules: { action: "remove" },
465+
// Append to existing guidelines
466+
guidelines: { action: "append", content: "\n* Always cite data sources" },
467+
},
468+
// Additional instructions appended after all sections
469+
content: "Focus on financial analysis and reporting.",
470+
},
471+
});
472+
```
473+
474+
Available section IDs: `identity`, `tone`, `tool_efficiency`, `environment_context`, `code_change_rules`, `guidelines`, `safety`, `tool_instructions`, `custom_instructions`. Use the `SYSTEM_PROMPT_SECTIONS` constant for descriptions of each section.
475+
476+
Each section override supports four actions:
477+
- **`replace`** — Replace the section content entirely
478+
- **`remove`** — Remove the section from the prompt
479+
- **`append`** — Add content after the existing section
480+
- **`prepend`** — Add content before the existing section
481+
482+
Unknown section IDs are handled gracefully: content from `replace`/`append`/`prepend` overrides is appended to additional instructions, and `remove` overrides are silently ignored.
483+
484+
#### Replace Mode
447485

448486
For full control (removes all guardrails), use `mode: "replace"`:
449487

nodejs/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
export { CopilotClient } from "./client.js";
1212
export { CopilotSession, type AssistantMessageEvent } from "./session.js";
13-
export { defineTool, approveAll } from "./types.js";
13+
export { defineTool, approveAll, SYSTEM_PROMPT_SECTIONS } from "./types.js";
1414
export type {
1515
ConnectionState,
1616
CopilotClientOptions,
@@ -31,6 +31,7 @@ export type {
3131
PermissionRequest,
3232
PermissionRequestResult,
3333
ResumeSessionConfig,
34+
SectionOverride,
3435
SessionConfig,
3536
SessionEvent,
3637
SessionEventHandler,
@@ -44,7 +45,9 @@ export type {
4445
SessionMetadata,
4546
SystemMessageAppendConfig,
4647
SystemMessageConfig,
48+
SystemMessageCustomizeConfig,
4749
SystemMessageReplaceConfig,
50+
SystemPromptSection,
4851
Tool,
4952
ToolHandler,
5053
ToolInvocation,

0 commit comments

Comments
 (0)