agent-shim 8.23.0 → 8.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -3207,9 +3207,6 @@ var HTTP_STATUS = {
3207
3207
  badGateway: 502
3208
3208
  };
3209
3209
 
3210
- // src/frontdoor/rcControl.ts
3211
- import * as https from "node:https";
3212
-
3213
3210
  // src/frontdoor/rcSessions.ts
3214
3211
  var RC_SESSIONS_PATH_PREFIX = "/v1/code/sessions";
3215
3212
  var RC_SESSION_ID_PREFIX = "cse_";
@@ -3630,6 +3627,8 @@ function rcObserverAsPassthrough(observer) {
3630
3627
  }
3631
3628
  };
3632
3629
  }
3630
+
3631
+ // src/frontdoor/rcWrites.ts
3633
3632
  function buildRcUserMessagePayload(uuid, sessionId, text) {
3634
3633
  return {
3635
3634
  type: "user",
@@ -3730,14 +3729,55 @@ var RC_PERMISSION_MODES = ["default", "acceptEdits", "bypassPermissions", "plan"
3730
3729
  function isRcPermissionMode(value) {
3731
3730
  return RC_PERMISSION_MODES.some((mode) => mode === value);
3732
3731
  }
3732
+ function buildRcControlRequestPayload(requestId, request4) {
3733
+ return { type: "control_request", request_id: requestId, request: request4 };
3734
+ }
3733
3735
  function buildRcInterruptPayload(requestId) {
3734
- return { type: "control_request", request_id: requestId, request: { subtype: "interrupt" } };
3736
+ return buildRcControlRequestPayload(requestId, { subtype: "interrupt" });
3735
3737
  }
3736
3738
  function buildRcSetModelPayload(requestId, model) {
3737
- return { type: "control_request", request_id: requestId, request: { subtype: "set_model", model } };
3739
+ return buildRcControlRequestPayload(requestId, { subtype: "set_model", model });
3738
3740
  }
3739
3741
  function buildRcSetPermissionModePayload(requestId, mode) {
3740
- return { type: "control_request", request_id: requestId, request: { subtype: "set_permission_mode", mode } };
3742
+ return buildRcControlRequestPayload(requestId, { subtype: "set_permission_mode", mode });
3743
+ }
3744
+ function buildRcEndSessionPayload(requestId, reason) {
3745
+ return buildRcControlRequestPayload(requestId, { subtype: "end_session", ...reason === void 0 ? {} : { reason } });
3746
+ }
3747
+ function buildRcGetUsagePayload(requestId, skipBehaviors) {
3748
+ return buildRcControlRequestPayload(requestId, { subtype: "get_usage", ...skipBehaviors === true ? { skip_behaviors: true } : {} });
3749
+ }
3750
+ var RC_CONTEXT_USAGE_DETAILS = ["summary", "full"];
3751
+ function isRcContextUsageDetail(value) {
3752
+ return RC_CONTEXT_USAGE_DETAILS.some((detail) => detail === value);
3753
+ }
3754
+ function buildRcGetContextUsagePayload(requestId, detail) {
3755
+ return buildRcControlRequestPayload(requestId, { subtype: "get_context_usage", ...detail === void 0 ? {} : { detail } });
3756
+ }
3757
+ var RC_READ_FILE_ENCODINGS = ["utf-8", "base64"];
3758
+ function isRcReadFileEncoding(value) {
3759
+ return RC_READ_FILE_ENCODINGS.some((encoding) => encoding === value);
3760
+ }
3761
+ function buildRcReadFilePayload(requestId, path32, options) {
3762
+ return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path32, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
3763
+ }
3764
+ function buildRcFileSuggestionsPayload(requestId, query) {
3765
+ return buildRcControlRequestPayload(requestId, { subtype: "file_suggestions", query });
3766
+ }
3767
+ function buildRcMcpStatusPayload(requestId) {
3768
+ return buildRcControlRequestPayload(requestId, { subtype: "mcp_status" });
3769
+ }
3770
+ function buildRcMcpReconnectPayload(requestId, serverName) {
3771
+ return buildRcControlRequestPayload(requestId, { subtype: "mcp_reconnect", serverName });
3772
+ }
3773
+ function buildRcMcpAuthenticatePayload(requestId, serverName, redirectUri) {
3774
+ return buildRcControlRequestPayload(requestId, { subtype: "mcp_authenticate", serverName, redirectUri });
3775
+ }
3776
+ function buildRcMcpOAuthCallbackUrlPayload(requestId, serverName, callbackUrl) {
3777
+ return buildRcControlRequestPayload(requestId, { subtype: "mcp_oauth_callback_url", serverName, callbackUrl });
3778
+ }
3779
+ function buildRcKeepAlivePayload() {
3780
+ return { type: "keep_alive" };
3741
3781
  }
3742
3782
  async function answerRcControlRequest(deps, sessionId, requestId, decision) {
3743
3783
  const credential = deps.credentialOf(sessionId);
@@ -3764,7 +3804,7 @@ async function answerRcControlRequest(deps, sessionId, requestId, decision) {
3764
3804
  }
3765
3805
  return result;
3766
3806
  }
3767
- async function interruptRcSession(deps, sessionId) {
3807
+ async function deliverRcClientPayload(deps, sessionId, payload, action, to = "events") {
3768
3808
  const credential = deps.credentialOf(sessionId);
3769
3809
  if (credential === void 0) {
3770
3810
  return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
@@ -3772,12 +3812,12 @@ async function interruptRcSession(deps, sessionId) {
3772
3812
  if (credential.authorization === void 0) {
3773
3813
  return noOAuthCredentialResult(sessionId);
3774
3814
  }
3775
- const body = JSON.stringify(buildRcEventWriteBody(buildRcInterruptPayload(deps.newUuid())));
3815
+ const body = JSON.stringify(buildRcEventWriteBody(payload));
3776
3816
  let answer;
3777
3817
  try {
3778
- answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3818
+ answer = await (to === "teleport-events" ? deps.dial.writeTeleportEvents(sessionId, rcWriteHeaders(credential), body) : deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body));
3779
3819
  } catch (error) {
3780
- return { ok: false, message: `the door could not reach the API host to interrupt session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
3820
+ return { ok: false, message: `the door could not reach the API host to ${action}: ${error instanceof Error ? error.message : String(error)}` };
3781
3821
  }
3782
3822
  const result = rcEventWriteResultFromAnswer(answer);
3783
3823
  if (result.ok) {
@@ -3785,53 +3825,102 @@ async function interruptRcSession(deps, sessionId) {
3785
3825
  }
3786
3826
  return result;
3787
3827
  }
3828
+ async function interruptRcSession(deps, sessionId) {
3829
+ const requestId = deps.newUuid();
3830
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcInterruptPayload(requestId), `interrupt session ${sessionId}`);
3831
+ return result.ok ? { ...result, requestId } : result;
3832
+ }
3788
3833
  async function setRcSessionModel(deps, sessionId, model) {
3789
3834
  if (model === "") {
3790
3835
  return { ok: false, message: "a set-model write needs a non-empty model id: the SDK's own field treats an omitted or null value as a reset to the session default, which this operation never sends, and an empty string is neither" };
3791
3836
  }
3792
- const credential = deps.credentialOf(sessionId);
3793
- if (credential === void 0) {
3794
- return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
3795
- }
3796
- if (credential.authorization === void 0) {
3797
- return noOAuthCredentialResult(sessionId);
3837
+ const requestId = deps.newUuid();
3838
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcSetModelPayload(requestId, model), `set the model on session ${sessionId}`);
3839
+ return result.ok ? { ...result, requestId } : result;
3840
+ }
3841
+ async function setRcSessionPermissionMode(deps, sessionId, mode) {
3842
+ const requestId = deps.newUuid();
3843
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcSetPermissionModePayload(requestId, mode), `set the permission mode on session ${sessionId}`);
3844
+ return result.ok ? { ...result, requestId } : result;
3845
+ }
3846
+ async function endRcSession(deps, sessionId, reason) {
3847
+ if (reason === "") {
3848
+ return { ok: false, message: "an end-session write needs either no reason or a non-empty one: the worker reads an absent reason as its own unspecified form, and an empty string is neither" };
3798
3849
  }
3799
- const body = JSON.stringify(buildRcEventWriteBody(buildRcSetModelPayload(deps.newUuid(), model)));
3800
- let answer;
3801
- try {
3802
- answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3803
- } catch (error) {
3804
- return { ok: false, message: `the door could not reach the API host to set the model on session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
3850
+ const requestId = deps.newUuid();
3851
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcEndSessionPayload(requestId, reason), `end session ${sessionId}`);
3852
+ return result.ok ? { ...result, requestId } : result;
3853
+ }
3854
+ async function getRcSessionUsage(deps, sessionId, skipBehaviors) {
3855
+ const requestId = deps.newUuid();
3856
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcGetUsagePayload(requestId, skipBehaviors), `request the usage of session ${sessionId}`);
3857
+ return result.ok ? { ...result, requestId } : result;
3858
+ }
3859
+ async function getRcSessionContextUsage(deps, sessionId, detail) {
3860
+ const requestId = deps.newUuid();
3861
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcGetContextUsagePayload(requestId, detail), `request the context usage of session ${sessionId}`);
3862
+ return result.ok ? { ...result, requestId } : result;
3863
+ }
3864
+ async function readRcSessionFile(deps, sessionId, path32, options) {
3865
+ if (path32 === "") {
3866
+ return { ok: false, message: "a read-file write needs a non-empty path: the SDK's own field names the file the worker resolves against the session's cwd, and an empty string names none" };
3805
3867
  }
3806
- const result = rcEventWriteResultFromAnswer(answer);
3807
- if (result.ok) {
3808
- deps.noteSequenceNums(sessionId, result.sequenceNums);
3868
+ if (options?.maxBytes !== void 0 && (!Number.isInteger(options.maxBytes) || options.maxBytes <= 0)) {
3869
+ return { ok: false, message: "a read-file write's max-bytes is a positive whole number of bytes: the SDK's own field caps the read, and a zero or negative cap reads nothing" };
3809
3870
  }
3810
- return result;
3871
+ const requestId = deps.newUuid();
3872
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path32, options), `read ${path32} from session ${sessionId}`);
3873
+ return result.ok ? { ...result, requestId } : result;
3811
3874
  }
3812
- async function setRcSessionPermissionMode(deps, sessionId, mode) {
3813
- const credential = deps.credentialOf(sessionId);
3814
- if (credential === void 0) {
3815
- return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
3875
+ async function suggestRcSessionFiles(deps, sessionId, query) {
3876
+ const requestId = deps.newUuid();
3877
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcFileSuggestionsPayload(requestId, query), `request file suggestions from session ${sessionId}`);
3878
+ return result.ok ? { ...result, requestId } : result;
3879
+ }
3880
+ async function getRcSessionMcpStatus(deps, sessionId) {
3881
+ const requestId = deps.newUuid();
3882
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpStatusPayload(requestId), `request the MCP status of session ${sessionId}`);
3883
+ return result.ok ? { ...result, requestId } : result;
3884
+ }
3885
+ async function reconnectRcSessionMcpServer(deps, sessionId, serverName) {
3886
+ if (serverName === "") {
3887
+ return { ok: false, message: "an mcp-reconnect write needs a non-empty server name: the SDK's own field names the server as mcp_status reports it, and an empty string names none" };
3816
3888
  }
3817
- if (credential.authorization === void 0) {
3818
- return noOAuthCredentialResult(sessionId);
3889
+ const requestId = deps.newUuid();
3890
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpReconnectPayload(requestId, serverName), `reconnect MCP server ${serverName} on session ${sessionId}`);
3891
+ return result.ok ? { ...result, requestId } : result;
3892
+ }
3893
+ async function authenticateRcSessionMcpServer(deps, sessionId, serverName, redirectUri) {
3894
+ if (serverName === "" || redirectUri === "") {
3895
+ return { ok: false, message: "an mcp-authenticate write needs a non-empty server name and redirect URI: the CLI's own request names the server and where its OAuth flow redirects back to, and an empty string names neither" };
3819
3896
  }
3820
- const body = JSON.stringify(buildRcEventWriteBody(buildRcSetPermissionModePayload(deps.newUuid(), mode)));
3821
- let answer;
3822
- try {
3823
- answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3824
- } catch (error) {
3825
- return { ok: false, message: `the door could not reach the API host to set the permission mode on session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
3897
+ const requestId = deps.newUuid();
3898
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpAuthenticatePayload(requestId, serverName, redirectUri), `authenticate MCP server ${serverName} on session ${sessionId}`);
3899
+ return result.ok ? { ...result, requestId } : result;
3900
+ }
3901
+ async function submitRcSessionMcpOAuthCallbackUrl(deps, sessionId, serverName, callbackUrl) {
3902
+ if (serverName === "" || callbackUrl === "") {
3903
+ return { ok: false, message: "an mcp-oauth-callback-url write needs a non-empty server name and callback URL: the CLI's own request names the server and the URL its OAuth flow landed on, and an empty string names neither" };
3826
3904
  }
3827
- const result = rcEventWriteResultFromAnswer(answer);
3828
- if (result.ok) {
3829
- deps.noteSequenceNums(sessionId, result.sequenceNums);
3905
+ const requestId = deps.newUuid();
3906
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpOAuthCallbackUrlPayload(requestId, serverName, callbackUrl), `hand MCP server ${serverName}'s OAuth callback to session ${sessionId}`);
3907
+ return result.ok ? { ...result, requestId } : result;
3908
+ }
3909
+ async function sendRcKeepAlive(deps, sessionId) {
3910
+ return await deliverRcClientPayload(deps, sessionId, buildRcKeepAlivePayload(), `keep session ${sessionId} alive`);
3911
+ }
3912
+ async function teleportRcSession(deps, sessionId, marker) {
3913
+ if (marker === "") {
3914
+ return { ok: false, message: "a teleport write needs a non-empty marker: the teleport relay anchors on a marker line's uuid and string content, and an empty string carries neither" };
3830
3915
  }
3831
- return result;
3916
+ return await deliverRcClientPayload(deps, sessionId, buildRcUserMessagePayload(deps.newUuid(), sessionId, marker), `teleport a marker into session ${sessionId}`, "teleport-events");
3917
+ }
3918
+ function buildRcMarkReadBody(eventId) {
3919
+ return eventId === void 0 ? {} : { event_id: eventId };
3832
3920
  }
3833
3921
 
3834
3922
  // src/frontdoor/rcControl.ts
3923
+ import * as https from "node:https";
3835
3924
  var RC_PERMISSION_MODE_LIST = RC_PERMISSION_MODES.join(", ");
3836
3925
  var CONTROL_PATH_PREFIX = "/__agent-shim/rc";
3837
3926
  var CONTROL_BODY_CAP_BYTES = 10485760;
@@ -3840,6 +3929,25 @@ function answerJson(response, status, body) {
3840
3929
  response.writeHead(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")) });
3841
3930
  response.end(text);
3842
3931
  }
3932
+ function isJsonObject(value) {
3933
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3934
+ }
3935
+ function bodyString(body, field) {
3936
+ const value = body[field];
3937
+ return typeof value === "string" && value !== "" ? value : void 0;
3938
+ }
3939
+ function bodyStringAllowingEmpty(body, field) {
3940
+ const value = body[field];
3941
+ return typeof value === "string" ? value : void 0;
3942
+ }
3943
+ function bodyBoolean(body, field) {
3944
+ const value = body[field];
3945
+ return typeof value === "boolean" ? value : void 0;
3946
+ }
3947
+ function bodyPositiveInteger(body, field) {
3948
+ const value = body[field];
3949
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
3950
+ }
3843
3951
  async function readBody(request4) {
3844
3952
  return await new Promise((resolve, reject) => {
3845
3953
  const chunks = [];
@@ -3867,9 +3975,136 @@ function createRcControlHandler(deps) {
3867
3975
  const pendingPath = `${CONTROL_PATH_PREFIX}/pending`;
3868
3976
  const injectPath = `${CONTROL_PATH_PREFIX}/inject`;
3869
3977
  const answerPath = `${CONTROL_PATH_PREFIX}/answer`;
3870
- const interruptPath = `${CONTROL_PATH_PREFIX}/interrupt`;
3871
- const setModelPath = `${CONTROL_PATH_PREFIX}/set-model`;
3872
- const setPermissionModePath = `${CONTROL_PATH_PREFIX}/set-permission-mode`;
3978
+ const controlWrites = [
3979
+ {
3980
+ name: "interrupt",
3981
+ path: "interrupt",
3982
+ read: (body) => {
3983
+ const session = bodyString(body, "session");
3984
+ return session === void 0 ? { error: "the interrupt body must be JSON naming a non-empty session id" } : { session, run: async () => await deps.interrupt(session) };
3985
+ }
3986
+ },
3987
+ {
3988
+ name: "set-model",
3989
+ path: "set-model",
3990
+ read: (body) => {
3991
+ const session = bodyString(body, "session");
3992
+ const model = bodyString(body, "model");
3993
+ return session === void 0 || model === void 0 ? { error: "the set-model body must be JSON naming a non-empty session id and model id" } : { session, run: async () => await deps.setModel(session, model) };
3994
+ }
3995
+ },
3996
+ {
3997
+ name: "set-permission-mode",
3998
+ path: "set-permission-mode",
3999
+ read: (body) => {
4000
+ const session = bodyString(body, "session");
4001
+ const mode = body.mode;
4002
+ return session === void 0 || !isRcPermissionMode(mode) ? { error: `the set-permission-mode body must be JSON naming a non-empty session id and a mode the SDK's own type permits (${RC_PERMISSION_MODE_LIST})` } : { session, run: async () => await deps.setPermissionMode(session, mode) };
4003
+ }
4004
+ },
4005
+ {
4006
+ name: "end-session",
4007
+ path: "end-session",
4008
+ read: (body) => {
4009
+ const session = bodyString(body, "session");
4010
+ const reason = "reason" in body ? bodyString(body, "reason") : void 0;
4011
+ return session === void 0 || "reason" in body && reason === void 0 ? { error: "the end-session body must be JSON naming a non-empty session id, and a reason only as a non-empty string or absent" } : { session, run: async () => await deps.endSession(session, reason) };
4012
+ }
4013
+ },
4014
+ {
4015
+ name: "get-usage",
4016
+ path: "get-usage",
4017
+ read: (body) => {
4018
+ const session = bodyString(body, "session");
4019
+ const skipBehaviors = "skipBehaviors" in body ? bodyBoolean(body, "skipBehaviors") : void 0;
4020
+ return session === void 0 || "skipBehaviors" in body && skipBehaviors === void 0 ? { error: "the get-usage body must be JSON naming a non-empty session id, and skipBehaviors only as a boolean or absent" } : { session, run: async () => await deps.getUsage(session, skipBehaviors) };
4021
+ }
4022
+ },
4023
+ {
4024
+ name: "get-context-usage",
4025
+ path: "get-context-usage",
4026
+ read: (body) => {
4027
+ const session = bodyString(body, "session");
4028
+ const detail = "detail" in body && isRcContextUsageDetail(body.detail) ? body.detail : void 0;
4029
+ return session === void 0 || "detail" in body && detail === void 0 ? { error: `the get-context-usage body must be JSON naming a non-empty session id, and a detail level the SDK's own type permits (${RC_CONTEXT_USAGE_DETAILS.join(", ")}) or none` } : { session, run: async () => await deps.getContextUsage(session, detail) };
4030
+ }
4031
+ },
4032
+ {
4033
+ name: "read-file",
4034
+ path: "read-file",
4035
+ read: (body) => {
4036
+ const session = bodyString(body, "session");
4037
+ const path32 = bodyString(body, "path");
4038
+ const maxBytes = "maxBytes" in body ? bodyPositiveInteger(body, "maxBytes") : void 0;
4039
+ const encoding = "encoding" in body && isRcReadFileEncoding(body.encoding) ? body.encoding : void 0;
4040
+ const refused2 = session === void 0 || path32 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
4041
+ return refused2 ? { error: `the read-file body must be JSON naming a non-empty session id and path, maxBytes only as a positive whole number or absent, and an encoding the SDK's own type permits (${RC_READ_FILE_ENCODINGS.join(", ")}) or none` } : { session, run: async () => await deps.readFile(session, path32, { ...maxBytes === void 0 ? {} : { maxBytes }, ...encoding === void 0 ? {} : { encoding } }) };
4042
+ }
4043
+ },
4044
+ {
4045
+ name: "file-suggestions",
4046
+ path: "file-suggestions",
4047
+ read: (body) => {
4048
+ const session = bodyString(body, "session");
4049
+ const query = bodyStringAllowingEmpty(body, "query");
4050
+ return session === void 0 || query === void 0 ? { error: "the file-suggestions body must be JSON naming a non-empty session id and a query string, which may be empty" } : { session, run: async () => await deps.fileSuggestions(session, query) };
4051
+ }
4052
+ },
4053
+ {
4054
+ name: "keep-alive",
4055
+ path: "keep-alive",
4056
+ read: (body) => {
4057
+ const session = bodyString(body, "session");
4058
+ return session === void 0 ? { error: "the keep-alive body must be JSON naming a non-empty session id" } : { session, run: async () => await deps.keepAlive(session) };
4059
+ }
4060
+ },
4061
+ {
4062
+ name: "mcp-status",
4063
+ path: "mcp-status",
4064
+ read: (body) => {
4065
+ const session = bodyString(body, "session");
4066
+ return session === void 0 ? { error: "the mcp-status body must be JSON naming a non-empty session id" } : { session, run: async () => await deps.mcpStatus(session) };
4067
+ }
4068
+ },
4069
+ {
4070
+ name: "mcp-reconnect",
4071
+ path: "mcp-reconnect",
4072
+ read: (body) => {
4073
+ const session = bodyString(body, "session");
4074
+ const serverName = bodyString(body, "serverName");
4075
+ return session === void 0 || serverName === void 0 ? { error: "the mcp-reconnect body must be JSON naming a non-empty session id and server name" } : { session, run: async () => await deps.mcpReconnect(session, serverName) };
4076
+ }
4077
+ },
4078
+ {
4079
+ name: "mcp-authenticate",
4080
+ path: "mcp-authenticate",
4081
+ read: (body) => {
4082
+ const session = bodyString(body, "session");
4083
+ const serverName = bodyString(body, "serverName");
4084
+ const redirectUri = bodyString(body, "redirectUri");
4085
+ return session === void 0 || serverName === void 0 || redirectUri === void 0 ? { error: "the mcp-authenticate body must be JSON naming a non-empty session id, server name, and redirect URI" } : { session, run: async () => await deps.mcpAuthenticate(session, serverName, redirectUri) };
4086
+ }
4087
+ },
4088
+ {
4089
+ name: "mcp-oauth-callback-url",
4090
+ path: "mcp-oauth-callback-url",
4091
+ read: (body) => {
4092
+ const session = bodyString(body, "session");
4093
+ const serverName = bodyString(body, "serverName");
4094
+ const callbackUrl = bodyString(body, "callbackUrl");
4095
+ return session === void 0 || serverName === void 0 || callbackUrl === void 0 ? { error: "the mcp-oauth-callback-url body must be JSON naming a non-empty session id, server name, and callback URL" } : { session, run: async () => await deps.mcpOAuthCallbackUrl(session, serverName, callbackUrl) };
4096
+ }
4097
+ },
4098
+ {
4099
+ name: "teleport",
4100
+ path: "teleport",
4101
+ read: (body) => {
4102
+ const session = bodyString(body, "session");
4103
+ const marker = bodyString(body, "marker");
4104
+ return session === void 0 || marker === void 0 ? { error: "the teleport body must be JSON naming a non-empty session id and marker" } : { session, run: async () => await deps.teleport(session, marker) };
4105
+ }
4106
+ }
4107
+ ];
3873
4108
  return (request4, response) => {
3874
4109
  const presented = request4.headers.authorization;
3875
4110
  const bearerPrefix = "bearer ".length;
@@ -3954,73 +4189,33 @@ function createRcControlHandler(deps) {
3954
4189
  answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
3955
4190
  return;
3956
4191
  }
3957
- if (url.pathname === interruptPath) {
3958
- if (request4.method !== "POST") {
3959
- answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the interrupt operation is a POST" });
3960
- return;
3961
- }
3962
- let parsed;
3963
- try {
3964
- parsed = JSON.parse(await readBody(request4));
3965
- } catch {
3966
- answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body is not readable JSON within the control body cap" });
3967
- return;
3968
- }
3969
- if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || typeof parsed.session !== "string" || parsed.session === "") {
3970
- answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body must be JSON naming a non-empty session id" });
3971
- return;
3972
- }
3973
- const result = await deps.interrupt(parsed.session);
3974
- if (result.ok) {
3975
- answerJson(response, HTTP_STATUS.ok, { session: parsed.session, sequenceNums: result.sequenceNums });
3976
- return;
4192
+ for (const route of controlWrites) {
4193
+ if (url.pathname !== `${CONTROL_PATH_PREFIX}/${route.path}`) {
4194
+ continue;
3977
4195
  }
3978
- answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
3979
- return;
3980
- }
3981
- if (url.pathname === setModelPath) {
3982
4196
  if (request4.method !== "POST") {
3983
- answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-model operation is a POST" });
4197
+ answerJson(response, HTTP_STATUS.methodNotAllowed, { error: `the ${route.name} operation is a POST` });
3984
4198
  return;
3985
4199
  }
3986
4200
  let parsed;
3987
4201
  try {
3988
4202
  parsed = JSON.parse(await readBody(request4));
3989
4203
  } catch {
3990
- answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body is not readable JSON within the control body cap" });
3991
- return;
3992
- }
3993
- if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("model" in parsed) || typeof parsed.session !== "string" || typeof parsed.model !== "string" || parsed.session === "" || parsed.model === "") {
3994
- answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body must be JSON naming a non-empty session id and model id" });
3995
- return;
3996
- }
3997
- const result = await deps.setModel(parsed.session, parsed.model);
3998
- if (result.ok) {
3999
- answerJson(response, HTTP_STATUS.ok, { session: parsed.session, model: parsed.model, sequenceNums: result.sequenceNums });
4000
- return;
4001
- }
4002
- answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
4003
- return;
4004
- }
4005
- if (url.pathname === setPermissionModePath) {
4006
- if (request4.method !== "POST") {
4007
- answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-permission-mode operation is a POST" });
4204
+ answerJson(response, HTTP_STATUS.badRequest, { error: `the ${route.name} body is not readable JSON within the control body cap` });
4008
4205
  return;
4009
4206
  }
4010
- let parsed;
4011
- try {
4012
- parsed = JSON.parse(await readBody(request4));
4013
- } catch {
4014
- answerJson(response, HTTP_STATUS.badRequest, { error: "the set-permission-mode body is not readable JSON within the control body cap" });
4207
+ if (!isJsonObject(parsed)) {
4208
+ answerJson(response, HTTP_STATUS.badRequest, { error: `the ${route.name} body must be a JSON object` });
4015
4209
  return;
4016
4210
  }
4017
- if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("mode" in parsed) || typeof parsed.session !== "string" || parsed.session === "" || !isRcPermissionMode(parsed.mode)) {
4018
- answerJson(response, HTTP_STATUS.badRequest, { error: `the set-permission-mode body must be JSON naming a non-empty session id and a mode the SDK's own type permits (${RC_PERMISSION_MODE_LIST})` });
4211
+ const read = route.read(parsed);
4212
+ if ("error" in read) {
4213
+ answerJson(response, HTTP_STATUS.badRequest, { error: read.error });
4019
4214
  return;
4020
4215
  }
4021
- const result = await deps.setPermissionMode(parsed.session, parsed.mode);
4216
+ const result = await read.run();
4022
4217
  if (result.ok) {
4023
- answerJson(response, HTTP_STATUS.ok, { session: parsed.session, mode: parsed.mode, sequenceNums: result.sequenceNums });
4218
+ answerJson(response, HTTP_STATUS.ok, { session: read.session, ...result.requestId === void 0 ? {} : { request: result.requestId }, sequenceNums: result.sequenceNums });
4024
4219
  return;
4025
4220
  }
4026
4221
  answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
@@ -4138,6 +4333,19 @@ function sequenceNumsOfBody(body) {
4138
4333
  }
4139
4334
  return sequenceNums;
4140
4335
  }
4336
+ function requestIdOfBody(body) {
4337
+ let parsed;
4338
+ try {
4339
+ parsed = JSON.parse(body);
4340
+ } catch {
4341
+ return void 0;
4342
+ }
4343
+ if (typeof parsed !== "object" || parsed === null || !("request" in parsed)) {
4344
+ return void 0;
4345
+ }
4346
+ const value = parsed.request;
4347
+ return typeof value === "string" && value !== "" ? value : void 0;
4348
+ }
4141
4349
  function frontDoorRcControl(transport, token) {
4142
4350
  const headers = { authorization: `Bearer ${token}` };
4143
4351
  const listAnswerOf = async (path32, what) => {
@@ -4226,7 +4434,8 @@ function frontDoorRcControl(transport, token) {
4226
4434
  if (sequenceNums === void 0) {
4227
4435
  return { ok: false, message: `the door accepted the event but its answer named no sequence numbers: ${answer.body}` };
4228
4436
  }
4229
- return { ok: true, sequenceNums };
4437
+ const requestId = requestIdOfBody(answer.body);
4438
+ return requestId === void 0 ? { ok: true, sequenceNums } : { ok: true, sequenceNums, requestId };
4230
4439
  }
4231
4440
  return { ok: false, message: describeFailure(answer.status, controlErrorOf(answer.body) ?? answer.body) };
4232
4441
  };
@@ -4243,7 +4452,22 @@ function frontDoorRcControl(transport, token) {
4243
4452
  ),
4244
4453
  interruptSession: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/interrupt`, JSON.stringify({ session: sessionId }), (status, message) => `interrupting ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4245
4454
  setModel: async (sessionId, model) => await writeOf(`${CONTROL_PATH_PREFIX}/set-model`, JSON.stringify({ session: sessionId, model }), (status, message) => `setting the model on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4246
- setPermissionMode: async (sessionId, mode) => await writeOf(`${CONTROL_PATH_PREFIX}/set-permission-mode`, JSON.stringify({ session: sessionId, mode }), (status, message) => `setting the permission mode on ${sessionId} failed (HTTP ${String(status)}): ${message}`)
4455
+ setPermissionMode: async (sessionId, mode) => await writeOf(`${CONTROL_PATH_PREFIX}/set-permission-mode`, JSON.stringify({ session: sessionId, mode }), (status, message) => `setting the permission mode on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4456
+ endSession: async (sessionId, reason) => await writeOf(`${CONTROL_PATH_PREFIX}/end-session`, JSON.stringify({ session: sessionId, ...reason === void 0 ? {} : { reason } }), (status, message) => `ending ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4457
+ getUsage: async (sessionId, skipBehaviors) => await writeOf(`${CONTROL_PATH_PREFIX}/get-usage`, JSON.stringify({ session: sessionId, ...skipBehaviors === void 0 ? {} : { skipBehaviors } }), (status, message) => `requesting the usage of ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4458
+ getContextUsage: async (sessionId, detail) => await writeOf(`${CONTROL_PATH_PREFIX}/get-context-usage`, JSON.stringify({ session: sessionId, ...detail === void 0 ? {} : { detail } }), (status, message) => `requesting the context usage of ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4459
+ readFile: async (sessionId, path32, options) => await writeOf(
4460
+ `${CONTROL_PATH_PREFIX}/read-file`,
4461
+ JSON.stringify({ session: sessionId, path: path32, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
4462
+ (status, message) => `reading ${path32} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
4463
+ ),
4464
+ fileSuggestions: async (sessionId, query) => await writeOf(`${CONTROL_PATH_PREFIX}/file-suggestions`, JSON.stringify({ session: sessionId, query }), (status, message) => `requesting file suggestions from ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4465
+ keepAlive: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/keep-alive`, JSON.stringify({ session: sessionId }), (status, message) => `keeping ${sessionId} alive failed (HTTP ${String(status)}): ${message}`),
4466
+ mcpStatus: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-status`, JSON.stringify({ session: sessionId }), (status, message) => `requesting the MCP status of ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4467
+ mcpReconnect: async (sessionId, serverName) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-reconnect`, JSON.stringify({ session: sessionId, serverName }), (status, message) => `reconnecting MCP server ${serverName} on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4468
+ mcpAuthenticate: async (sessionId, serverName, redirectUri) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-authenticate`, JSON.stringify({ session: sessionId, serverName, redirectUri }), (status, message) => `authenticating MCP server ${serverName} on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4469
+ mcpOAuthCallbackUrl: async (sessionId, serverName, callbackUrl) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-oauth-callback-url`, JSON.stringify({ session: sessionId, serverName, callbackUrl }), (status, message) => `handing MCP server ${serverName}'s OAuth callback to ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4470
+ teleport: async (sessionId, marker) => await writeOf(`${CONTROL_PATH_PREFIX}/teleport`, JSON.stringify({ session: sessionId, marker }), (status, message) => `teleporting a marker into ${sessionId} failed (HTTP ${String(status)}): ${message}`)
4247
4471
  };
4248
4472
  }
4249
4473
 
@@ -4390,36 +4614,38 @@ function tlsFacts(options) {
4390
4614
  }
4391
4615
  function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
4392
4616
  const agent = new ExemptTlsAgent();
4393
- return {
4394
- writeEvents: async (sessionId, headers, body) => await new Promise((resolve, reject) => {
4395
- const options = {
4396
- host: target.host,
4397
- port: target.port,
4398
- method: "POST",
4399
- path: `/v1/code/sessions/${encodeURIComponent(sessionId)}/events`,
4400
- // The Host header names the host whose API this is, not the address dialled: a redirected test dials a loopback stand-in while still speaking to the API host by name, exactly as the forwarded paths present their SNI.
4401
- headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
4402
- agent
4403
- };
4404
- options.servername = target.servername ?? target.host;
4405
- if (target.ca !== void 0) {
4406
- options.ca = [...target.ca];
4407
- }
4408
- if (target.rejectUnauthorized !== void 0) {
4409
- options.rejectUnauthorized = target.rejectUnauthorized;
4410
- }
4411
- const request4 = https2.request(options, (response) => {
4412
- const chunks = [];
4413
- response.on("data", (chunk) => {
4414
- chunks.push(chunk);
4415
- });
4416
- response.on("end", () => {
4417
- resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
4418
- });
4617
+ const post = async (path32, headers, body) => await new Promise((resolve, reject) => {
4618
+ const options = {
4619
+ host: target.host,
4620
+ port: target.port,
4621
+ method: "POST",
4622
+ path: path32,
4623
+ // The Host header names the host whose API this is, not the address dialled: a redirected test dials a loopback stand-in while still speaking to the API host by name, exactly as the forwarded paths present their SNI.
4624
+ headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
4625
+ agent
4626
+ };
4627
+ options.servername = target.servername ?? target.host;
4628
+ if (target.ca !== void 0) {
4629
+ options.ca = [...target.ca];
4630
+ }
4631
+ if (target.rejectUnauthorized !== void 0) {
4632
+ options.rejectUnauthorized = target.rejectUnauthorized;
4633
+ }
4634
+ const request4 = https2.request(options, (response) => {
4635
+ const chunks = [];
4636
+ response.on("data", (chunk) => {
4637
+ chunks.push(chunk);
4419
4638
  });
4420
- request4.once("error", reject);
4421
- request4.end(body);
4422
- })
4639
+ response.on("end", () => {
4640
+ resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
4641
+ });
4642
+ });
4643
+ request4.once("error", reject);
4644
+ request4.end(body);
4645
+ });
4646
+ return {
4647
+ writeEvents: async (sessionId, headers, body) => await post(`/v1/code/sessions/${encodeURIComponent(sessionId)}/events`, headers, body),
4648
+ writeTeleportEvents: async (sessionId, headers, body) => await post(`/v1/code/sessions/${encodeURIComponent(sessionId)}/teleport-events`, headers, body)
4423
4649
  };
4424
4650
  }
4425
4651
  var NO_CHUNKS = {
@@ -4437,7 +4663,8 @@ function lateRcEventDial(resolve) {
4437
4663
  return built.dial;
4438
4664
  };
4439
4665
  return {
4440
- writeEvents: async (sessionId, headers, body) => await dialFor().writeEvents(sessionId, headers, body)
4666
+ writeEvents: async (sessionId, headers, body) => await dialFor().writeEvents(sessionId, headers, body),
4667
+ writeTeleportEvents: async (sessionId, headers, body) => await dialFor().writeTeleportEvents(sessionId, headers, body)
4441
4668
  };
4442
4669
  }
4443
4670
  function lateRcStreamDial(resolve) {
@@ -4450,7 +4677,8 @@ function lateRcStreamDial(resolve) {
4450
4677
  return built.dial;
4451
4678
  };
4452
4679
  return {
4453
- announcePresence: async (sessionId, headers, clientId) => await dialFor().announcePresence(sessionId, headers, clientId),
4680
+ announcePresence: async (sessionId, headers, clientId, clear) => await dialFor().announcePresence(sessionId, headers, clientId, clear),
4681
+ markRead: async (sessionId, headers, eventId) => await dialFor().markRead(sessionId, headers, eventId),
4454
4682
  openStream: async (sessionId, headers, resume, signal) => await dialFor().openStream(sessionId, headers, resume, signal)
4455
4683
  };
4456
4684
  }
@@ -4492,11 +4720,16 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
4492
4720
  return Buffer.concat(chunks).toString("utf8");
4493
4721
  };
4494
4722
  return {
4495
- announcePresence: async (sessionId, headers, clientId) => {
4496
- const body = JSON.stringify({ client_id: clientId, clear: false });
4723
+ announcePresence: async (sessionId, headers, clientId, clear) => {
4724
+ const body = JSON.stringify({ client_id: clientId, clear });
4497
4725
  const { status, response } = await request4("POST", `/v1/code/sessions/${encodeURIComponent(sessionId)}/client/presence`, { ...headers, "content-type": "application/json" }, body, new AbortController().signal);
4498
4726
  return { status, body: await readAll(response) };
4499
4727
  },
4728
+ markRead: async (sessionId, headers, eventId) => {
4729
+ const body = JSON.stringify(buildRcMarkReadBody(eventId));
4730
+ const { status, response } = await request4("POST", `/v1/code/sessions/${encodeURIComponent(sessionId)}/mark_read`, { ...headers, "content-type": "application/json" }, body, new AbortController().signal);
4731
+ return { status, body: await readAll(response) };
4732
+ },
4500
4733
  openStream: async (sessionId, headers, resume, signal) => {
4501
4734
  const path32 = resume === void 0 ? `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream` : `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream?from_sequence_num=${String(resume.fromSequenceNum)}`;
4502
4735
  const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
@@ -4865,6 +5098,52 @@ var RcSetPermissionModeInputSchema = z4.strictObject({
4865
5098
  session: z4.string().min(1),
4866
5099
  mode: RcPermissionModeSchema
4867
5100
  });
5101
+ var RcEndSessionInputSchema = z4.strictObject({
5102
+ session: z4.string().min(1),
5103
+ reason: z4.string().min(1).optional()
5104
+ });
5105
+ var RcGetUsageInputSchema = z4.strictObject({
5106
+ session: z4.string().min(1),
5107
+ skipBehaviors: z4.boolean().optional()
5108
+ });
5109
+ var RcGetContextUsageInputSchema = z4.strictObject({
5110
+ session: z4.string().min(1),
5111
+ detail: z4.enum(RC_CONTEXT_USAGE_DETAILS).optional()
5112
+ });
5113
+ var RcReadFileInputSchema = z4.strictObject({
5114
+ session: z4.string().min(1),
5115
+ path: z4.string().min(1),
5116
+ maxBytes: z4.number().int().positive().optional(),
5117
+ encoding: z4.enum(RC_READ_FILE_ENCODINGS).optional()
5118
+ });
5119
+ var RcFileSuggestionsInputSchema = z4.strictObject({
5120
+ session: z4.string().min(1),
5121
+ query: z4.string()
5122
+ });
5123
+ var RcKeepAliveInputSchema = z4.strictObject({
5124
+ session: z4.string().min(1)
5125
+ });
5126
+ var RcMcpStatusInputSchema = z4.strictObject({
5127
+ session: z4.string().min(1)
5128
+ });
5129
+ var RcMcpReconnectInputSchema = z4.strictObject({
5130
+ session: z4.string().min(1),
5131
+ serverName: z4.string().min(1)
5132
+ });
5133
+ var RcMcpAuthenticateInputSchema = z4.strictObject({
5134
+ session: z4.string().min(1),
5135
+ serverName: z4.string().min(1),
5136
+ redirectUri: z4.string().min(1)
5137
+ });
5138
+ var RcMcpOAuthCallbackUrlInputSchema = z4.strictObject({
5139
+ session: z4.string().min(1),
5140
+ serverName: z4.string().min(1),
5141
+ callbackUrl: z4.string().min(1)
5142
+ });
5143
+ var RcTeleportInputSchema = z4.strictObject({
5144
+ session: z4.string().min(1),
5145
+ marker: z4.string().min(1)
5146
+ });
4868
5147
  var RcListOutputSchema = z4.strictObject({ sessions: z4.readonly(z4.array(RcSessionSummarySchema)) });
4869
5148
  var RcStatusOutputSchema = z4.strictObject({ statuses: z4.readonly(z4.array(RcSessionStatusSchema)) });
4870
5149
  var RcPendingOutputSchema = z4.strictObject({ pending: z4.readonly(z4.array(RcPendingRequestSummarySchema)) });
@@ -4872,6 +5151,11 @@ var RcWriteOutputSchema = z4.strictObject({
4872
5151
  session: z4.string(),
4873
5152
  sequenceNums: z4.readonly(z4.array(z4.number()))
4874
5153
  });
5154
+ var RcControlWriteOutputSchema = z4.strictObject({
5155
+ session: z4.string(),
5156
+ request: z4.string(),
5157
+ sequenceNums: z4.readonly(z4.array(z4.number()))
5158
+ });
4875
5159
  var RcAnswerOutputSchema = z4.strictObject({
4876
5160
  session: z4.string(),
4877
5161
  request: z4.string(),
@@ -4900,6 +5184,15 @@ function createRcApiRouter(deps) {
4900
5184
  }
4901
5185
  return result.sequenceNums;
4902
5186
  };
5187
+ const deliveredControl = (sessionId, result) => {
5188
+ if (!result.ok) {
5189
+ throw new ORPCError("BAD_GATEWAY", { message: result.message });
5190
+ }
5191
+ if (result.requestId === void 0) {
5192
+ throw new ORPCError("INTERNAL_SERVER_ERROR", { message: "the control request was delivered but the door cannot name the request id it minted, so the worker's answer cannot be matched" });
5193
+ }
5194
+ return { request: result.requestId, sequenceNums: result.sequenceNums };
5195
+ };
4903
5196
  return {
4904
5197
  rc: {
4905
5198
  list: authed.output(RcListOutputSchema).handler(() => ({ sessions: deps.list() })),
@@ -4922,9 +5215,20 @@ function createRcApiRouter(deps) {
4922
5215
  const decision = { approve: input.approve, message: input.approve || input.text === void 0 ? void 0 : input.text };
4923
5216
  return { session: input.session, request: input.request, sequenceNums: delivered(input.session, await deps.answer(input.session, input.request, decision)) };
4924
5217
  }),
4925
- interrupt: authed.input(RcInterruptInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.interrupt(input.session)) })),
4926
- setModel: authed.input(RcSetModelInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.setModel(input.session, input.model)) })),
4927
- setPermissionMode: authed.input(RcSetPermissionModeInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.setPermissionMode(input.session, input.mode)) })),
5218
+ interrupt: authed.input(RcInterruptInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.interrupt(input.session)) })),
5219
+ setModel: authed.input(RcSetModelInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.setModel(input.session, input.model)) })),
5220
+ setPermissionMode: authed.input(RcSetPermissionModeInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.setPermissionMode(input.session, input.mode)) })),
5221
+ endSession: authed.input(RcEndSessionInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.endSession(input.session, input.reason)) })),
5222
+ getUsage: authed.input(RcGetUsageInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.getUsage(input.session, input.skipBehaviors)) })),
5223
+ getContextUsage: authed.input(RcGetContextUsageInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.getContextUsage(input.session, input.detail)) })),
5224
+ readFile: authed.input(RcReadFileInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.readFile(input.session, input.path, { ...input.maxBytes === void 0 ? {} : { maxBytes: input.maxBytes }, ...input.encoding === void 0 ? {} : { encoding: input.encoding } })) })),
5225
+ fileSuggestions: authed.input(RcFileSuggestionsInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.fileSuggestions(input.session, input.query)) })),
5226
+ keepAlive: authed.input(RcKeepAliveInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.keepAlive(input.session)) })),
5227
+ mcpStatus: authed.input(RcMcpStatusInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpStatus(input.session)) })),
5228
+ mcpReconnect: authed.input(RcMcpReconnectInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpReconnect(input.session, input.serverName)) })),
5229
+ mcpAuthenticate: authed.input(RcMcpAuthenticateInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpAuthenticate(input.session, input.serverName, input.redirectUri)) })),
5230
+ mcpOAuthCallbackUrl: authed.input(RcMcpOAuthCallbackUrlInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpOAuthCallbackUrl(input.session, input.serverName, input.callbackUrl)) })),
5231
+ teleport: authed.input(RcTeleportInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.teleport(input.session, input.marker)) })),
4928
5232
  subscribe: authed.input(RcSessionQuerySchema).output(eventIterator(RcStreamEventSchema)).handler(async function* ({ input, signal }) {
4929
5233
  const queue = [];
4930
5234
  let wake;
@@ -9881,9 +10185,26 @@ function createRcStreamHub(deps) {
9881
10185
  }
9882
10186
  return headers;
9883
10187
  };
10188
+ let receiptFailureLogged = false;
10189
+ const sendReceipt = (attachment, headers, eventId) => {
10190
+ if (eventId === void 0) {
10191
+ return;
10192
+ }
10193
+ deps.dial.markRead(attachment.sessionId, headers, eventId).then(
10194
+ () => {
10195
+ receiptFailureLogged = false;
10196
+ },
10197
+ (error) => {
10198
+ if (!receiptFailureLogged) {
10199
+ receiptFailureLogged = true;
10200
+ deps.log?.(`rc stream ${attachment.sessionId}: the read receipt for event ${eventId} could not be sent (${error instanceof Error ? error.message : String(error)}); further receipt failures stay quiet until one lands`);
10201
+ }
10202
+ }
10203
+ );
10204
+ };
9884
10205
  const attempt = async (attachment, credential) => {
9885
10206
  const headers = streamHeaders(credential);
9886
- const presence = await deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId);
10207
+ const presence = await deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId, false);
9887
10208
  if (presence.status === HTTP_STATUS.unauthorized) {
9888
10209
  return "unauthorized";
9889
10210
  }
