Skip to content

Commit 4fc09a1

Browse files
MackinnonBuckCopilot
authored andcommitted
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 2cca386 commit 4fc09a1

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
@@ -182,6 +182,52 @@ Event types: `SessionLifecycleCreated`, `SessionLifecycleDeleted`, `SessionLifec
182182

183183
- `Bool(v bool) *bool` - Helper to create bool pointers for `AutoStart` option
184184

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

187233
The SDK supports image attachments via the `Attachments` field in `MessageOptions`. You can attach images by providing their file path, or by passing base64-encoded data directly using a blob attachment:

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
@@ -480,8 +480,8 @@ The SDK auto-injects environment context, tool instructions, and security guardr
480480
Use `mode: "customize"` to selectively override individual sections of the prompt while preserving the rest:
481481

482482
```typescript
483-
import { SYSTEM_PROMPT_SECTIONS } from "@anthropic-ai/sdk";
484-
import type { SectionOverride, SystemPromptSection } from "@anthropic-ai/sdk";
483+
import { SYSTEM_PROMPT_SECTIONS } from "@github/copilot-sdk";
484+
import type { SectionOverride, SystemPromptSection } from "@github/copilot-sdk";
485485

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

python/README.md

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

223+
### System Message Customization
224+
225+
Control the system prompt using `system_message` in session config:
226+
227+
```python
228+
session = await client.create_session(
229+
system_message={
230+
"content": "Always check for security vulnerabilities before suggesting changes."
231+
}
232+
)
233+
```
234+
235+
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"`.
236+
237+
#### Customize Mode
238+
239+
Use `mode: "customize"` to selectively override individual sections of the prompt while preserving the rest:
240+
241+
```python
242+
from copilot import SYSTEM_PROMPT_SECTIONS
243+
244+
session = await client.create_session(
245+
system_message={
246+
"mode": "customize",
247+
"sections": {
248+
# Replace the tone/style section
249+
"tone": {"action": "replace", "content": "Respond in a warm, professional tone. Be thorough in explanations."},
250+
# Remove coding-specific rules
251+
"code_change_rules": {"action": "remove"},
252+
# Append to existing guidelines
253+
"guidelines": {"action": "append", "content": "\n* Always cite data sources"},
254+
},
255+
# Additional instructions appended after all sections
256+
"content": "Focus on financial analysis and reporting.",
257+
}
258+
)
259+
```
260+
261+
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.
262+
263+
Each section override supports four actions:
264+
- **`replace`** — Replace the section content entirely
265+
- **`remove`** — Remove the section from the prompt
266+
- **`append`** — Add content after the existing section
267+
- **`prepend`** — Add content before the existing section
268+
269+
Unknown section IDs are handled gracefully: content from `replace`/`append`/`prepend` overrides is appended to additional instructions, and `remove` overrides are silently ignored.
270+
223271
### Tools
224272

225273
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
@@ -82,6 +82,35 @@ async def test_should_create_a_session_with_replaced_systemMessage_config(
8282
system_message = _get_system_message(traffic[0])
8383
assert system_message == test_system_message # Exact match
8484

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

0 commit comments

Comments
 (0)