Skip to content

Commit 0b29536

Browse files
Update @github/copilot to 1.0.76-5 (#2140)
* Update @github/copilot to 1.0.76-5 - Updated nodejs and test harness dependencies - Re-ran code generators - Formatted generated code * Fix SDK compatibility with Copilot 1.0.76-5 Update protocol generators and handwritten adapters for the latest RPC schema, including session filesystem SQLite transactions and newly required request fields. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7b5e2763-c535-4d58-bd7d-409c5d27c448 * Preserve SDK compatibility in Copilot update Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7b5e2763-c535-4d58-bd7d-409c5d27c448 * Format SessionFs transaction changes Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7b5e2763-c535-4d58-bd7d-409c5d27c448 * Fix Windows shell timeout test timing Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7b5e2763-c535-4d58-bd7d-409c5d27c448 * Apply nightly Rust formatting Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7b5e2763-c535-4d58-bd7d-409c5d27c448 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Mackinnon Buck <mackinnon.buck@gmail.com> Copilot-Session: 7b5e2763-c535-4d58-bd7d-409c5d27c448
1 parent 7f1f847 commit 0b29536

301 files changed

Lines changed: 41505 additions & 14360 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

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,

dotnet/test/E2E/InMemorySessionFsSqliteHandler.cs

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ internal record SqliteCall(string SessionId, string QueryType, string Query);
1717
/// for file operations instead of touching disk.
1818
/// </summary>
1919
internal sealed class InMemorySessionFsSqliteHandler(string sessionId, List<SqliteCall> sqliteCalls)
20-
: SessionFsProvider, ISessionFsSqliteProvider
20+
: SessionFsProvider, ISessionFsSqliteProvider, ISessionFsSqliteTransactionProvider
2121
{
2222
internal ConcurrentDictionary<string, string> Files { get; } = new();
2323
private readonly ConcurrentDictionary<string, byte> _directories = new();
@@ -45,28 +45,82 @@ private SqliteConnection GetOrCreateDb()
4545
string query,
4646
IDictionary<string, object?>? bindParams,
4747
CancellationToken cancellationToken)
48+
{
49+
return Task.FromResult(RunStatement(GetOrCreateDb(), null, queryType, query, bindParams));
50+
}
51+
52+
public Task<IList<SessionFsSqliteResult>> TransactionAsync(
53+
IList<SessionFsSqliteStatement> statements,
54+
CancellationToken cancellationToken)
55+
{
56+
var db = GetOrCreateDb();
57+
using var transaction = db.BeginTransaction();
58+
try
59+
{
60+
IList<SessionFsSqliteResult> results = statements
61+
.Select(statement => RunStatement(db, transaction, statement.QueryType, statement.Query, statement.Params)
62+
?? new SessionFsSqliteResult())
63+
.ToList();
64+
try
65+
{
66+
transaction.Commit();
67+
}
68+
catch (Exception ex)
69+
{
70+
throw new SessionFsSqliteTransactionException(
71+
ex.Message,
72+
SessionFsSqliteTransactionErrorClass.PostCommitAmbiguous,
73+
ex);
74+
}
75+
return Task.FromResult(results);
76+
}
77+
catch (SessionFsSqliteTransactionException)
78+
{
79+
throw;
80+
}
81+
catch (SqliteException ex)
82+
{
83+
transaction.Rollback();
84+
var errorClass = ex.SqliteErrorCode is 5 or 6
85+
? SessionFsSqliteTransactionErrorClass.BusyOrLocked
86+
: SessionFsSqliteTransactionErrorClass.Fatal;
87+
throw new SessionFsSqliteTransactionException(ex.Message, errorClass, ex);
88+
}
89+
catch (Exception ex)
90+
{
91+
transaction.Rollback();
92+
throw new SessionFsSqliteTransactionException(ex.Message, SessionFsSqliteTransactionErrorClass.Fatal, ex);
93+
}
94+
}
95+
96+
private SessionFsSqliteResult? RunStatement(
97+
SqliteConnection db,
98+
SqliteTransaction? transaction,
99+
SessionFsSqliteQueryType queryType,
100+
string query,
101+
IDictionary<string, object?>? bindParams)
48102
{
49103
sqliteCalls.Add(new SqliteCall(sessionId, queryType.Value, query));
50104

51105
var trimmed = query.Trim();
52106
if (trimmed.Length == 0)
53107
{
54-
return Task.FromResult<SessionFsSqliteResult?>(null);
108+
return null;
55109
}
56110

57-
var db = GetOrCreateDb();
58-
59111
if (queryType == SessionFsSqliteQueryType.Exec)
60112
{
61113
using var cmd = db.CreateCommand();
114+
cmd.Transaction = transaction;
62115
cmd.CommandText = trimmed;
63116
cmd.ExecuteNonQuery();
64-
return Task.FromResult<SessionFsSqliteResult?>(null);
117+
return null;
65118
}
66119

67120
if (queryType == SessionFsSqliteQueryType.Query)
68121
{
69122
using var cmd = db.CreateCommand();
123+
cmd.Transaction = transaction;
70124
cmd.CommandText = trimmed;
71125
AddParams(cmd, bindParams);
72126

@@ -88,33 +142,35 @@ private SqliteConnection GetOrCreateDb()
88142
rows.Add(row);
89143
}
90144

91-
return Task.FromResult<SessionFsSqliteResult?>(new SessionFsSqliteResult
145+
return new SessionFsSqliteResult
92146
{
93147
Columns = columns,
94148
Rows = rows,
95149
RowsAffected = 0,
96-
});
150+
};
97151
}
98152

99153
if (queryType == SessionFsSqliteQueryType.Run)
100154
{
101155
using var cmd = db.CreateCommand();
156+
cmd.Transaction = transaction;
102157
cmd.CommandText = trimmed;
103158
AddParams(cmd, bindParams);
104159

105160
var rowsAffected = cmd.ExecuteNonQuery();
106161

107162
using var rowidCmd = db.CreateCommand();
163+
rowidCmd.Transaction = transaction;
108164
rowidCmd.CommandText = "SELECT last_insert_rowid()";
109165
var lastRowid = rowidCmd.ExecuteScalar();
110166

111-
return Task.FromResult<SessionFsSqliteResult?>(new SessionFsSqliteResult
167+
return new SessionFsSqliteResult
112168
{
113169
Columns = [],
114170
Rows = [],
115171
RowsAffected = rowsAffected,
116172
LastInsertRowid = lastRowid is long l ? l : null,
117-
});
173+
};
118174
}
119175

120176
throw new ArgumentException($"Unknown queryType: {queryType}");

0 commit comments

Comments
 (0)