Skip to content

Commit 6e3d72c

Browse files
Support sessionFs in Node SDK. Update runtime. (#917)
* feat: add session data store support to TypeScript SDK - Add sessionDataStore option to CopilotClientOptions - Extend codegen to generate client API handler types (SessionDataStoreHandler) - Register as session data storage provider on connection via sessionDataStore.setDataStore RPC - Add E2E tests for persist, resume, list, delete, and reject scenarios Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: replace sessionDataStore with SessionFs virtual filesystem Migrate the TypeScript SDK from the event-level sessionDataStore abstraction to the general-purpose SessionFs virtual filesystem, matching the runtime's new design (copilot-agent-runtime#5432). Key changes: - Regenerate RPC types from runtime schema with sessionFs.* methods - Replace SessionDataStoreConfig with SessionFsConfig (initialCwd, sessionStatePath, conventions + 9 filesystem handler callbacks) - Client calls sessionFs.setProvider on connect (was setDataStore) - Client registers sessionFs.* RPC handlers (readFile, writeFile, appendFile, exists, stat, mkdir, readdir, rm, rename) - New E2E tests with InMemorySessionFs (filesystem-level, not events) - Remove old session_store tests and snapshots * Test cleanup * Test large output handling * Expand API surface slightly * Update test * Move to per-session client APIs * Simplify * Move createSessionFsHandler onto SessionConfig * Fix * Update to newer API schema * Add compaction+sessionFs test * Improve compaction test * Update codegen output * Update to latest runtime * fix: bump @github/copilot to 1.0.15-1, remove spurious root package-lock * fix: remove hardcoded COPILOT_CLI_PATH from test * skip postToolUse hook tests broken by runtime (issue #972) --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 346a38e commit 6e3d72c

30 files changed

+1863
-90
lines changed

dotnet/src/Generated/Rpc.cs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,30 @@ public class AccountGetQuotaResult
219219
public Dictionary<string, AccountGetQuotaResultQuotaSnapshotsValue> QuotaSnapshots { get => field ??= []; set; }
220220
}
221221

222+
/// <summary>RPC data type for SessionFsSetProvider operations.</summary>
223+
public class SessionFsSetProviderResult
224+
{
225+
/// <summary>Whether the provider was set successfully.</summary>
226+
[JsonPropertyName("success")]
227+
public bool Success { get; set; }
228+
}
229+
230+
/// <summary>RPC data type for SessionFsSetProvider operations.</summary>
231+
internal class SessionFsSetProviderRequest
232+
{
233+
/// <summary>Initial working directory for sessions.</summary>
234+
[JsonPropertyName("initialCwd")]
235+
public string InitialCwd { get; set; } = string.Empty;
236+
237+
/// <summary>Path within each session's SessionFs where the runtime stores files for that session.</summary>
238+
[JsonPropertyName("sessionStatePath")]
239+
public string SessionStatePath { get; set; } = string.Empty;
240+
241+
/// <summary>Path conventions used by this filesystem.</summary>
242+
[JsonPropertyName("conventions")]
243+
public SessionFsSetProviderRequestConventions Conventions { get; set; }
244+
}
245+
222246
/// <summary>RPC data type for SessionLog operations.</summary>
223247
public class SessionLogResult
224248
{
@@ -705,7 +729,7 @@ public class Server
705729
[JsonPropertyName("name")]
706730
public string Name { get; set; } = string.Empty;
707731

708-
/// <summary>Connection status: connected, failed, pending, disabled, or not_configured.</summary>
732+
/// <summary>Connection status: connected, failed, needs-auth, pending, disabled, or not_configured.</summary>
709733
[JsonPropertyName("status")]
710734
public ServerStatus Status { get; set; }
711735

@@ -1156,6 +1180,19 @@ internal class SessionShellKillRequest
11561180
public SessionShellKillRequestSignal? Signal { get; set; }
11571181
}
11581182

1183+
/// <summary>Path conventions used by this filesystem.</summary>
1184+
[JsonConverter(typeof(JsonStringEnumConverter<SessionFsSetProviderRequestConventions>))]
1185+
public enum SessionFsSetProviderRequestConventions
1186+
{
1187+
/// <summary>The <c>windows</c> variant.</summary>
1188+
[JsonStringEnumMemberName("windows")]
1189+
Windows,
1190+
/// <summary>The <c>posix</c> variant.</summary>
1191+
[JsonStringEnumMemberName("posix")]
1192+
Posix,
1193+
}
1194+
1195+
11591196
/// <summary>Log severity level. Determines how the message is displayed in the timeline. Defaults to "info".</summary>
11601197
[JsonConverter(typeof(JsonStringEnumConverter<SessionLogRequestLevel>))]
11611198
public enum SessionLogRequestLevel
@@ -1188,7 +1225,7 @@ public enum SessionModeGetResultMode
11881225
}
11891226