@@ -9903,6 +10224,7 @@ function createRcStreamHub(deps) {
9903
10224
  const parser = createSseParser();
9904
10225
  for await (const chunk of answer.chunks) {
9905
10226
  attachment.controller.signal.throwIfAborted();
10227
+ let lastEventId;
9906
10228
  for (const parsed of parser.write(Buffer.from(chunk).toString("utf8"))) {
9907
10229
  const envelope = parseRcStreamEnvelope(parsed);
9908
10230
  if (envelope === void 0) {
@@ -9910,10 +10232,21 @@ function createRcStreamHub(deps) {
9910
10232
  }
9911
10233
  deps.fileStreamEvent(attachment.sessionId, envelope);
9912
10234
  deps.fanout.publish({ session: attachment.sessionId, envelope });
10235
+ if (typeof envelope.event_id === "string" && envelope.event_type !== "mark_read") {
10236
+ lastEventId = envelope.event_id;
10237
+ }
9913
10238
  }
10239
+ sendReceipt(attachment, headers, lastEventId);
9914
10240
  }
9915
10241
  return "dropped";
9916
10242
  };
10243
+ const clearPresence = (attachment, headers) => {
10244
+ if (headers === void 0) {
10245
+ return;
10246
+ }
10247
+ void deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId, true).catch(() => {
10248
+ });
10249
+ };
9917
10250
  const runLoop = async (attachment) => {
9918
10251
  let retriedUnauthorized = false;
9919
10252
  for (; ; ) {
@@ -9925,6 +10258,7 @@ function createRcStreamHub(deps) {
9925
10258
  if (!live || credential?.authorization === void 0) {
9926
10259
  deps.log?.(`rc stream ${attachment.sessionId}: the attachment ended (${live ? "the session's client credential is no longer known" : "the tracker gave up on the session"})`);
9927
10260
  persistCursor(attachment.sessionId);
10261
+ clearPresence(attachment, credential === void 0 ? void 0 : streamHeaders(credential));
9928
10262
  if (attachments.get(attachment.sessionId) === attachment) {
9929
10263
  attachments.delete(attachment.sessionId);
9930
10264
  }
@@ -9987,6 +10321,8 @@ function createRcStreamHub(deps) {
9987
10321
  close: () => {
9988
10322
  for (const attachment of attachments.values()) {
9989
10323
  persistCursor(attachment.sessionId);
10324
+ const credential = deps.credentialOf(attachment.sessionId);
10325
+ clearPresence(attachment, credential?.authorization === void 0 ? void 0 : streamHeaders(credential));
9990
10326
  attachment.stop();
9991
10327
  }
9992
10328
  attachments.clear();
@@ -9994,6 +10330,193 @@ function createRcStreamHub(deps) {
9994
10330
  };
9995
10331
  }
9996
10332
 
10333
+ // src/frontdoor/rcSelfHostEndpoints.ts
10334
+ var RC_SOURCE_WORKER = "worker";
10335
+ var RC_SOURCE_CLIENT = "client";
10336
+ function isRecord6(value) {
10337
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10338
+ }
10339
+ function parseJsonObject2(body) {
10340
+ let parsed;
10341
+ try {
10342
+ parsed = JSON.parse(body);
10343
+ } catch {
10344
+ return void 0;
10345
+ }
10346
+ return isRecord6(parsed) ? parsed : void 0;
10347
+ }
10348
+ function nonEmptyString2(record, field) {
10349
+ const value = record === void 0 ? void 0 : record[field];
10350
+ return typeof value === "string" && value !== "" ? value : void 0;
10351
+ }
10352
+ function numberOf(record, field) {
10353
+ const value = record === void 0 ? void 0 : record[field];
10354
+ if (typeof value === "number" && Number.isFinite(value)) {
10355
+ return value;
10356
+ }
10357
+ if (typeof value === "string" && /^[0-9]+$/.test(value)) {
10358
+ return Number(value);
10359
+ }
10360
+ return void 0;
10361
+ }
10362
+ function pageOfRcChannel(url, rows, idOf, rowOf) {
10363
+ const query = new URL(url, "http://127.0.0.1").searchParams;
10364
+ const anchor = query.get("after_event_id") ?? query.get("cursor");
10365
+ const rawLimit = query.get("limit");
10366
+ const limit = rawLimit === null ? void 0 : numberOf({ value: rawLimit }, "value");
10367
+ if (limit !== void 0 && limit < 1) {
10368
+ return { error: "the limit names a whole number of rows of at least one" };
10369
+ }
10370
+ const from = anchor === null ? 0 : rows.findIndex((row) => idOf(row) === anchor) + 1;
10371
+ if (anchor !== null && from === 0) {
10372
+ return { error: `the anchor names an event id this surface holds no row for: ${anchor}` };
10373
+ }
10374
+ const page = limit === void 0 ? rows.slice(from) : rows.slice(from, from + limit);
10375
+ const last = page[page.length - 1];
10376
+ return { data: page.map(rowOf), ...last === void 0 ? {} : { next_cursor: idOf(last) } };
10377
+ }
10378
+ async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
10379
+ const { request: request4, response, session } = ctx;
10380
+ if (tail === "client/presence" && method === "POST") {
10381
+ const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10382
+ const clientId = nonEmptyString2(body, "client_id");
10383
+ const clear = body?.clear;
10384
+ if (clientId === void 0 || clear !== void 0 && typeof clear !== "boolean") {
10385
+ await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a presence announcement is JSON naming a non-empty client_id, with clear only as a boolean or absent" } });
10386
+ return true;
10387
+ }
10388
+ if (clear === true) {
10389
+ session.clients.delete(clientId);
10390
+ ctx.log?.(`rc selfhost ${session.id}: client ${clientId} cleared its presence`);
10391
+ } else {
10392
+ session.clients.set(clientId, ctx.now());
10393
+ ctx.log?.(`rc selfhost ${session.id}: client ${clientId} announced its presence${session.clients.size === 1 ? "" : ` (${String(session.clients.size)} clients now announced)`}`);
10394
+ }
10395
+ await ctx.answerJson(response, HTTP_STATUS.ok, { refresh_after_seconds: ctx.presenceRefreshSeconds });
10396
+ return true;
10397
+ }
10398
+ if (tail === "mark_read" && method === "POST") {
10399
+ const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10400
+ const eventId = nonEmptyString2(body, "event_id");
10401
+ const clientId = nonEmptyString2(body, "client_id");
10402
+ if (body !== void 0 && "event_id" in body && eventId === void 0 || "client_id" in (body ?? {}) && clientId === void 0) {
10403
+ await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a read receipt is JSON naming the event_id it read or none for the whole session, and client_id only as a non-empty string or absent" } });
10404
+ return true;
10405
+ }
10406
+ const receipt = ctx.publish(session, clientId ?? RC_SOURCE_CLIENT, { type: "mark_read", ...eventId === void 0 ? {} : { event_id: eventId }, ...clientId === void 0 ? {} : { client_id: clientId } }, true);
10407
+ ctx.log?.(`rc selfhost ${session.id}: ${clientId === void 0 ? "an unnamed client" : `client ${clientId}`} marked ${eventId === void 0 ? "the session read" : `event ${eventId} read`} as sequence ${String(receipt.sequenceNum)}`);
10408
+ await ctx.answerJson(response, HTTP_STATUS.ok, {});
10409
+ return true;
10410
+ }
10411
+ if (tail === "teleport-events" && method === "POST") {
10412
+ const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10413
+ const events = body?.events;
10414
+ if (!Array.isArray(events) || events.length === 0 || events.length > maxBatchEvents) {
10415
+ await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: `a teleport write is a non-empty array of at most ${String(maxBatchEvents)} events` } });
10416
+ return true;
10417
+ }
10418
+ const results = [];
10419
+ for (const event of events) {
10420
+ if (!isRecord6(event) || !isRecord6(event.payload)) {
10421
+ await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "every event in a teleport write is an object carrying one payload object" } });
10422
+ return true;
10423
+ }
10424
+ const published = ctx.publish(session, RC_SOURCE_CLIENT, event.payload);
10425
+ results.push({ sequence_num: String(published.sequenceNum), duplicate: published.duplicate });
10426
+ }
10427
+ ctx.sweep();
10428
+ ctx.log?.(`rc selfhost ${session.id}: teleport write published ${String(results.length)} marker event(s)`);
10429
+ await ctx.answerJson(response, HTTP_STATUS.ok, { results });
10430
+ return true;
10431
+ }
10432
+ if (tail === "teleport-events" && method === "GET") {
10433
+ const page = pageOfRcChannel(request4.url, session.events, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.payload }));
10434
+ if ("error" in page) {
10435
+ await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: page.error } });
10436
+ return true;
10437
+ }
10438
+ await ctx.answerJson(response, HTTP_STATUS.ok, page);
10439
+ return true;
10440
+ }
10441
+ return false;
10442
+ }
10443
+ var compatSessionRow = (session, now) => ({ id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), updated_at: new Date(now()).toISOString(), session_status: "active" });
10444
+ async function serveRcCompatSessions(ctx, pathname, compatPath) {
10445
+ const { request: request4, response } = ctx;
10446
+ const method = request4.method.toUpperCase();
10447
+ if (pathname === compatPath) {
10448
+ if (method !== "GET") {
10449
+ await ctx.answerJson(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
10450
+ return;
10451
+ }
10452
+ const listRefusal = ctx.credentialRefusal(request4.headers);
10453
+ if (listRefusal !== void 0) {
10454
+ await ctx.unauthorized(response, listRefusal);
10455
+ return;
10456
+ }
10457
+ await ctx.answerJson(response, HTTP_STATUS.ok, { data: [...ctx.sessions.values()].map((session2) => compatSessionRow(session2, ctx.now)) });
10458
+ return;
10459
+ }
10460
+ const rest = pathname.slice(`${compatPath}/`.length);
10461
+ const slash = rest.indexOf("/");
10462
+ const address = slash === -1 ? rest : rest.slice(0, slash);
10463
+ const compatTail = slash === -1 ? "" : rest.slice(slash + 1);
10464
+ const refusal = ctx.credentialRefusal(request4.headers);
10465
+ if (refusal !== void 0) {
10466
+ await ctx.unauthorized(response, refusal);
10467
+ return;
10468
+ }
10469
+ const session = ctx.sessions.get(address) ?? ctx.sessions.get(`cse_${address}`);
10470
+ if (compatTail === "") {
10471
+ if (method === "GET") {
10472
+ if (session === void 0) {
10473
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
10474
+ return;
10475
+ }
10476
+ await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
10477
+ return;
10478
+ }
10479
+ if (method === "PATCH" || method === "PUT") {
10480
+ if (session === void 0) {
10481
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
10482
+ return;
10483
+ }
10484
+ const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10485
+ const title = nonEmptyString2(body, "title");
10486
+ if (title === void 0) {
10487
+ await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a compatibility session update is JSON naming a non-empty title" } });
10488
+ return;
10489
+ }
10490
+ session.title = title;
10491
+ session.lastTrafficAt = ctx.now();
10492
+ ctx.log?.(`rc selfhost ${session.id}: retitled through the compatibility family`);
10493
+ await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
10494
+ return;
10495
+ }
10496
+ await ctx.answerJson(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session read is a GET and its update a PATCH or PUT" } });
10497
+ return;
10498
+ }
10499
+ if ((compatTail === "archive" || compatTail === "unarchive") && method === "POST") {
10500
+ await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES);
10501
+ if (session === void 0) {
10502
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
10503
+ return;
10504
+ }
10505
+ if (compatTail === "unarchive") {
10506
+ await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
10507
+ return;
10508
+ }
10509
+ for (const sink of [...session.workerStreams, ...session.clientStreams]) {
10510
+ sink.retire();
10511
+ }
10512
+ ctx.sessions.delete(session.id);
10513
+ ctx.log?.(`rc selfhost ${session.id}: archived through the compatibility family`);
10514
+ await ctx.answerJson(response, HTTP_STATUS.ok, {});
10515
+ return;
10516
+ }
10517
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the compatibility session family serves no path "${compatTail}"` } });
10518
+ }
10519
+
9997
10520
  // src/frontdoor/rcWebFetch.ts
9998
10521
  import * as dns2 from "node:dns";
9999
10522
  import * as http2 from "node:http";
@@ -10262,21 +10785,19 @@ var RC_PRESENCE_REFRESH_SECONDS = 60;
10262
10785
  var RC_SELF_HOST_MAX_BATCH_EVENTS = 100;
10263
10786
  var RC_SELF_HOST_WORKER_EPOCH = 1;
10264
10787
  var RC_STREAM_EVENT_NAME2 = "client_event";
10265
- var RC_SOURCE_WORKER = "worker";
10266
- var RC_SOURCE_CLIENT = "client";
10267
10788
  var CONFLICT_REASON_HEADER2 = "x-ccr-conflict-reason";
10268
10789
  var CONFLICT_REASON_EPOCH_STALE = "epoch_stale";
10269
- function isRecord6(value) {
10790
+ function isRecord7(value) {
10270
10791
  return typeof value === "object" && value !== null && !Array.isArray(value);
10271
10792
  }
10272
10793
  function isWebSearchRefusal(value) {
10273
10794
  return !Array.isArray(value);
10274
10795
  }
10275
- function nonEmptyString2(record, field) {
10796
+ function nonEmptyString3(record, field) {
10276
10797
  const value = record === void 0 ? void 0 : record[field];
10277
10798
  return typeof value === "string" && value !== "" ? value : void 0;
10278
10799
  }
10279
- function numberOf(record, field) {
10800
+ function numberOf2(record, field) {
10280
10801
  const value = record === void 0 ? void 0 : record[field];
10281
10802
  if (typeof value === "number" && Number.isFinite(value)) {
10282
10803
  return value;
@@ -10343,14 +10864,14 @@ async function readBody2(request4, capBytes) {
10343
10864
  });
10344
10865
  });
10345
10866
  }
10346
- function parseJsonObject2(body) {
10867
+ function parseJsonObject3(body) {
10347
10868
  let parsed;
10348
10869
  try {
10349
10870
  parsed = JSON.parse(body);
10350
10871
  } catch {
10351
10872
  return void 0;
10352
10873
  }
10353
- return isRecord6(parsed) ? parsed : void 0;
10874
+ return isRecord7(parsed) ? parsed : void 0;
10354
10875
  }
10355
10876
  function mintWorkerJwt(randomToken, issuedAt) {
10356
10877
  const encode = (value) => Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
@@ -10373,11 +10894,16 @@ function createRcSelfHostSurface(deps) {
10373
10894
  for (const session of sessions.values()) {
10374
10895
  while (session.events[0] !== void 0 && now - session.events[0].storedAt > RC_SELF_HOST_RETENTION_MS) {
10375
10896
  const dropped = session.events.shift();
10376
- const uuid = dropped === void 0 ? void 0 : nonEmptyString2(dropped.payload, "uuid");
10897
+ const uuid = dropped === void 0 ? void 0 : nonEmptyString3(dropped.payload, "uuid");
10377
10898
  if (uuid !== void 0) {
10378
10899
  session.seenPayloadUuids.delete(uuid);
10379
10900
  }
10380
10901
  }
10902
+ for (const [clientId, lastSeenAt] of session.clients) {
10903
+ if (now - lastSeenAt > RC_SELF_HOST_RETENTION_MS) {
10904
+ session.clients.delete(clientId);
10905
+ }
10906
+ }
10381
10907
  if (session.workerStreams.size === 0 && session.clientStreams.size === 0 && now - session.lastTrafficAt > RC_SELF_HOST_RETENTION_MS) {
10382
10908
  sessions.delete(session.id);
10383
10909
  }
@@ -10416,7 +10942,7 @@ function createRcSelfHostSurface(deps) {
10416
10942
  const frameOf = (event) => {
10417
10943
  const envelope = {
10418
10944
  event_id: event.eventId,
10419
- event_type: nonEmptyString2(event.payload, "type") ?? "event",
10945
+ event_type: nonEmptyString3(event.payload, "type") ?? "event",
10420
10946
  sequence_num: event.sequenceNum,
10421
10947
  source: event.source,
10422
10948
  payload: event.payload,
@@ -10430,7 +10956,7 @@ data: ${JSON.stringify(envelope)}
10430
10956
  };
10431
10957
  const holdStream = async (session, which, wantsReplay, request4, response) => {
10432
10958
  const url = new URL(request4.url, "http://127.0.0.1");
10433
- const cursor = numberOf({ value: url.searchParams.get("from_sequence_num") }, "value") ?? numberOf({ value: singleHeader2(request4.headers, "last-event-id") }, "value");
10959
+ const cursor = numberOf2({ value: url.searchParams.get("from_sequence_num") }, "value") ?? numberOf2({ value: singleHeader2(request4.headers, "last-event-id") }, "value");
10434
10960
  const replay = cursor === void 0 ? [] : session.events.filter((event) => wantsReplay(event) && event.sequenceNum > cursor);
10435
10961
  response.start(HTTP_STATUS.ok, { "content-type": "text/event-stream", "cache-control": "no-cache" });
10436
10962
  response.flush();
@@ -10459,8 +10985,8 @@ data: ${JSON.stringify(envelope)}
10459
10985
  sink.retire();
10460
10986
  response.end();
10461
10987
  };
10462
- const publish = (session, source, payload) => {
10463
- const uuid = nonEmptyString2(payload, "uuid");
10988
+ const publish = (session, source, payload, toWorker = source === RC_SOURCE_CLIENT) => {
10989
+ const uuid = nonEmptyString3(payload, "uuid");
10464
10990
  const duplicate = uuid !== void 0 && session.seenPayloadUuids.has(uuid);
10465
10991
  session.sequenceNum += 1;
10466
10992
  const event = {
@@ -10468,9 +10994,9 @@ data: ${JSON.stringify(envelope)}
10468
10994
  eventId: uuid ?? deps.newUuid(),
10469
10995
  source,
10470
10996
  // The one enrichment the real service was observed making on the live rig's stream: the wall-clock instant it took the write. The worker's handler ignores it; the door's own parser forwards it verbatim.
10471
- payload: source === RC_SOURCE_CLIENT ? { ...payload, server_received_wall_ms: deps.now() } : payload,
10997
+ payload: source === RC_SOURCE_WORKER ? payload : { ...payload, server_received_wall_ms: deps.now() },
10472
10998
  createdAt: new Date(deps.now()).toISOString(),
10473
- toWorker: source === RC_SOURCE_CLIENT,
10999
+ toWorker,
10474
11000
  storedAt: deps.now()
10475
11001
  };
10476
11002
  session.events.push(event);
@@ -10518,8 +11044,26 @@ data: ${JSON.stringify(envelope)}
10518
11044
  }
10519
11045
  session.lastTrafficAt = deps.now();
10520
11046
  if (tail === "") {
11047
+ if (method === "PATCH" || method === "PUT") {
11048
+ const refusal = credentialRefusal(request4.headers);
11049
+ if (refusal !== void 0) {
11050
+ await unauthorized(response, refusal);
11051
+ return;
11052
+ }
11053
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11054
+ const title = nonEmptyString3(body, "title");
11055
+ if (title === void 0) {
11056
+ await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a session update is JSON naming a non-empty title" } });
11057
+ return;
11058
+ }
11059
+ session.title = title;
11060
+ session.lastTrafficAt = deps.now();
11061
+ deps.log?.(`rc selfhost ${session.id}: retitled at its client's request`);
11062
+ await answerJson2(response, HTTP_STATUS.ok, {});
11063
+ return;
11064
+ }
10521
11065
  if (method !== "GET") {
10522
- await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the session read is a GET" } });
11066
+ await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the session read is a GET, and its update a PATCH or PUT" } });
10523
11067
  return;
10524
11068
  }
10525
11069
  await answerJson2(response, HTTP_STATUS.ok, { session: { id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), status: "active" } });
