Skip to content

Commit a63c606

Browse files
Address PR review feedback: fix docs, add examples and E2E tests
- Fix incorrect package name in nodejs/README.md (@anthropic-ai/sdk -> @github/copilot-sdk) - Add standalone 'System Message Customization' sections with full code examples to Python and Go READMEs (matching TypeScript/.NET) - Add E2E tests for customize mode to Python, Go, and .NET (matching existing Node.js E2E test coverage) - Fix 'end of the prompt' wording in docs to 'additional instructions' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 72039e1 commit a63c606

File tree

6 files changed

+201
-2
lines changed

6 files changed

+201
-2
lines changed

dotnet/test/SessionTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,37 @@ public async Task Should_Create_A_Session_With_Replaced_SystemMessage_Config()
9191
Assert.Equal(testSystemMessage, GetSystemMessage(traffic[0]));
9292
}
9393

94+
[Fact]
95+
public async Task Should_Create_A_Session_With_Customized_SystemMessage_Config()
96+
{
97+
var customTone = "Respond in a warm, professional tone. Be thorough in explanations.";
98+
var appendedContent = "Always mention quarterly earnings.";
99+
var session = await CreateSessionAsync(new SessionConfig
100+
{
101+
SystemMessage = new SystemMessageConfig
102+
{
103+
Mode = SystemMessageMode.Customize,
104+
Sections = new Dictionary<string, SectionOverride>
105+
{
106+
[SystemPromptSections.Tone] = new() { Action = SectionOverrideAction.Replace, Content = customTone },
107+
[SystemPromptSections.CodeChangeRules] = new() { Action = SectionOverrideAction.Remove },
108+
},
109+
Content = appendedContent
110+
}
111+
});
112+
113+
await session.SendAsync(new MessageOptions { Prompt = "Who are you?" });
114+
var assistantMessage = await TestHelper.GetFinalAssistantMessageAsync(session);
115+
Assert.NotNull(assistantMessage);
116+
117+
var traffic = await Ctx.GetExchangesAsync();
118+
Assert.NotEmpty(traffic);
119+
var systemMessage = GetSystemMessage(traffic[0]);
120+
Assert.Contains(customTone, systemMessage);
121+
Assert.Contains(appendedContent, systemMessage);
122+
Assert.DoesNotContain("<code_change_instructions>", systemMessage);
123+
}
124+
94125
[Fact]
95126
public async Task Should_Create_A_Session_With_AvailableTools()
96127
{

go/README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,52 @@ Event types: `SessionLifecycleCreated`, `SessionLifecycleDeleted`, `SessionLifec
178178

179179
- `Bool(v bool) *bool` - Helper to create bool pointers for `AutoStart` option
180180

181+
### System Message Customization
182+
183+
Control the system prompt using `SystemMessage` in session config:
184+
185+
```go
186+
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
187+
SystemMessage: &copilot.SystemMessageConfig{
188+
Content: "Always check for security vulnerabilities before suggesting changes.",
189+
},
190+
})
191+
```
192+
193+
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"`.
194+
195+
#### Customize Mode
196+
197+
Use `Mode: "customize"` to selectively override individual sections of the prompt while preserving the rest:
198+
199+
```go
200+
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
201+
SystemMessage: &copilot.SystemMessageConfig{
202+
Mode: "customize",
203+
Sections: map[string]copilot.SectionOverride{
204+
// Replace the tone/style section
205+
copilot.SectionTone: {Action: "replace", Content: "Respond in a warm, professional tone. Be thorough in explanations."},
206+
// Remove coding-specific rules
207+
copilot.SectionCodeChangeRules: {Action: "remove"},
208+
// Append to existing guidelines
209+
copilot.SectionGuidelines: {Action: "append", Content: "\n* Always cite data sources"},
210+
},
211+
// Additional instructions appended after all sections
212+
Content: "Focus on financial analysis and reporting.",
213+
},
214+
})
215+
```
216+
217+
Available section constants: `SectionIdentity`, `SectionTone`, `SectionToolEfficiency`, `SectionEnvironmentContext`, `SectionCodeChangeRules`, `SectionGuidelines`, `SectionSafety`, `SectionToolInstructions`, `SectionCustomInstructions`.
218+
219+
Each section override supports four actions:
220+
- **`replace`** — Replace the section content entirely
221+
- **`remove`** — Remove the section from the prompt
222+
- **`append`** — Add content after the existing section
223+
- **`prepend`** — Add content before the existing section
224+
225+
Unknown section IDs are handled gracefully: content from `replace`/`append`/`prepend` overrides is appended to additional instructions, and `remove` overrides are silently ignored.
226+
181227
## Image Support
182228

183229
The SDK supports image attachments via the `Attachments` field in `MessageOptions`. You can attach images by providing their file path:

go/internal/e2e/session_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,51 @@ func TestSession(t *testing.T) {
184184
}
185185
})
186186

