Skip to content

Commit e31c878

Browse files
edburnsCopilot
andcommitted
Rename SystemPromptSections → SystemMessageSections for cross-SDK consistency
Introduce `SystemMessageSections` as a sealed superclass containing all well-known section identifier constants. The existing `SystemPromptSections` becomes a deprecated final subclass that inherits everything unchanged, preserving backward compatibility. New files: - `SystemMessageSections.java`: Sealed class with all `public static final String` constants (IDENTITY, TONE, TOOL_EFFICIENCY, etc.) and Javadoc matching the naming convention used by Node, Python, .NET, Go, and Rust SDKs. - `SystemMessageSectionsIT.java`: Failsafe integration test that validates transform callbacks on IDENTITY and TONE sections receive non-empty content from the live CLI, plus an equivalence test ensuring the deprecated subclass inherits all constants correctly. Modified files: - `SystemPromptSections.java`: Gutted to an empty `@Deprecated(since="1.0.2", forRemoval=true)` final class extending `SystemMessageSections`. - `SystemMessageConfig.java`: Updated Javadoc references from `SystemPromptSections` to `SystemMessageSections`. - `SectionOverride.java`: Updated Javadoc references from `SystemPromptSections` to `SystemMessageSections`. Fixes #1679 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent edb5f7a commit e31c878

5 files changed

Lines changed: 218 additions & 71 deletions

File tree

java/src/main/java/com/github/copilot/rpc/SectionOverride.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,33 @@
1212
import com.fasterxml.jackson.annotation.JsonProperty;
1313

1414
/**
15-
* Override operation for a single system prompt section in
15+
* Override operation for a single system message section in
1616
* {@link SystemMessageMode#CUSTOMIZE} mode.
1717
* <p>
1818
* Each {@code SectionOverride} describes how one named section of the default
19-
* system prompt should be modified. The section name keys come from
20-
* {@link SystemPromptSections}.
19+
* system message should be modified. The section name keys come from
20+
* {@link SystemMessageSections}.
2121
*
2222
* <h2>Static override example</h2>
2323
*
2424
* <pre>{@code
2525
* var config = new SystemMessageConfig().setMode(SystemMessageMode.CUSTOMIZE).setSections(Map.of(
26-
* SystemPromptSections.TONE,
26+
* SystemMessageSections.TONE,
2727
* new SectionOverride().setAction(SectionOverrideAction.REPLACE).setContent("Be concise and formal."),
28-
* SystemPromptSections.CODE_CHANGE_RULES, new SectionOverride().setAction(SectionOverrideAction.REMOVE)));
28+
* SystemMessageSections.CODE_CHANGE_RULES, new SectionOverride().setAction(SectionOverrideAction.REMOVE)));
2929
* }</pre>
3030
*
3131
* <h2>Transform callback example</h2>
3232
*
3333
* <pre>{@code
3434
* var config = new SystemMessageConfig().setMode(SystemMessageMode.CUSTOMIZE)
35-
* .setSections(Map.of(SystemPromptSections.IDENTITY, new SectionOverride().setTransform(
35+
* .setSections(Map.of(SystemMessageSections.IDENTITY, new SectionOverride().setTransform(
3636
* content -> CompletableFuture.completedFuture(content + "\nAlways end replies with DONE."))));
3737
* }</pre>
3838
*
3939
* @see SystemMessageConfig
4040
* @see SectionOverrideAction
41-
* @see SystemPromptSections
41+
* @see SystemMessageSections
4242
* @since 1.2.0
4343
*/
4444
@JsonInclude(JsonInclude.Include.NON_NULL)

