Skip to content

Commit 3bc85e8

Browse files
authored
Merge branch 'main' into scottaddie-replace-azure-ai-foundry-branding
2 parents 083ae83 + 07d7db5 commit 3bc85e8

308 files changed

Lines changed: 41722 additions & 14375 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
.github/workflows/*.lock.yml linguist-generated=true merge=ours
22

3+
# Cross-platform tools rewrite these files, so keep their output deterministic.
4+
java/**/*.java text eol=lf
5+
36
# Generated files — keep LF line endings so codegen output is deterministic across platforms.
47
nodejs/src/generated/* eol=lf linguist-generated=true
58
dotnet/src/Generated/* eol=lf linguist-generated=true

.github/instructions/docs-style.instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ When a callout applies to a specific language, put the qualifier as bold text in
4949

5050
```markdown
5151
> [!TIP]
52-
> **(Python / Go)** These SDKs use a single `Data` class/struct with all fields optional.
52+
> **(Python / Go)** These SDKs use separate, per-event data types.
5353
```
5454

5555
## Lists

docs/features/streaming-events.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ session.on(AssistantMessageDeltaEvent.class, event ->
210210
</details>
211211

212212
> [!TIP]
213-
> **(Python / Go)** These SDKs use a single `Data` class/struct with all possible fields as optional/nullable. Only the fields listed in the tables below are populated for each event type—the rest will be `None` / `nil`.
213+
> **(Python / Go)** These SDKs use separate, per-event data types (for example, `AssistantMessageDeltaData`), so only the relevant fields exist on each type.
214214
>
215215
> [!TIP]
216216
> **(.NET)** The .NET SDK uses separate, strongly-typed data classes per event (e.g., `AssistantMessageDeltaData`), so only the relevant fields exist on each type.

docs/getting-started.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2053,6 +2053,7 @@ let mut options = ClientOptions::default();
20532053
options.transport = Transport::External {
20542054
host: "localhost".to_string(),
20552055
port: 4321,
2056+
connection_token: None,
20562057
};
20572058
let client = Client::start(options).await?;
20582059

dotnet/src/Generated/Rpc.cs

Lines changed: 12113 additions & 8389 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dotnet/src/Generated/SessionEvents.cs

Lines changed: 551 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dotnet/src/Session.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,7 @@ internal void SetCanvasHandler(ICanvasHandler? handler)
11451145
ClientSessionApis.Canvas = handler is null ? null : new CanvasHandlerAdapter(handler);
11461146
}
11471147

1148-
private static readonly JsonElement NullJsonElement = JsonDocument.Parse("null").RootElement.Clone();
1148+
private static readonly JsonElement NullJsonElement = JsonElement.Parse("null");
11491149

11501150
private static JsonElement SerializeActionResult(object? value)
11511151
{
@@ -1823,6 +1823,7 @@ await Rpc.Model.SwitchToAsync(
18231823
null,
18241824
options.ModelCapabilities,
18251825
options.ContextTier,
1826+
null,
18261827
cancellationToken);
18271828
}
18281829

dotnet/src/SessionFsProvider.cs

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*--------------------------------------------------------------------------------------------*/
44

55
using GitHub.Copilot.Rpc;
6+
using System.Diagnostics.CodeAnalysis;
67
using System.Text.Json;
78

89
namespace GitHub.Copilot;
@@ -27,6 +28,23 @@ public sealed class SessionFsSqliteResult
2728
public long? LastInsertRowid { get; set; }
2829
}
2930

31+
/// <summary>
32+
/// One statement in an atomic SQLite transaction passed to
33+
/// <see cref="ISessionFsSqliteTransactionProvider.TransactionAsync"/>.
34+
/// </summary>
35+
[Experimental(Diagnostics.Experimental)]
36+
public sealed class SessionFsSqliteStatement
37+
{
38+
/// <summary>How to execute: <c>"exec"</c>, <c>"query"</c>, or <c>"run"</c>.</summary>
39+
public SessionFsSqliteQueryType QueryType { get; set; }
40+
41+
/// <summary>SQL statement to execute.</summary>
42+
public string Query { get; set; } = string.Empty;
43+
44+
/// <summary>Optional named bind parameters.</summary>
45+
public IDictionary<string, object?>? Params { get; set; }
46+
}
47+
3048
/// <summary>
3149
/// Optional interface for <see cref="SessionFsProvider"/> subclasses that support
3250
/// per-session SQLite databases. Implement this interface on your provider to enable
@@ -55,6 +73,52 @@ public interface ISessionFsSqliteProvider
5573
Task<bool> ExistsAsync(CancellationToken cancellationToken);
5674
}
5775