@@ -10564,17 +11108,17 @@ data: ${JSON.stringify(envelope)}
10564
11108
  return;
10565
11109
  }
10566
11110
  if (workerTail === "" && method === "PUT") {
10567
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
10568
- const claimedEpoch = numberOf(body, "worker_epoch");
11111
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11112
+ const claimedEpoch = numberOf2(body, "worker_epoch");
10569
11113
  if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
10570
11114
  await epochStale(response);
10571
11115
  return;
10572
11116
  }
10573
- const status = nonEmptyString2(body, "worker_status");
11117
+ const status = nonEmptyString3(body, "worker_status");
10574
11118
  if (status !== void 0) {
10575
11119
  session.workerStatus = status;
10576
11120
  }
10577
- if (isRecord6(body?.external_metadata)) {
11121
+ if (isRecord7(body?.external_metadata)) {
10578
11122
  session.externalMetadata = body.external_metadata;
10579
11123
  }
10580
11124
  await answerJson2(response, HTTP_STATUS.ok, {});
@@ -10591,8 +11135,8 @@ data: ${JSON.stringify(envelope)}
10591
11135
  return;
10592
11136
  }
10593
11137
  if (workerTail === "events" && method === "POST") {
10594
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
10595
- const claimedEpoch = numberOf(body, "worker_epoch");
11138
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11139
+ const claimedEpoch = numberOf2(body, "worker_epoch");
10596
11140
  if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
10597
11141
  await epochStale(response);
10598
11142
  return;
@@ -10604,7 +11148,7 @@ data: ${JSON.stringify(envelope)}
10604
11148
  }