11901227

1191-
/// <summary>Connection status: connected, failed, pending, disabled, or not_configured.</summary>
1228+
/// <summary>Connection status: connected, failed, needs-auth, pending, disabled, or not_configured.</summary>
11921229
[JsonConverter(typeof(JsonStringEnumConverter<ServerStatus>))]
11931230
public enum ServerStatus
11941231
{
@@ -1198,6 +1235,9 @@ public enum ServerStatus
11981235
/// <summary>The <c>failed</c> variant.</summary>
11991236
[JsonStringEnumMemberName("failed")]
12001237
Failed,
1238+
/// <summary>The <c>needs-auth</c> variant.</summary>
1239+
[JsonStringEnumMemberName("needs-auth")]
1240+
NeedsAuth,
12011241
/// <summary>The <c>pending</c> variant.</summary>
12021242
[JsonStringEnumMemberName("pending")]
12031243
Pending,
@@ -1285,6 +1325,8 @@ internal ServerRpc(JsonRpc rpc)
12851325
Models = new ServerModelsApi(rpc);
12861326
Tools = new ServerToolsApi(rpc);
12871327
Account = new ServerAccountApi(rpc);
1328+
Mcp = new ServerMcpApi(rpc);
1329+
SessionFs = new ServerSessionFsApi(rpc);
12881330
}
12891331

12901332
/// <summary>Calls "ping".</summary>
@@ -1302,6 +1344,12 @@ public async Task<PingResult> PingAsync(string? message = null, CancellationToke
13021344

13031345
/// <summary>Account APIs.</summary>
13041346
public ServerAccountApi Account { get; }
1347+
1348+
/// <summary>Mcp APIs.</summary>
1349+
public ServerMcpApi Mcp { get; }
1350+
1351+
/// <summary>SessionFs APIs.</summary>
1352+
public ServerSessionFsApi SessionFs { get; }
13051353
}
13061354

13071355
/// <summary>Provides server-scoped Models APIs.</summary>
@@ -1356,6 +1404,35 @@ public async Task<AccountGetQuotaResult> GetQuotaAsync(CancellationToken cancell
13561404
}
13571405
}
13581406

1407+
/// <summary>Provides server-scoped Mcp APIs.</summary>
1408+
public class ServerMcpApi
1409+
{
1410+
private readonly JsonRpc _rpc;
1411+
1412+
internal ServerMcpApi(JsonRpc rpc)
1413+
{
1414+
_rpc = rpc;
1415+
}
1416+
}
1417+
1418+
/// <summary>Provides server-scoped SessionFs APIs.</summary>
1419+
public class ServerSessionFsApi
1420+
{
1421+
private readonly JsonRpc _rpc;
1422+
1423+
internal ServerSessionFsApi(JsonRpc rpc)
1424+
{
1425+
_rpc = rpc;
1426+
}
1427+
1428+
/// <summary>Calls "sessionFs.setProvider".</summary>
1429+
public async Task<SessionFsSetProviderResult> SetProviderAsync(string initialCwd, string sessionStatePath, SessionFsSetProviderRequestConventions conventions, CancellationToken cancellationToken = default)
1430+
{
1431+
var request = new SessionFsSetProviderRequest { InitialCwd = initialCwd, SessionStatePath = sessionStatePath, Conventions = conventions };
1432+
return await CopilotClient.InvokeRpcAsync<SessionFsSetProviderResult>(_rpc, "sessionFs.setProvider", [request], cancellationToken);
1433+
}
1434+
}
1435+
13591436
/// <summary>Provides typed session-scoped RPC methods.</summary>
13601437
public class SessionRpc
13611438
{
@@ -1959,6 +2036,8 @@ public async Task<SessionShellKillResult> KillAsync(string processId, SessionShe
19592036
[JsonSerializable(typeof(SessionExtensionsReloadResult))]
19602037
[JsonSerializable(typeof(SessionFleetStartRequest))]
19612038
[JsonSerializable(typeof(SessionFleetStartResult))]
2039+
[JsonSerializable(typeof(SessionFsSetProviderRequest))]
2040+
[JsonSerializable(typeof(SessionFsSetProviderResult))]
19622041
[JsonSerializable(typeof(SessionLogRequest))]
19632042
[JsonSerializable(typeof(SessionLogResult))]
19642043
[JsonSerializable(typeof(SessionMcpDisableRequest))]

dotnet/src/Generated/SessionEvents.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,11 @@ public partial class SessionIdleData
12161216
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
12171217
[JsonPropertyName("backgroundTasks")]
12181218
public SessionIdleDataBackgroundTasks? BackgroundTasks { get; set; }
1219+
1220+
/// <summary>True when the preceding agentic loop was cancelled via abort signal.</summary>
1221+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
1222+
[JsonPropertyName("aborted")]
1223+
public bool? Aborted { get; set; }
12191224
}
12201225

12211226
/// <summary>Session title change payload containing the new display title.</summary>
@@ -2593,7 +2598,7 @@ public partial class SessionMcpServerStatusChangedData
25932598
[JsonPropertyName("serverName")]
25942599
public required string ServerName { get; set; }
25952600

2596-
/// <summary>New connection status: connected, failed, pending, disabled, or not_configured.</summary>
2601+
/// <summary>New connection status: connected, failed, needs-auth, pending, disabled, or not_configured.</summary>
25972602
[JsonPropertyName("status")]
25982603
public required SessionMcpServersLoadedDataServersItemStatus Status { get; set; }
25992604
}
@@ -3786,7 +3791,7 @@ public partial class SessionMcpServersLoadedDataServersItem
37863791
[JsonPropertyName("name")]
37873792
public required string Name { get; set; }
37883793