76+
/// <summary>
77+
/// Optional capability for session filesystem providers that support atomic SQLite transactions.
78+
/// </summary>
79+
public interface ISessionFsSqliteTransactionProvider
80+
{
81+
/// <summary>
82+
/// Executes <paramref name="statements"/> atomically against the per-session database.
83+
/// </summary>
84+
/// <param name="statements">Statements to execute in order, inside a single transaction.</param>
85+
/// <param name="cancellationToken">Cancellation token.</param>
86+
/// <returns>One result per statement, in the same order as <paramref name="statements"/>.</returns>
87+
/// <exception cref="SessionFsSqliteTransactionException">
88+
/// Thrown to tell the runtime how the failure should be classified. Any other exception
89+
/// is reported as <see cref="SessionFsSqliteTransactionErrorClass.Fatal"/>.
90+
/// </exception>
91+
Task<IList<SessionFsSqliteResult>> TransactionAsync(
92+
IList<SessionFsSqliteStatement> statements,
93+
CancellationToken cancellationToken);
94+
}
95+
96+
/// <summary>
97+
/// Thrown by an <see cref="ISessionFsSqliteTransactionProvider"/> to classify a failed SQLite transaction.
98+
/// <see cref="SessionFsSqliteTransactionErrorClass.BusyOrLocked"/> guarantees the transaction
99+
/// rolled back and is safe to retry; <see cref="SessionFsSqliteTransactionErrorClass.PostCommitAmbiguous"/>
100+
/// must never be retried.
101+
/// </summary>
102+
[Experimental(Diagnostics.Experimental)]
103+
public sealed class SessionFsSqliteTransactionException : Exception
104+
{
105+
/// <summary>Initializes a new instance of the <see cref="SessionFsSqliteTransactionException"/> class.</summary>
106+
/// <param name="message">Human-readable failure description.</param>
107+
/// <param name="errorClass">How the runtime should classify the failure.</param>
108+
/// <param name="innerException">Optional underlying exception.</param>
109+
public SessionFsSqliteTransactionException(
110+
string message,
111+
SessionFsSqliteTransactionErrorClass errorClass,
112+
Exception? innerException = null)
113+
: base(message, innerException)
114+
{
115+
ErrorClass = errorClass;
116+
}
117+
118+
/// <summary>Gets the failure classification reported to the runtime.</summary>
119+
public SessionFsSqliteTransactionErrorClass ErrorClass { get; }
120+
}
121+
58122
/// <summary>
59123
/// Base class for session filesystem providers. Subclasses override the
60124
/// virtual methods and use normal C# patterns (return values, throw exceptions).
@@ -297,7 +361,7 @@ async Task<SessionFsSqliteQueryResult> ISessionFsHandler.SqliteQueryAsync(Sessio
297361
{
298362
Rows = result?.Rows?.Select(row => (IDictionary<string, JsonElement>)row.ToDictionary(
299363
kvp => kvp.Key,
300-
kvp => CopilotClient.ToJsonElementForWire(kvp.Value)!.Value)).ToList() ?? [],
364+
kvp => ToJsonElement(kvp.Value))).ToList() ?? [],
301365
Columns = result?.Columns ?? [],
302366
RowsAffected = result?.RowsAffected ?? 0,
303367
LastInsertRowid = result?.LastInsertRowid,
@@ -309,6 +373,78 @@ async Task<SessionFsSqliteQueryResult> ISessionFsHandler.SqliteQueryAsync(Sessio
309373
}
310374
}
311375