10605
11149
  const results = [];
10606
11150
  for (const event of events) {
10607
- if (!isRecord6(event) || !isRecord6(event.payload)) {
11151
+ if (!isRecord7(event) || !isRecord7(event.payload)) {
10608
11152
  await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "every event in a batch is an object carrying one payload object" } });
10609
11153
  return;
10610
11154
  }
@@ -10625,8 +11169,8 @@ data: ${JSON.stringify(envelope)}
10625
11169
  return;
10626
11170
  }
10627
11171
  if (workerTail === "internal-events" && method === "POST") {
10628
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
10629
- const claimedEpoch = numberOf(body, "worker_epoch");
11172
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11173
+ const claimedEpoch = numberOf2(body, "worker_epoch");
10630
11174
  if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
10631
11175
  await epochStale(response);
10632
11176
  return;
@@ -10637,19 +11181,26 @@ data: ${JSON.stringify(envelope)}
10637
11181
  return;
10638
11182
  }
10639
11183
  for (const event of events) {
10640
- if (isRecord6(event)) {
10641
- session.internalEvents.push({ cursor: deps.newUuid(), body: event });
11184
+ if (isRecord7(event)) {
11185
+ session.internalEvents.push({ eventId: deps.newUuid(), body: event });
10642
11186
  }
10643
11187
  }
10644
11188
  await answerJson2(response, HTTP_STATUS.ok, {});
10645
11189
  return;
10646
11190
  }