3789-
/// <summary>Connection status: connected, failed, pending, disabled, or not_configured.</summary>
3794+
/// <summary>Connection status: connected, failed, needs-auth, pending, disabled, or not_configured.</summary>
37903795
[JsonPropertyName("status")]
37913796
public required SessionMcpServersLoadedDataServersItemStatus Status { get; set; }
37923797

@@ -3998,7 +4003,7 @@ public enum ElicitationRequestedDataMode
39984003
Url,
39994004
}
40004005

4001-
/// <summary>Connection status: connected, failed, pending, disabled, or not_configured.</summary>
4006+
/// <summary>Connection status: connected, failed, needs-auth, pending, disabled, or not_configured.</summary>
40024007
[JsonConverter(typeof(JsonStringEnumConverter<SessionMcpServersLoadedDataServersItemStatus>))]
40034008
public enum SessionMcpServersLoadedDataServersItemStatus
40044009
{
@@ -4008,6 +4013,9 @@ public enum SessionMcpServersLoadedDataServersItemStatus
40084013
/// <summary>The <c>failed</c> variant.</summary>
40094014
[JsonStringEnumMemberName("failed")]
40104015
Failed,
4016+
/// <summary>The <c>needs-auth</c> variant.</summary>
4017+
[JsonStringEnumMemberName("needs-auth")]
4018+
NeedsAuth,
40114019
/// <summary>The <c>pending</c> variant.</summary>
40124020
[JsonStringEnumMemberName("pending")]
40134021
Pending,

dotnet/test/HooksTests.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ await session.SendAsync(new MessageOptions
4646
Assert.Contains(preToolUseInputs, i => !string.IsNullOrEmpty(i.ToolName));
4747
}
4848

49-
[Fact]
49+
// TODO: Re-enable once runtime postToolUse hooks are fixed (https://github.com/github/copilot-sdk/issues/972)
50+
[Fact(Skip = "Runtime postToolUse hooks broken")]
5051
public async Task Should_Invoke_PostToolUse_Hook_After_Model_Runs_A_Tool()
5152
{
5253
var postToolUseInputs = new List<PostToolUseHookInput>();
@@ -83,7 +84,8 @@ await session.SendAsync(new MessageOptions
8384
Assert.Contains(postToolUseInputs, i => i.ToolResult != null);
8485
}
8586

86-
[Fact]
87+
// TODO: Re-enable once runtime postToolUse hooks are fixed (https://github.com/github/copilot-sdk/issues/972)
88+
[Fact(Skip = "Runtime postToolUse hooks broken")]
8789
public async Task Should_Invoke_Both_PreToolUse_And_PostToolUse_Hooks_For_Single_Tool_Call()
8890
{
8991
var preToolUseInputs = new List<PreToolUseHookInput>();

go/generated_session_events.go

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

go/internal/e2e/hooks_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ func TestHooks(t *testing.T) {
7474
}
7575
})
7676

77+
// TODO: Re-enable once runtime postToolUse hooks are fixed (https://github.com/github/copilot-sdk/issues/972)
7778
t.Run("should invoke postToolUse hook after model runs a tool", func(t *testing.T) {
79+
t.Skip("Runtime postToolUse hooks broken")
7880
ctx.ConfigureForTest(t)
7981

8082
var postToolUseInputs []copilot.PostToolUseHookInput
@@ -139,7 +141,9 @@ func TestHooks(t *testing.T) {
139141
}
140142
})
141143

144+
// TODO: Re-enable once runtime postToolUse hooks are fixed (https://github.com/github/copilot-sdk/issues/972)
142145
t.Run("should invoke both preToolUse and postToolUse hooks for a single tool call", func(t *testing.T) {
146+
t.Skip("Runtime postToolUse hooks broken")
143147
ctx.ConfigureForTest(t)
144148

145149
var preToolUseInputs []copilot.PreToolUseHookInput

0 commit comments

Comments
 (0)