Skip to content

Commit 85d91ca

Browse files
stephentoubCopilot
andauthored
Add runtime_instructions system message section to all SDKs (#1377)
* Add runtime_instructions system message section to all SDKs Add support for the new runtime_instructions section added by the latest copilot-agent-runtime. This section targets runtime-provided context and instructions assembled from sources such as systemMessage.content, system notifications, memories, workspace context, mode-specific instructions, and content-exclusion policy. Changes across SDKs (excluding Java): - Node.js: Add to SystemMessageSection type union and SYSTEM_MESSAGE_SECTIONS - Python: Add to SystemMessageSection Literal type and SYSTEM_MESSAGE_SECTIONS - Go: Add SectionRuntimeInstructions constant with doc comments on all constants - .NET: Add SystemMessageSection.RuntimeInstructions using string-backed struct pattern (replaces former static class with string constants) - Rust: Comment-only update (uses raw strings, no predefined constants) Also renames SystemPromptSection/SYSTEM_PROMPT_SECTIONS to SystemMessageSection/SYSTEM_MESSAGE_SECTIONS across all SDKs for consistency, since these are sections of SystemMessageConfig. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix README: SystemMessageSection is a struct, not an enum Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix PublicDtoTests to handle non-string dictionary keys The test's TryCreateGenericCollection only handled dictionaries with string keys. With SystemMessageSection now being a struct used as a dictionary key, the test fell through to the IEnumerable path which created a List<KeyValuePair<...>> incompatible with IDictionary<...>. Generalized the dictionary handling to support any key type. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d8c0c7c commit 85d91ca

14 files changed

Lines changed: 166 additions & 73 deletions

File tree

docs/getting-started.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1908,7 +1908,7 @@ const session = await client.createSession({
19081908
});
19091909
```
19101910

1911-
Available section IDs: `identity`, `tone`, `tool_efficiency`, `environment_context`, `code_change_rules`, `guidelines`, `safety`, `tool_instructions`, `custom_instructions`, `last_instructions`.
1911+
Available section IDs: `identity`, `tone`, `tool_efficiency`, `environment_context`, `code_change_rules`, `guidelines`, `safety`, `tool_instructions`, `custom_instructions`, `runtime_instructions`, `last_instructions`.
19121912

19131913
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.
19141914

dotnet/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copilot SDK
1+
# Copilot SDK
22

33
SDK for programmatic control of GitHub Copilot CLI.
44

@@ -627,18 +627,18 @@ var session = await client.CreateSessionAsync(new SessionConfig
627627
SystemMessage = new SystemMessageConfig
628628
{
629629
Mode = SystemMessageMode.Customize,
630-
Sections = new Dictionary<string, SectionOverride>
630+
Sections = new Dictionary<SystemMessageSection, SectionOverride>
631631
{
632-
[SystemPromptSections.Tone] = new() { Action = SectionOverrideAction.Replace, Content = "Respond in a warm, professional tone. Be thorough in explanations." },
633-
[SystemPromptSections.CodeChangeRules] = new() { Action = SectionOverrideAction.Remove },
634-
[SystemPromptSections.Guidelines] = new() { Action = SectionOverrideAction.Append, Content = "\n* Always cite data sources" },
632+
[SystemMessageSection.Tone] = new() { Action = SectionOverrideAction.Replace, Content = "Respond in a warm, professional tone. Be thorough in explanations." },
633+
[SystemMessageSection.CodeChangeRules] = new() { Action = SectionOverrideAction.Remove },
634+
[SystemMessageSection.Guidelines] = new() { Action = SectionOverrideAction.Append, Content = "\n* Always cite data sources" },
635635
},
636636
Content = "Focus on financial analysis and reporting."
637637
}
638638
});
639639
```
640640

641-
Available section IDs are defined as constants on `SystemPromptSections`: `Identity`, `Tone`, `ToolEfficiency`, `EnvironmentContext`, `CodeChangeRules`, `Guidelines`, `Safety`, `ToolInstructions`, `CustomInstructions`, `LastInstructions`.
641+
Available section IDs are defined as static properties on the `SystemMessageSection` struct: `Identity`, `Tone`, `ToolEfficiency`, `EnvironmentContext`, `CodeChangeRules`, `Guidelines`, `Safety`, `ToolInstructions`, `CustomInstructions`, `RuntimeInstructions`, `LastInstructions`.
642642

643643
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.
644644

dotnet/src/Client.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*---------------------------------------------------------------------------------------------
1+
/*---------------------------------------------------------------------------------------------
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
*--------------------------------------------------------------------------------------------*/
44

@@ -464,13 +464,13 @@ private static (SystemMessageConfig? wireConfig, Dictionary<string, Func<string,
464464
}
465465

466466
var callbacks = new Dictionary<string, Func<string, Task<string>>>();
467-
var wireSections = new Dictionary<string, SectionOverride>();
467+
var wireSections = new Dictionary<SystemMessageSection, SectionOverride>();
468468

469469
foreach (var (sectionId, sectionOverride) in systemMessage.Sections)
470470
{
471471
if (sectionOverride.Transform != null)
472472
{
473-
callbacks[sectionId] = sectionOverride.Transform;
473+
callbacks[sectionId.Value] = sectionOverride.Transform;
474474
wireSections[sectionId] = new SectionOverride { Action = SectionOverrideAction.Transform };
475475
}
476476
else

dotnet/src/Types.cs

Lines changed: 82 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,7 +1748,7 @@ public enum SystemMessageMode
17481748
}
17491749

17501750
/// <summary>
1751-
/// Specifies the operation to perform on a system prompt section.
1751+
/// Specifies the operation to perform on a system message section.
17521752
/// </summary>
17531753
[JsonConverter(typeof(JsonStringEnumConverter<SectionOverrideAction>))]
17541754
public enum SectionOverrideAction
@@ -1771,7 +1771,7 @@ public enum SectionOverrideAction
17711771
}
17721772