10647
11191
  if (workerTail === "internal-events" && method === "GET") {
10648
- const url = new URL(request4.url, "http://127.0.0.1");
10649
- const cursor = url.searchParams.get("cursor");
10650
- const from = cursor === null ? 0 : session.internalEvents.findIndex((event) => event.cursor === cursor) + 1;
10651
- const page = session.internalEvents.slice(from);
10652
- await answerJson2(response, HTTP_STATUS.ok, { data: page.map((event) => event.body), next_cursor: page.length === 0 ? void 0 : page[page.length - 1]?.cursor });
11192
+ const query = new URL(request4.url, "http://127.0.0.1").searchParams;
11193
+ if (query.get("session_agent_id") !== null || query.get("subagents") !== null) {
11194
+ await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "this surface keeps one internal-events channel per session, so an agent-scoped read has no subset to serve" } });
11195
+ return;
11196
+ }
11197
+ const page = pageOfRcChannel(request4.url, session.internalEvents, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.body }));
11198
+ if ("error" in page) {
11199
+ const anchorUnknown = query.get("after_event_id") !== null && page.error.startsWith("the anchor names an event id");
11200
+ await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: anchorUnknown ? "after_event_id_not_found" : "invalid_request_error", message: page.error } });
11201
+ return;
11202
+ }
11203
+ await answerJson2(response, HTTP_STATUS.ok, page);
10653
11204
  return;