java/src/main/java/com/github/copilot/rpc/SystemMessageConfig.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,18 @@
3535
* <pre>{@code
3636
* var config = new SystemMessageConfig().setMode(SystemMessageMode.CUSTOMIZE)
3737
* .setSections(
38-
* Map.of(SystemPromptSections.TONE,
38+
* Map.of(SystemMessageSections.TONE,
3939
* new SectionOverride().setAction(SectionOverrideAction.REPLACE)
4040
* .setContent("Be concise and formal."),
41-
* SystemPromptSections.CODE_CHANGE_RULES,
41+
* SystemMessageSections.CODE_CHANGE_RULES,
4242
* new SectionOverride().setAction(SectionOverrideAction.REMOVE)))
4343
* .setContent("Additional instructions appended after all sections.");
4444
* }</pre>
4545
*
4646
* @see SessionConfig#setSystemMessage(SystemMessageConfig)
4747
* @see SystemMessageMode
4848
* @see SectionOverride
49-
* @see SystemPromptSections
49+
* @see SystemMessageSections
5050
* @since 1.0.0
5151
*/
5252
@JsonInclude(JsonInclude.Include.NON_NULL)
@@ -122,7 +122,7 @@ public Map<String, SectionOverride> getSections() {
122122
/**
123123
* Sets section-level overrides for {@link SystemMessageMode#CUSTOMIZE} mode.
124124
* <p>
125-
* Keys are section identifiers from {@link SystemPromptSections}. Each value
125+
* Keys are section identifiers from {@link SystemMessageSections}. Each value
126126
* describes how that section should be modified. Sections with a
127127
* {@link SectionOverride#getTransform() transform} callback are handled locally
128128
* by the SDK via a {@code systemMessage.transform} RPC call; the rest are sent
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot.rpc;
6+
7+
/**
8+
* Well-known system message section identifiers for use with
9+
* {@link SystemMessageMode#CUSTOMIZE} mode.
10+
* <p>
11+
* Each constant names a section of the default Copilot system message. Pass
12+
* these as keys in the {@code sections} map of {@link SystemMessageConfig} to
13+
* override individual sections.
14+
*
15+
* <h2>Example</h2>
16+
*
17+
* <pre>{@code
18+
* var config = new SystemMessageConfig().setMode(SystemMessageMode.CUSTOMIZE).setSections(Map.of(
19+
* SystemMessageSections.TONE,
20+
* new SectionOverride().setAction(SectionOverrideAction.REPLACE).setContent("Always be concise."),
21+
* SystemMessageSections.CODE_CHANGE_RULES, new SectionOverride().setAction(SectionOverrideAction.REMOVE)));
22+
* }</pre>
23+
*
24+
* @see SystemMessageConfig
25+
* @see SectionOverride
26+
* @since 1.0.2
27+
*/
28+
public sealed class SystemMessageSections permits SystemPromptSections {
29+
30+
/** Agent identity preamble and mode statement. */
31+
public static final String IDENTITY = "identity";
32+
33+
/** Response style, conciseness rules, output formatting preferences. */
34+
public static final String TONE = "tone";
35+
36+
/** Tool usage patterns, parallel calling, batching guidelines. */
37+
public static final String TOOL_EFFICIENCY = "tool_efficiency";
38+
39+
/** CWD, OS, git root, directory listing, available tools. */
40+
public static final String ENVIRONMENT_CONTEXT = "environment_context";
41+
42+
/** Coding rules, linting/testing, ecosystem tools, style. */
43+
public static final String CODE_CHANGE_RULES = "code_change_rules";
44+
45+
/** Tips, behavioral best practices, behavioral guidelines. */
46+
public static final String GUIDELINES = "guidelines";
47+
48+
/** Environment limitations, prohibited actions, security policies. */
49+
public static final String SAFETY = "safety";
50+
51+
/** Per-tool usage instructions. */
52+
public static final String TOOL_INSTRUCTIONS = "tool_instructions";
53+
54+
/** Repository and organization custom instructions. */
55+
public static final String CUSTOM_INSTRUCTIONS = "custom_instructions";
56+
57+
/**
58+
* Runtime-provided context and instructions (e.g. system notifications,
59+
* memories, workspace context, mode-specific instructions, content-exclusion
60+
* policy).
61+
*
62+
* @since 1.3.0
63+
*/
64+
public static final String RUNTIME_INSTRUCTIONS = "runtime_instructions";
65+
66+
/**
67+
* End-of-prompt instructions: parallel tool calling, persistence, task
68+
* completion.
69+
*/
70+
public static final String LAST_INSTRUCTIONS = "last_instructions";
71+
72+
/** Package-private constructor for the sealed hierarchy. */
73+
SystemMessageSections() {
74+
}
75+
}

java/src/main/java/com/github/copilot/rpc/SystemPromptSections.java

Lines changed: 8 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,71 +5,19 @@
55
package com.github.copilot.rpc;
66

77
/**
8-
* Well-known system prompt section identifiers for use with
9-
* {@link SystemMessageMode#CUSTOMIZE} mode.
8+
* Deprecated: use {@link SystemMessageSections} instead.
109
* <p>
11-
* Each constant names a section of the default Copilot system prompt. Pass
12-
* these as keys in the {@code sections} map of {@link SystemMessageConfig} to
13-
* override individual sections.
10+
* This class is retained for backward compatibility. All constants are
11+
* inherited from {@link SystemMessageSections}.
1412
*
15-
* <h2>Example</h2>
16-
*
17-
* <pre>{@code
18-
* var config = new SystemMessageConfig().setMode(SystemMessageMode.CUSTOMIZE).setSections(Map.of(
19-
* SystemPromptSections.TONE,
20-
* new SectionOverride().setAction(SectionOverrideAction.REPLACE).setContent("Always be concise."),
21-
* SystemPromptSections.CODE_CHANGE_RULES, new SectionOverride().setAction(SectionOverrideAction.REMOVE)));
22-
* }</pre>
23-
*
24-
* @see SystemMessageConfig
25-
* @see SectionOverride
13+
* @deprecated Use {@link SystemMessageSections} — this class will be removed in
14+
* a future major version.
15+
* @see SystemMessageSections
2616
* @since 1.2.0
2717
*/
28-
public final class SystemPromptSections {
29-
30-
/** Agent identity preamble and mode statement. */
31-
public static final String IDENTITY = "identity";
32-
33-
/** Response style, conciseness rules, output formatting preferences. */
34-
public static final String TONE = "tone";
35-
36-
/** Tool usage patterns, parallel calling, batching guidelines. */
37-
public static final String TOOL_EFFICIENCY = "tool_efficiency";
38-
39-
/** CWD, OS, git root, directory listing, available tools. */
40-
public static final String ENVIRONMENT_CONTEXT = "environment_context";
41-
42-
/** Coding rules, linting/testing, ecosystem tools, style. */
43-
public static final String CODE_CHANGE_RULES = "code_change_rules";
44-
45-
/** Tips, behavioral best practices, behavioral guidelines. */
46-
public static final String GUIDELINES = "guidelines";
47-
48-
/** Environment limitations, prohibited actions, security policies. */
49-
public static final String SAFETY = "safety";
50-
51-
/** Per-tool usage instructions. */
52-
public static final String TOOL_INSTRUCTIONS = "tool_instructions";
53-
54-
/** Repository and organization custom instructions. */
55-
public static final String CUSTOM_INSTRUCTIONS = "custom_instructions";
56-
57-
/**
58-
* Runtime-provided context and instructions (e.g. system notifications,
59-
* memories, workspace context, mode-specific instructions, content-exclusion
60-
* policy).
61-
*
62-
* @since 1.3.0
63-
*/
64-
public static final String RUNTIME_INSTRUCTIONS = "runtime_instructions";
65-
66-
/**
67-
* End-of-prompt instructions: parallel tool calling, persistence, task
68-
* completion.
69-
*/
70-
public static final String LAST_INSTRUCTIONS = "last_instructions";
18+
@Deprecated(since = "1.0.2", forRemoval = true)
19+
public final class SystemPromptSections extends SystemMessageSections {
7120

7221
private SystemPromptSections() {
73-
// utility class
7422
}
7523
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
import static org.junit.jupiter.api.Assertions.assertNotNull;
9+
import static org.junit.jupiter.api.Assertions.assertTrue;
10+
11+
import java.util.Map;
12+
import java.util.concurrent.CompletableFuture;
13+
import java.util.concurrent.ConcurrentHashMap;
14+
import java.util.concurrent.TimeUnit;
15+
16+
import org.junit.jupiter.api.AfterAll;
17+
import org.junit.jupiter.api.BeforeAll;
18+
import org.junit.jupiter.api.Test;
19+
20+
import com.github.copilot.rpc.CopilotClientOptions;
21+
import com.github.copilot.rpc.MessageOptions;
22+
import com.github.copilot.rpc.PermissionHandler;
23+
import com.github.copilot.rpc.SectionOverride;
24+
import com.github.copilot.rpc.SessionConfig;
25+
import com.github.copilot.rpc.SystemMessageConfig;
26+
import com.github.copilot.rpc.SystemMessageSections;
27+
import com.github.copilot.rpc.SystemPromptSections;
28+
29+
/**
30+
* Failsafe integration test that validates {@link SystemMessageSections}
31+
* constants are recognized by the live Copilot CLI runtime.
32+
* <p>
33+
* Uses a transform callback on the {@code identity} section to assert the
34+
* runtime invokes the callback with non-empty content — proving the constant is
35+
* a valid section identifier understood by the runtime.
36+
* <p>
37+
* Requires the CLI to be installed and the user to be signed in. Uses
38+
* {@link TestUtil#findCliPath()} so the test harness binary is found in CI.
39+
*/
40+
@SuppressWarnings("deprecation")
41+
class SystemMessageSectionsIT {
42+
43+
private static CopilotClient client;
44+
45+
@BeforeAll
46+
static void setup() throws Exception {
47+
String cliPath = TestUtil.findCliPath();
48+
CopilotClientOptions options = new CopilotClientOptions().setCliPath(cliPath).setUseLoggedInUser(true);
49+
client = new CopilotClient(options);
50+
client.start().get(30, TimeUnit.SECONDS);
51+
}
52+
53+
@AfterAll
54+
static void teardown() throws Exception {
55+
if (client != null) {
56+
client.close();
57+
}
58+
}
59+
60+
/**
61+
* Verifies that a transform callback on {@link SystemMessageSections#IDENTITY}
62+
* is invoked by the runtime with non-empty section content.
63+
* <p>
64+
* This proves the constant {@code "identity"} is a real section ID that the
65+
* runtime recognizes and populates.
66+
*/
67+
@Test
68+
void transformOnIdentitySectionReceivesNonEmptyContent() throws Exception {
69+
// Thread-safe container to capture what the runtime passes to our transform
70+
ConcurrentHashMap<String, String> capturedContent = new ConcurrentHashMap<>();
71+
72+
var systemMessage = new SystemMessageConfig().setMode(SystemMessageMode.CUSTOMIZE)
73+
.setSections(Map.of(SystemMessageSections.IDENTITY, new SectionOverride().setTransform(content -> {
74+
capturedContent.put("identity", content);
75+
return CompletableFuture.completedFuture(content);
76+
}), SystemMessageSections.TONE, new SectionOverride().setTransform(content -> {
77+
capturedContent.put("tone", content);
78+
return CompletableFuture.completedFuture(content);
79+
})));
80+
81+
CopilotSession session = client.createSession(new SessionConfig().setSystemMessage(systemMessage)
82+
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(30, TimeUnit.SECONDS);
83+
84+
try {
85+
// Send a message to trigger the runtime to build the system message
86+
// (transforms fire during session creation or first message)
87+
session.sendAndWait(new MessageOptions().setPrompt("Say hello"), 60_000).get(90, TimeUnit.SECONDS);
88+
89+
// Assert: identity transform was invoked with non-empty content
90+
String identityContent = capturedContent.get("identity");
91+
assertNotNull(identityContent, "Expected identity transform callback to be invoked by the runtime");
92+
assertTrue(!identityContent.isBlank(), "Expected identity section content to be non-empty but was blank");
93+
94+
// Assert: tone transform was also invoked
95+
String toneContent = capturedContent.get("tone");
96+
assertNotNull(toneContent, "Expected tone transform callback to be invoked by the runtime");
97+
assertTrue(!toneContent.isBlank(), "Expected tone section content to be non-empty but was blank");
98+
} finally {
99+
session.close();
100+
}
101+
}
102+
103+
/**
104+
* Verifies that the deprecated {@link SystemPromptSections} constants resolve
105+
* to the same values as {@link SystemMessageSections} — ensuring backward
106+
* compatibility.
107+
*/
108+
@Test
109+
void deprecatedSystemPromptSectionsMatchesSystemMessageSections() {
110+
// These are compile-time constants so this test guards against accidental
111+
// divergence if someone edits one class but not the other.
112+
assertEquals(SystemMessageSections.IDENTITY, SystemPromptSections.IDENTITY);
113+
assertEquals(SystemMessageSections.TONE, SystemPromptSections.TONE);
114+
assertEquals(SystemMessageSections.TOOL_EFFICIENCY, SystemPromptSections.TOOL_EFFICIENCY);
115+
assertEquals(SystemMessageSections.ENVIRONMENT_CONTEXT, SystemPromptSections.ENVIRONMENT_CONTEXT);
116+
assertEquals(SystemMessageSections.CODE_CHANGE_RULES, SystemPromptSections.CODE_CHANGE_RULES);
117+
assertEquals(SystemMessageSections.GUIDELINES, SystemPromptSections.GUIDELINES);
118+
assertEquals(SystemMessageSections.SAFETY, SystemPromptSections.SAFETY);
119+
assertEquals(SystemMessageSections.TOOL_INSTRUCTIONS, SystemPromptSections.TOOL_INSTRUCTIONS);
120+
assertEquals(SystemMessageSections.CUSTOM_INSTRUCTIONS, SystemPromptSections.CUSTOM_INSTRUCTIONS);
121+
assertEquals(SystemMessageSections.RUNTIME_INSTRUCTIONS, SystemPromptSections.RUNTIME_INSTRUCTIONS);
122+
assertEquals(SystemMessageSections.LAST_INSTRUCTIONS, SystemPromptSections.LAST_INSTRUCTIONS);
123+
}
124+
}

0 commit comments

Comments
 (0)