376+
async Task<SessionFsSqliteTransactionResult> ISessionFsHandler.SqliteTransactionAsync(SessionFsSqliteTransactionRequest request, CancellationToken cancellationToken)
377+
{
378+
if (this is not ISessionFsSqliteTransactionProvider transactionProvider)
379+
{
380+
return new SessionFsSqliteTransactionResult
381+
{
382+
Error = new SessionFsSqliteTransactionError
383+
{
384+
ErrorClass = SessionFsSqliteTransactionErrorClass.Fatal,
385+
Message = "SQLite is not supported by this provider.",
386+
},
387+
};
388+
}
389+
390+
IList<SessionFsSqliteResult> results;
391+
try
392+
{
393+
var statements = request.Statements.Select(statement => new SessionFsSqliteStatement
394+
{
395+
QueryType = statement.QueryType,
396+
Query = statement.Query,
397+
Params = statement.Params?.ToDictionary(kvp => kvp.Key, kvp => JsonElementToValue(kvp.Value)),
398+
}).ToList();
399+
results = await transactionProvider.TransactionAsync(statements, cancellationToken).ConfigureAwait(false);
400+
}
401+
catch (SessionFsSqliteTransactionException ex)
402+
{
403+
return new SessionFsSqliteTransactionResult
404+
{
405+
Error = new SessionFsSqliteTransactionError { ErrorClass = ex.ErrorClass, Message = ex.Message },
406+
};
407+
}
408+
catch (Exception ex)
409+
{
410+
return new SessionFsSqliteTransactionResult
411+
{
412+
Error = new SessionFsSqliteTransactionError
413+
{
414+
ErrorClass = SessionFsSqliteTransactionErrorClass.Fatal,
415+
Message = ex.Message,
416+
},
417+
};
418+
}
419+
420+
try
421+
{
422+
return new SessionFsSqliteTransactionResult
423+
{
424+
Results = results.Select(result => new SessionFsSqliteQueryResult
425+
{
426+
Rows = result.Rows?.Select(row => (IDictionary<string, JsonElement>)row.ToDictionary(
427+
kvp => kvp.Key,
428+
kvp => ToJsonElement(kvp.Value))).ToList() ?? [],
429+
Columns = result.Columns ?? [],
430+
RowsAffected = result.RowsAffected,
431+
LastInsertRowid = result.LastInsertRowid,
432+
}).ToList(),
433+
};
434+
}
435+
catch (Exception ex)
436+
{
437+
return new SessionFsSqliteTransactionResult
438+
{
439+
Error = new SessionFsSqliteTransactionError
440+
{
441+
ErrorClass = SessionFsSqliteTransactionErrorClass.PostCommitAmbiguous,
442+
Message = ex.Message,
443+
},
444+
};
445+
}
446+
}
447+
312448
async Task<SessionFsSqliteExistsResult> ISessionFsHandler.SqliteExistsAsync(SessionFsSqliteExistsRequest request, CancellationToken cancellationToken)
313449
{
314450
if (this is not ISessionFsSqliteProvider sqliteProvider)
@@ -336,6 +472,9 @@ private static SessionFsError ToSessionFsError(Exception ex)
336472
return new SessionFsError { Code = code, Message = ex.Message };
337473
}
338474

475+
private static JsonElement ToJsonElement(object? value) =>
476+
CopilotClient.ToJsonElementForWire(value) ?? JsonElement.Parse("null");
477+
339478
private static object? JsonElementToValue(JsonElement element) => element.ValueKind switch
340479
{
341480
JsonValueKind.Null => null,

dotnet/test/E2E/CommandsE2ETests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public async Task Session_Commands_List_Returns_Builtins_And_Respects_Client_Com
3030
await TestHelper.WaitForConditionAsync(
3131
async () =>
3232
{
33-
clientCommands = await session.Rpc.Commands.ListAsync(new CommandsListRequest
33+
clientCommands = await session.Rpc.Commands.ListAsync(new SessionCommandsListRequest
3434
{
3535
IncludeBuiltins = false,
3636
IncludeClientCommands = true,
@@ -45,7 +45,7 @@ await TestHelper.WaitForConditionAsync(
4545
Assert.Contains(clientCommands.Commands, c => IsCommand(c, "rollback", SlashCommandKind.Client));
4646
Assert.DoesNotContain(clientCommands.Commands, c => c.Kind == SlashCommandKind.Builtin);
4747

48-
var builtinCommands = await session.Rpc.Commands.ListAsync(new CommandsListRequest
48+
var builtinCommands = await session.Rpc.Commands.ListAsync(new SessionCommandsListRequest
4949
{
5050
IncludeBuiltins = true,
5151
IncludeClientCommands = false,
@@ -64,7 +64,7 @@ public async Task Session_Commands_Invoke_Known_Builtin_Returns_Expected_Result(
6464
{
6565
var session = await CreateSessionAsync();
6666

67-
var builtinCommands = await session.Rpc.Commands.ListAsync(new CommandsListRequest
67+
var builtinCommands = await session.Rpc.Commands.ListAsync(new SessionCommandsListRequest
6868
{
6969
IncludeBuiltins = true,
7070
IncludeClientCommands = false,
@@ -128,7 +128,7 @@ public async Task Session_Commands_Execute_Runs_Registered_Command_Handler()
128128
await TestHelper.WaitForConditionAsync(
129129
async () =>
130130
{
131-
var commands = await session.Rpc.Commands.ListAsync(new CommandsListRequest
131+
var commands = await session.Rpc.Commands.ListAsync(new SessionCommandsListRequest
132132
{
133133
IncludeBuiltins = false,
134134
IncludeClientCommands = true,

0 commit comments

Comments
 (0)