10654
11205
  }
10655
11206
  if (workerTail === "diagnostics" && method === "POST") {
@@ -10658,8 +11209,8 @@ data: ${JSON.stringify(envelope)}
10658
11209
  return;
10659
11210
  }
10660
11211
  if (workerTail === "web-fetch" && method === "POST") {
10661
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
10662
- const target = nonEmptyString2(body, "url");
11212
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11213
+ const target = nonEmptyString3(body, "url");
10663
11214
  if (target === void 0) {
10664
11215
  await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-fetch carries one url" } });
10665
11216
  return;
@@ -10681,8 +11232,8 @@ data: ${JSON.stringify(envelope)}
10681
11232
  return;
10682
11233
  }
10683
11234
  if (workerTail === "web-search" && method === "POST") {
10684
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
10685
- const query = nonEmptyString2(body, "query");
11235
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11236
+ const query = nonEmptyString3(body, "query");
10686
11237
  if (query === void 0) {
10687
11238
  await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-search carries one query" } });
10688
11239
  return;
@@ -10693,7 +11244,7 @@ data: ${JSON.stringify(envelope)}
10693
11244
  await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "the domain filters of a worker web-search are string arrays" } });
10694
11245
  return;
10695
11246
  }
10696
- const searchProfile = nonEmptyString2(body, "search_profile");
11247
+ const searchProfile = nonEmptyString3(body, "search_profile");
10697
11248
  const backend = deps.webSearch;