17731773
/// <summary>
1774-
/// Override operation for a single system prompt section.
1774+
/// Override operation for a single system message section.
17751775
/// </summary>
17761776
public sealed class SectionOverride
17771777
{
@@ -1797,30 +1797,95 @@ public sealed class SectionOverride
17971797
}
17981798

17991799
/// <summary>
1800-
/// Known system prompt section identifiers for the "customize" mode.
1800+
/// Identifies a system message section for the "customize" mode.
18011801
/// </summary>
1802-
public static class SystemPromptSections
1802+
[JsonConverter(typeof(SystemMessageSection.Converter))]
1803+
public readonly struct SystemMessageSection : IEquatable<SystemMessageSection>
18031804
{
18041805
/// <summary>Agent identity preamble and mode statement.</summary>
1805-
public const string Identity = "identity";
1806+
public static SystemMessageSection Identity { get; } = new("identity");
18061807
/// <summary>Response style, conciseness rules, output formatting preferences.</summary>
1807-
public const string Tone = "tone";
1808+
public static SystemMessageSection Tone { get; } = new("tone");
18081809
/// <summary>Tool usage patterns, parallel calling, batching guidelines.</summary>
1809-
public const string ToolEfficiency = "tool_efficiency";
1810+
public static SystemMessageSection ToolEfficiency { get; } = new("tool_efficiency");
18101811
/// <summary>CWD, OS, git root, directory listing, available tools.</summary>
1811-
public const string EnvironmentContext = "environment_context";
1812+
public static SystemMessageSection EnvironmentContext { get; } = new("environment_context");
18121813
/// <summary>Coding rules, linting/testing, ecosystem tools, style.</summary>
1813-
public const string CodeChangeRules = "code_change_rules";
1814+
public static SystemMessageSection CodeChangeRules { get; } = new("code_change_rules");
18141815
/// <summary>Tips, behavioral best practices, behavioral guidelines.</summary>
1815-
public const string Guidelines = "guidelines";
1816+
public static SystemMessageSection Guidelines { get; } = new("guidelines");
18161817
/// <summary>Environment limitations, prohibited actions, security policies.</summary>
1817-
public const string Safety = "safety";
1818+
public static SystemMessageSection Safety { get; } = new("safety");
18181819
/// <summary>Per-tool usage instructions.</summary>
1819-
public const string ToolInstructions = "tool_instructions";
1820+
public static SystemMessageSection ToolInstructions { get; } = new("tool_instructions");
18201821
/// <summary>Repository and organization custom instructions.</summary>
1821-
public const string CustomInstructions = "custom_instructions";
1822+
public static SystemMessageSection CustomInstructions { get; } = new("custom_instructions");
1823+
/// <summary>Runtime-provided context and instructions (e.g. system notifications, memories, workspace context, mode-specific instructions, content-exclusion policy).</summary>
1824+
public static SystemMessageSection RuntimeInstructions { get; } = new("runtime_instructions");
18221825
/// <summary>End-of-prompt instructions: parallel tool calling, persistence, task completion.</summary>
1823-
public const string LastInstructions = "last_instructions";
1826+
public static SystemMessageSection LastInstructions { get; } = new("last_instructions");
1827+
1828+
/// <summary>Gets the underlying string value of this <see cref="SystemMessageSection"/>.</summary>
1829+
public string Value => _value ?? string.Empty;
1830+
1831+
private readonly string? _value;
1832+
1833+
/// <summary>Initializes a new instance of the <see cref="SystemMessageSection"/> struct.</summary>
1834+
/// <param name="value">The string value for this section identifier.</param>
1835+
[JsonConstructor]
1836+
public SystemMessageSection(string value) => _value = value;
1837+
1838+
/// <inheritdoc/>
1839+
public static bool operator ==(SystemMessageSection left, SystemMessageSection right) => left.Equals(right);
1840+
1841+
/// <inheritdoc/>
1842+
public static bool operator !=(SystemMessageSection left, SystemMessageSection right) => !left.Equals(right);
1843+
1844+
/// <inheritdoc/>
1845+
public override bool Equals([NotNullWhen(true)] object? obj) => obj is SystemMessageSection other && Equals(other);
1846+
1847+
/// <inheritdoc/>
1848+
public bool Equals(SystemMessageSection other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase);
1849+
1850+
/// <inheritdoc/>
1851+
public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value);
1852+
1853+
/// <inheritdoc/>
1854+
public override string ToString() => Value;
1855+
1856+
/// <summary>Provides a <see cref="JsonConverter{SystemMessageSection}"/> for serializing <see cref="SystemMessageSection"/> instances.</summary>
1857+
[EditorBrowsable(EditorBrowsableState.Never)]
1858+
public sealed class Converter : JsonConverter<SystemMessageSection>
1859+
{
1860+
/// <inheritdoc/>
1861+
public override SystemMessageSection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
1862+
{
1863+
if (reader.TokenType != JsonTokenType.String)
1864+
{
1865+
throw new JsonException("Expected string for SystemMessageSection.");
1866+
}
1867+
1868+
var value = reader.GetString();
1869+
if (value is null)
1870+
{
1871+
throw new JsonException("SystemMessageSection value cannot be null.");
1872+
}
1873+
1874+
return new SystemMessageSection(value);
1875+
}
1876+
1877+
/// <inheritdoc/>
1878+
public override void Write(Utf8JsonWriter writer, SystemMessageSection value, JsonSerializerOptions options) =>
1879+
writer.WriteStringValue(value.Value);
1880+
1881+
/// <inheritdoc/>
1882+
public override SystemMessageSection ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
1883+
new(reader.GetString()!);
1884+
1885+
/// <inheritdoc/>
1886+
public override void WriteAsPropertyName(Utf8JsonWriter writer, SystemMessageSection value, JsonSerializerOptions options) =>
1887+
writer.WritePropertyName(value.Value);
1888+
}
18241889
}
18251890