187+
t.Run("should create a session with customized systemMessage config", func(t *testing.T) {
188+
ctx.ConfigureForTest(t)
189+
190+
customTone := "Respond in a warm, professional tone. Be thorough in explanations."
191+
appendedContent := "Always mention quarterly earnings."
192+
session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{
193+
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
194+
SystemMessage: &copilot.SystemMessageConfig{
195+
Mode: "customize",
196+
Sections: map[string]copilot.SectionOverride{
197+
copilot.SectionTone: {Action: "replace", Content: customTone},
198+
copilot.SectionCodeChangeRules: {Action: "remove"},
199+
},
200+
Content: appendedContent,
201+
},
202+
})
203+
if err != nil {
204+
t.Fatalf("Failed to create session: %v", err)
205+
}
206+
207+
_, err = session.SendAndWait(t.Context(), copilot.MessageOptions{Prompt: "Who are you?"})
208+
if err != nil {
209+
t.Fatalf("Failed to send message: %v", err)
210+
}
211+
212+
// Validate the system message sent to the model
213+
traffic, err := ctx.GetExchanges()
214+
if err != nil {
215+
t.Fatalf("Failed to get exchanges: %v", err)
216+
}
217+
if len(traffic) == 0 {
218+
t.Fatal("Expected at least one exchange")
219+
}
220+
systemMessage := getSystemMessage(traffic[0])
221+
if !strings.Contains(systemMessage, customTone) {
222+
t.Errorf("Expected system message to contain custom tone, got %q", systemMessage)
223+
}
224+
if !strings.Contains(systemMessage, appendedContent) {
225+
t.Errorf("Expected system message to contain appended content, got %q", systemMessage)
226+
}
227+
if strings.Contains(systemMessage, "<code_change_instructions>") {
228+
t.Error("Expected system message to NOT contain code_change_instructions (it was removed)")
229+
}
230+
})
231+
187232
t.Run("should create a session with availableTools", func(t *testing.T) {
188233
ctx.ConfigureForTest(t)
189234

nodejs/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,8 +450,8 @@ The SDK auto-injects environment context, tool instructions, and security guardr
450450
Use `mode: "customize"` to selectively override individual sections of the prompt while preserving the rest:
451451

452452
```typescript
453-
import { SYSTEM_PROMPT_SECTIONS } from "@anthropic-ai/sdk";
454-
import type { SectionOverride, SystemPromptSection } from "@anthropic-ai/sdk";
453+
import { SYSTEM_PROMPT_SECTIONS } from "@github/copilot-sdk";
454+
import type { SectionOverride, SystemPromptSection } from "@github/copilot-sdk";
455455

456456
const session = await client.createSession({
457457
model: "gpt-5",

python/README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,54 @@ unsubscribe()
158158
- `session.foreground` - A session became the foreground session in TUI
159159
- `session.background` - A session is no longer the foreground session
160160

161+
### System Message Customization
162+
163+
Control the system prompt using `system_message` in session config:
164+
165+
```python
166+
session = await client.create_session(
167+
system_message={
168+
"content": "Always check for security vulnerabilities before suggesting changes."
169+
}
170+
)
171+
```
172+
173+
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"`.
174+
175+
#### Customize Mode
176+
177+
Use `mode: "customize"` to selectively override individual sections of the prompt while preserving the rest:
178+
179+
```python
180+
from copilot import SYSTEM_PROMPT_SECTIONS
181+
182+
session = await client.create_session(
183+
system_message={
184+
"mode": "customize",
185+
"sections": {
186+
# Replace the tone/style section
187+
"tone": {"action": "replace", "content": "Respond in a warm, professional tone. Be thorough in explanations."},
188+
# Remove coding-specific rules
189+
"code_change_rules": {"action": "remove"},
190+
# Append to existing guidelines
191+
"guidelines": {"action": "append", "content": "\n* Always cite data sources"},
192+
},
193+
# Additional instructions appended after all sections
194+
"content": "Focus on financial analysis and reporting.",
195+
}
196+
)
197+
```
198+
199+
Available section IDs: `"identity"`, `"tone"`, `"tool_efficiency"`, `"environment_context"`, `"code_change_rules"`, `"guidelines"`, `"safety"`, `"tool_instructions"`, `"custom_instructions"`. Use the `SYSTEM_PROMPT_SECTIONS` dict for descriptions of each section.
200+
201+
Each section override supports four actions:
202+
- **`replace`** — Replace the section content entirely
203+
- **`remove`** — Remove the section from the prompt
204+
- **`append`** — Add content after the existing section
205+
- **`prepend`** — Add content before the existing section
206+
207+
Unknown section IDs are handled gracefully: content from `replace`/`append`/`prepend` overrides is appended to additional instructions, and `remove` overrides are silently ignored.
208+
161209
### Tools
162210

163211
Define tools with automatic JSON schema generation using the `@define_tool` decorator and Pydantic models:

python/e2e/test_session.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,35 @@ async def test_should_create_a_session_with_replaced_systemMessage_config(
8888
system_message = _get_system_message(traffic[0])
8989
assert system_message == test_system_message # Exact match
9090

91+
async def test_should_create_a_session_with_customized_systemMessage_config(
92+
self, ctx: E2ETestContext
93+
):
94+
custom_tone = "Respond in a warm, professional tone. Be thorough in explanations."
95+
appended_content = "Always mention quarterly earnings."
96+
session = await ctx.client.create_session(
97+
{
98+
"system_message": {
99+
"mode": "customize",
100+
"sections": {
101+
"tone": {"action": "replace", "content": custom_tone},
102+
"code_change_rules": {"action": "remove"},
103+
},
104+
"content": appended_content,
105+
},
106+
"on_permission_request": PermissionHandler.approve_all,
107+
}
108+
)
109+
110+
assistant_message = await session.send_and_wait({"prompt": "Who are you?"})
111+
assert assistant_message is not None
112+
113+
# Validate the system message sent to the model
114+
traffic = await ctx.get_exchanges()
115+
system_message = _get_system_message(traffic[0])
116+
assert custom_tone in system_message
117+
assert appended_content in system_message
118+
assert "<code_change_instructions>" not in system_message
119+
91120
async def test_should_create_a_session_with_availableTools(self, ctx: E2ETestContext):
92121
session = await ctx.client.create_session(
93122
{

0 commit comments

Comments
 (0)