10698
11249
  if (backend === void 0) {
10699
11250
  deps.log?.(`rc selfhost ${session.id}: worker web-search of "${query}" refused: no search backend serves this door`);
@@ -10718,7 +11269,7 @@ data: ${JSON.stringify(envelope)}
10718
11269
  return;
10719
11270
  }
10720
11271
  if (tail === "events" && method === "POST") {
10721
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11272
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
10722
11273
  const events = body?.events;
10723
11274
  if (!Array.isArray(events) || events.length === 0 || events.length > RC_SELF_HOST_MAX_BATCH_EVENTS) {
10724
11275
  await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: `a client event write is a non-empty array of at most ${String(RC_SELF_HOST_MAX_BATCH_EVENTS)} events` } });
@@ -10726,7 +11277,7 @@ data: ${JSON.stringify(envelope)}
10726
11277
  }
10727
11278
  const results = [];
10728
11279
  for (const event of events) {
10729
- if (!isRecord6(event) || !isRecord6(event.payload)) {
11280
+ if (!isRecord7(event) || !isRecord7(event.payload)) {
10730
11281
  await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "every event in a write is an object carrying one payload object" } });
10731
11282
  return;
10732
11283
  }
@@ -10738,26 +11289,14 @@ data: ${JSON.stringify(envelope)}
10738
11289
  return;
10739
11290
  }
10740
11291
  if (tail === "events" && method === "GET") {
10741
- await answerJson2(response, HTTP_STATUS.ok, { data: session.events.map((event) => ({ event_id: event.eventId, event_type: nonEmptyString2(event.payload, "type") ?? "event", sequence_num: event.sequenceNum, source: event.source, payload: event.payload, created_at: event.createdAt })) });
11292
+ await answerJson2(response, HTTP_STATUS.ok, { data: session.events.map((event) => ({ event_id: event.eventId, event_type: nonEmptyString3(event.payload, "type") ?? "event", sequence_num: event.sequenceNum, source: event.source, payload: event.payload, created_at: event.createdAt })) });
10742
11293
  return;
10743
11294
  }
10744
11295
  if (tail === "events/stream" && method === "GET") {
10745
11296
  await holdStream(session, "clientStreams", () => true, request4, response);
10746
11297
  return;
10747
11298
  }