18261891
/// <summary>
@@ -1841,9 +1906,9 @@ public sealed class SystemMessageConfig
18411906

18421907
/// <summary>
18431908
/// Section-level overrides for customize mode.
1844-
/// Keys are section identifiers (see <see cref="SystemPromptSections"/>).
1909+
/// Keys are section identifiers (see <see cref="SystemMessageSection"/>).
18451910
/// </summary>
1846-
public IDictionary<string, SectionOverride>? Sections { get; set; }
1911+
public IDictionary<SystemMessageSection, SectionOverride>? Sections { get; set; }
18471912
}
18481913

18491914
/// <summary>
@@ -3001,7 +3066,7 @@ public sealed class SetForegroundSessionResponse
30013066
}
30023067

30033068
/// <summary>
3004-
/// Content data for a single system prompt section in a transform RPC call.
3069+
/// Content data for a single system message section in a transform RPC call.
30053070
/// </summary>
30063071
public sealed class SystemMessageTransformSection
30073072
{

dotnet/test/E2E/SessionE2ETests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*---------------------------------------------------------------------------------------------
1+
/*---------------------------------------------------------------------------------------------
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
*--------------------------------------------------------------------------------------------*/
44

@@ -101,10 +101,10 @@ public async Task Should_Create_A_Session_With_Customized_SystemMessage_Config()
101101
SystemMessage = new SystemMessageConfig
102102
{
103103
Mode = SystemMessageMode.Customize,
104-
Sections = new Dictionary<string, SectionOverride>
104+
Sections = new Dictionary<SystemMessageSection, SectionOverride>
105105
{
106-
[SystemPromptSections.Tone] = new() { Action = SectionOverrideAction.Replace, Content = customTone },
107-
[SystemPromptSections.CodeChangeRules] = new() { Action = SectionOverrideAction.Remove },
106+
[SystemMessageSection.Tone] = new() { Action = SectionOverrideAction.Replace, Content = customTone },
107+
[SystemMessageSection.CodeChangeRules] = new() { Action = SectionOverrideAction.Remove },
108108
},
109109
Content = appendedContent
110110
}

dotnet/test/E2E/SystemMessageTransformE2ETests.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ public async Task Should_Invoke_Transform_Callbacks_With_Section_Content()
2222
SystemMessage = new SystemMessageConfig
2323
{
2424
Mode = SystemMessageMode.Customize,
25-
Sections = new Dictionary<string, SectionOverride>
25+
Sections = new Dictionary<SystemMessageSection, SectionOverride>
2626
{
27-
["identity"] = new SectionOverride
27+
[SystemMessageSection.Identity] = new SectionOverride
2828
{
2929
Transform = async (content) =>
3030
{
@@ -33,7 +33,7 @@ public async Task Should_Invoke_Transform_Callbacks_With_Section_Content()
3333
return content;
3434
}
3535
},
36-
["tone"] = new SectionOverride
36+
[SystemMessageSection.Tone] = new SectionOverride
3737
{
3838
Transform = async (content) =>
3939
{
@@ -68,9 +68,9 @@ public async Task Should_Apply_Transform_Modifications_To_Section_Content()
6868
SystemMessage = new SystemMessageConfig
6969
{
7070
Mode = SystemMessageMode.Customize,
71-
Sections = new Dictionary<string, SectionOverride>
71+
Sections = new Dictionary<SystemMessageSection, SectionOverride>
7272
{
73-
["identity"] = new SectionOverride
73+
[SystemMessageSection.Identity] = new SectionOverride
7474
{
7575
Transform = async (content) =>
7676
{
@@ -108,13 +108,13 @@ public async Task Should_Work_With_Static_Overrides_And_Transforms_Together()
108108
SystemMessage = new SystemMessageConfig
109109
{
110110
Mode = SystemMessageMode.Customize,
111-
Sections = new Dictionary<string, SectionOverride>
111+
Sections = new Dictionary<SystemMessageSection, SectionOverride>
112112
{
113-
["safety"] = new SectionOverride
113+
[SystemMessageSection.Safety] = new SectionOverride
114114
{
115115
Action = SectionOverrideAction.Remove
116116
},
117-
["identity"] = new SectionOverride
117+
[SystemMessageSection.Identity] = new SectionOverride
118118
{
119119
Transform = async (content) =>
120120
{

dotnet/test/Unit/PublicDtoTests.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,15 +174,17 @@ private static bool TryCreateGenericCollection(Type type, HashSet<Type> visited,
174174
.FirstOrDefault(candidate =>
175175
candidate.IsGenericType &&
176176
(candidate.GetGenericTypeDefinition() == typeof(IDictionary<,>) ||
177-
candidate.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>)) &&
178-
candidate.GetGenericArguments()[0] == typeof(string));
177+
candidate.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>)));
179178

180179
if (dictionaryInterface is not null)
181180
{
181+
var keyType = dictionaryInterface.GetGenericArguments()[0];
182182
var valueType = dictionaryInterface.GetGenericArguments()[1];
183+
TryCreateSampleValue(keyType, visited, out var sampleKey);
183184
TryCreateSampleValue(valueType, visited, out var sampleValue);
184-
var dictionary = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(typeof(string), valueType))!;
185-
dictionary["key"] = sampleValue;
185+
var dictionaryType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType);
186+
var dictionary = (IDictionary)Activator.CreateInstance(dictionaryType)!;
187+
dictionary[sampleKey!] = sampleValue;
186188
value = dictionary;
187189
return true;
188190
}

go/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ Event types: `SessionLifecycleCreated`, `SessionLifecycleDeleted`, `SessionLifec
160160
- `SystemMessage` (\*SystemMessageConfig): System message configuration. Supports three modes:
161161
- **append** (default): Appends `Content` after the SDK-managed prompt
162162
- **replace**: Replaces the entire prompt with `Content`
163-
- **customize**: Selectively override individual sections via `Sections` map (keys: `SectionIdentity`, `SectionTone`, `SectionToolEfficiency`, `SectionEnvironmentContext`, `SectionCodeChangeRules`, `SectionGuidelines`, `SectionSafety`, `SectionToolInstructions`, `SectionCustomInstructions`, `SectionLastInstructions`; values: `SectionOverride` with `Action` and optional `Content`)
163+
- **customize**: Selectively override individual sections via `Sections` map (keys: `SectionIdentity`, `SectionTone`, `SectionToolEfficiency`, `SectionEnvironmentContext`, `SectionCodeChangeRules`, `SectionGuidelines`, `SectionSafety`, `SectionToolInstructions`, `SectionCustomInstructions`, `SectionRuntimeInstructions`, `SectionLastInstructions`; values: `SectionOverride` with `Action` and optional `Content`)
164164
- `Provider` (\*ProviderConfig): Custom API provider configuration (BYOK). See [Custom Providers](#custom-providers) section.
165165
- `Streaming` (*bool): Enable streaming delta events (nil = runtime default)
166166
- `InfiniteSessions` (\*InfiniteSessionConfig): Automatic context compaction configuration
@@ -233,7 +233,7 @@ session, err := client.CreateSession(ctx, &copilot.SessionConfig{
233233
})
234234
```
235235

236-
Available section constants: `SectionIdentity`, `SectionTone`, `SectionToolEfficiency`, `SectionEnvironmentContext`, `SectionCodeChangeRules`, `SectionGuidelines`, `SectionSafety`, `SectionToolInstructions`, `SectionCustomInstructions`, `SectionLastInstructions`.
236+
Available section constants: `SectionIdentity`, `SectionTone`, `SectionToolEfficiency`, `SectionEnvironmentContext`, `SectionCodeChangeRules`, `SectionGuidelines`, `SectionSafety`, `SectionToolInstructions`, `SectionCustomInstructions`, `SectionRuntimeInstructions`, `SectionLastInstructions`.
237237

238238
Each section override supports four actions:
239239

0 commit comments

Comments
 (0)