10748
- if (tail === "client/presence" && method === "POST") {
10749
- await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
10750
- await answerJson2(response, HTTP_STATUS.ok, { refresh_after_seconds: RC_PRESENCE_REFRESH_SECONDS });
10751
- return;
10752
- }
10753
- if (tail === "mark_read" && method === "POST") {
10754
- await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
10755
- await answerJson2(response, HTTP_STATUS.ok, {});
10756
- return;
10757
- }
10758
- if (tail === "teleport-events" && method === "POST") {
10759
- await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
10760
- await answerJson2(response, HTTP_STATUS.ok, {});
11299
+ if (await serveRcClientChannels({ request: request4, response, session, answerJson: answerJson2, readBody: readBody2, publish, sweep, now: deps.now, presenceRefreshSeconds: RC_PRESENCE_REFRESH_SECONDS, ...deps.log === void 0 ? {} : { log: deps.log } }, tail, method, RC_SELF_HOST_MAX_BATCH_EVENTS)) {
10761
11300
  return;
10762
11301
  }
10763
11302
  await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no path "${tail}"` } });
@@ -10774,17 +11313,8 @@ data: ${JSON.stringify(envelope)}
10774
11313
  await noCredential(response);
10775
11314
  return;
10776
11315
  }
10777
- if (pathname === RC_COMPAT_SESSIONS_PATH) {
10778
- if (method !== "GET") {
10779
- await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
10780
- return;
10781
- }
10782
- const listRefusal = credentialRefusal(request4.headers);
10783
- if (listRefusal !== void 0) {
10784
- await unauthorized(response, listRefusal);
10785
- return;
10786
- }
10787
- await answerJson2(response, HTTP_STATUS.ok, { data: [] });
11316
+ if (pathname === RC_COMPAT_SESSIONS_PATH || pathname.startsWith(`${RC_COMPAT_SESSIONS_PATH}/`)) {
11317
+ await serveRcCompatSessions({ request: request4, response, sessions, answerJson: answerJson2, readBody: readBody2, credentialRefusal, unauthorized, now: deps.now, ...deps.log === void 0 ? {} : { log: deps.log } }, pathname, RC_COMPAT_SESSIONS_PATH);
10788
11318
  return;
10789
11319
  }
10790
11320
  if (pathname !== RC_SESSIONS_PATH_PREFIX2 && !pathname.startsWith(`${RC_SESSIONS_PATH_PREFIX2}/`)) {
@@ -10798,11 +11328,11 @@ data: ${JSON.stringify(envelope)}
10798
11328
  await unauthorized(response, createRefusal);
10799
11329
  return;
10800
11330
  }
10801
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11331
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
10802
11332
  const session = {
10803
11333
  id: `${RC_SESSION_ID_PREFIX2}${deps.newUuid()}`,
10804
11334
  createdAt: deps.now(),
10805
- title: nonEmptyString2(body, "title"),
11335
+ title: nonEmptyString3(body, "title"),
10806
11336
  workerJwts: /* @__PURE__ */ new Set(),
10807
11337
  sequenceNum: 0,
10808
11338
  events: [],
@@ -10810,6 +11340,7 @@ data: ${JSON.stringify(envelope)}
10810
11340
  workerStreams: /* @__PURE__ */ new Set(),
10811
11341
  clientStreams: /* @__PURE__ */ new Set(),
10812
11342
  internalEvents: [],
11343
+ clients: /* @__PURE__ */ new Map(),
10813
11344
  workerStatus: void 0,
10814
11345
  externalMetadata: void 0,
10815
11346
  lastTrafficAt: deps.now()
@@ -10854,7 +11385,7 @@ data: ${JSON.stringify(envelope)}
10854
11385
  const pathname = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
10855
11386
  if (host === RC_SELF_HOST_OAUTH_HOST) {
10856
11387
  if (method === "POST" && pathname === "/v1/oauth/token") {
10857
- const body = parseJsonObject2(await readBody2(request4, CONTROL_BODY_CAP_BYTES));
11388
+ const body = parseJsonObject3(await readBody2(request4, CONTROL_BODY_CAP_BYTES));
10858
11389
  const record = deps.credentialRecord();
10859
11390
  if (record === void 0) {
10860
11391
  answerServerJson(response, HTTP_SERVICE_UNAVAILABLE, { error: "the front door's self-hosted Remote Control surface is on but no credential has been minted for it" });
@@ -10940,7 +11471,7 @@ var RC_SELF_HOST_RECORD_DIR = "rc-selfhost";
10940
11471
  var RC_SELF_HOST_RECORD_FILE = "credential.json";
10941
11472
  var SELF_HOST_EMAIL = "selfhost@agent-shim.invalid";
10942
11473
  var SELF_HOST_SUBSCRIPTION_TYPE = "max";
10943
- function isRecord7(value) {
11474
+ function isRecord8(value) {
10944
11475
  return typeof value === "object" && value !== null && !Array.isArray(value);
10945
11476
  }
10946
11477
  function readJsonObject(fs12, file) {
@@ -10954,7 +11485,7 @@ function readJsonObject(fs12, file) {
10954
11485
  } catch {
10955
11486
  return void 0;
10956
11487
  }
10957
- return isRecord7(parsed) ? parsed : void 0;
11488
+ return isRecord8(parsed) ? parsed : void 0;
10958
11489
  }
10959
11490
  function readRcSelfHostRecord(fs12, frontdoorDir) {
10960
11491
  const parsed = readJsonObject(fs12, path28.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
@@ -10978,7 +11509,7 @@ function mintRcSelfHostCredential(deps) {
10978
11509
  const recordFile = path28.join(recordDir, RC_SELF_HOST_RECORD_FILE);
10979
11510
  const previousRecord = readRcSelfHostRecord(deps.fs, deps.frontdoorDir);
10980
11511
  const existingCredentials = readJsonObject(deps.fs, credentialsFile);
10981
- const existingOauth = isRecord7(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
11512
+ const existingOauth = isRecord8(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
10982
11513
  const existingToken = typeof existingOauth?.accessToken === "string" && existingOauth.accessToken !== "" ? existingOauth.accessToken : void 0;
10983
11514
  const existingIsOwn = existingToken !== void 0 && previousRecord?.accessToken === existingToken;
10984
11515
  if (existingToken !== void 0 && !existingIsOwn && !deps.force) {
@@ -10995,7 +11526,7 @@ function mintRcSelfHostCredential(deps) {
10995
11526
  deps.fs.writeFilePrivate(credentialsFile, `${JSON.stringify({ ...existingCredentials, claudeAiOauth: { accessToken, refreshToken, expiresAt: null, scopes: [...RC_SELF_HOST_SCOPE_LIST], subscriptionType: SELF_HOST_SUBSCRIPTION_TYPE, rateLimitTier: null } }, null, 2)}
10996
11527
  `);
10997
11528
  const claudeJson = readJsonObject(deps.fs, claudeJsonFile) ?? {};
10998
- const existingBlock = (value) => isRecord7(value) ? value : {};
11529
+ const existingBlock = (value) => isRecord8(value) ? value : {};
10999
11530
  deps.fs.writeFilePrivate(claudeJsonFile, `${JSON.stringify({
11000
11531
  ...claudeJson,
11001
11532
  oauthAccount: {
@@ -11194,7 +11725,7 @@ import path30 from "node:path";
11194
11725
  // package.json
11195
11726
  var package_default = {
11196
11727
  name: "agent-shim",
11197
- version: "8.23.0",
11728
+ version: "8.25.0",
11198
11729
  description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
11199
11730
  license: "MIT",
11200
11731
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -12436,10 +12967,12 @@ export {
12436
12967
  ProviderKindMismatchError,
12437
12968
  ProviderNotFoundError,
12438
12969
  ProviderSchema,
12970
+ RC_CONTEXT_USAGE_DETAILS,
12439
12971
  RC_IDLE_EXPIRY_MS,
12440
12972
  RC_ORPC_PATH_PREFIX,
12441
12973
  RC_PENDING_DEADLINE_MS,
12442
12974
  RC_PERMISSION_MODES,
12975
+ RC_READ_FILE_ENCODINGS,
12443
12976
  RC_REQUEST_PARSE_CAP_BYTES,
12444
12977
  RC_SELF_HOST_ENV,
12445
12978
  RC_SELF_HOST_KEEPALIVE_MS,
@@ -12470,11 +13003,24 @@ export {
12470
13003
  addPool,
12471
13004
  addProvider,
12472
13005
  answerRcControlRequest,
13006
+ authenticateRcSessionMcpServer,
12473
13007
  buildEntryFacts,
12474
13008
  buildLayoutPaths,
13009
+ buildRcControlRequestPayload,
12475
13010
  buildRcControlResponsePayload,
13011
+ buildRcEndSessionPayload,
12476
13012
  buildRcEventWriteBody,
13013
+ buildRcFileSuggestionsPayload,
13014
+ buildRcGetContextUsagePayload,
13015
+ buildRcGetUsagePayload,
12477
13016
  buildRcInterruptPayload,
13017
+ buildRcKeepAlivePayload,
13018
+ buildRcMarkReadBody,
13019
+ buildRcMcpAuthenticatePayload,
13020
+ buildRcMcpOAuthCallbackUrlPayload,
13021
+ buildRcMcpReconnectPayload,
13022
+ buildRcMcpStatusPayload,
13023
+ buildRcReadFilePayload,
12478
13024
  buildRcSetModelPayload,
12479
13025
  buildRcSetPermissionModePayload,
12480
13026
  buildRcUserMessagePayload,
@@ -12507,6 +13053,7 @@ export {
12507
13053
  doorApiAuth,
12508
13054
  doorApiNodeHandlerOf,
12509
13055
  effectiveWindow,
13056
+ endRcSession,
12510
13057
  ensureCa,
12511
13058
  ensureFrontDoor,
12512
13059
  ensureHeadroom,
@@ -12523,6 +13070,9 @@ export {
12523
13070
  frontDoorRcApiClient,
12524
13071
  frontDoorRcControl,
12525
13072
  generateCa,
13073
+ getRcSessionContextUsage,
13074
+ getRcSessionMcpStatus,
13075
+ getRcSessionUsage,
12526
13076
  headroomSocketTarget,
12527
13077
  identityExists,
12528
13078
  injectRcUserMessage,
@@ -12530,7 +13080,9 @@ export {
12530
13080
  isIdentityDirectoryName,
12531
13081
  isInterceptedHost,
12532
13082
  isPrivateAddress,
13083
+ isRcContextUsageDetail,
12533
13084
  isRcPermissionMode,
13085
+ isRcReadFileEncoding,
12534
13086
  lateRcEventDial,
12535
13087
  lateRcStreamDial,
12536
13088
  listDirectoryRules,
@@ -12564,6 +13116,7 @@ export {
12564
13116
  readProfile,
12565
13117
  readProvider,
12566
13118
  readRcSelfHostRecord,
13119
+ readRcSessionFile,
12567
13120
  readUsageSnapshot,
12568
13121
  realConnectCertStore,
12569
13122
  realConnectEffects,
@@ -12571,6 +13124,7 @@ export {
12571
13124
  realRcEventDial,
12572
13125
  realRcStreamDial,
12573
13126
  realRcWebFetch,
13127
+ reconnectRcSessionMcpServer,
12574
13128
  recoverFarm,
12575
13129
  recoveryDiagnostics,
12576
13130
  removeDirectoryRule,
@@ -12590,6 +13144,7 @@ export {
12590
13144
  runFrontDoorSupervisor,
12591
13145
  runLauncher,
12592
13146
  runSupervisor,
13147
+ sendRcKeepAlive,
12593
13148
  servedByPipeline,
12594
13149
  setAllowAmbientCredential,
12595
13150
  setDefaultConfigProfile,
@@ -12605,6 +13160,9 @@ export {
12605
13160
  snapshotPath,
12606
13161
  spawnClaude,
12607
13162
  startConnectServer,
13163
+ submitRcSessionMcpOAuthCallbackUrl,
13164
+ suggestRcSessionFiles,
13165
+ teleportRcSession,
12608
13166
  topLevelNames,
12609
13167
  updateDirectoryRule,
12610
13168
  updateProvider,