agent-shim 8.22.0 → 8.24.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.cjs CHANGED
@@ -93,10 +93,12 @@ __export(index_exports, {
93
93
  ProviderKindMismatchError: () => ProviderKindMismatchError,
94
94
  ProviderNotFoundError: () => ProviderNotFoundError,
95
95
  ProviderSchema: () => ProviderSchema,
96
+ RC_CONTEXT_USAGE_DETAILS: () => RC_CONTEXT_USAGE_DETAILS,
96
97
  RC_IDLE_EXPIRY_MS: () => RC_IDLE_EXPIRY_MS,
97
98
  RC_ORPC_PATH_PREFIX: () => RC_ORPC_PATH_PREFIX,
98
99
  RC_PENDING_DEADLINE_MS: () => RC_PENDING_DEADLINE_MS,
99
100
  RC_PERMISSION_MODES: () => RC_PERMISSION_MODES,
101
+ RC_READ_FILE_ENCODINGS: () => RC_READ_FILE_ENCODINGS,
100
102
  RC_REQUEST_PARSE_CAP_BYTES: () => RC_REQUEST_PARSE_CAP_BYTES,
101
103
  RC_SELF_HOST_ENV: () => RC_SELF_HOST_ENV,
102
104
  RC_SELF_HOST_KEEPALIVE_MS: () => RC_SELF_HOST_KEEPALIVE_MS,
@@ -127,11 +129,23 @@ __export(index_exports, {
127
129
  addPool: () => addPool,
128
130
  addProvider: () => addProvider,
129
131
  answerRcControlRequest: () => answerRcControlRequest,
132
+ authenticateRcSessionMcpServer: () => authenticateRcSessionMcpServer,
130
133
  buildEntryFacts: () => buildEntryFacts,
131
134
  buildLayoutPaths: () => buildLayoutPaths,
135
+ buildRcControlRequestPayload: () => buildRcControlRequestPayload,
132
136
  buildRcControlResponsePayload: () => buildRcControlResponsePayload,
137
+ buildRcEndSessionPayload: () => buildRcEndSessionPayload,
133
138
  buildRcEventWriteBody: () => buildRcEventWriteBody,
139
+ buildRcFileSuggestionsPayload: () => buildRcFileSuggestionsPayload,
140
+ buildRcGetContextUsagePayload: () => buildRcGetContextUsagePayload,
141
+ buildRcGetUsagePayload: () => buildRcGetUsagePayload,
134
142
  buildRcInterruptPayload: () => buildRcInterruptPayload,
143
+ buildRcKeepAlivePayload: () => buildRcKeepAlivePayload,
144
+ buildRcMcpAuthenticatePayload: () => buildRcMcpAuthenticatePayload,
145
+ buildRcMcpOAuthCallbackUrlPayload: () => buildRcMcpOAuthCallbackUrlPayload,
146
+ buildRcMcpReconnectPayload: () => buildRcMcpReconnectPayload,
147
+ buildRcMcpStatusPayload: () => buildRcMcpStatusPayload,
148
+ buildRcReadFilePayload: () => buildRcReadFilePayload,
135
149
  buildRcSetModelPayload: () => buildRcSetModelPayload,
136
150
  buildRcSetPermissionModePayload: () => buildRcSetPermissionModePayload,
137
151
  buildRcUserMessagePayload: () => buildRcUserMessagePayload,
@@ -164,6 +178,7 @@ __export(index_exports, {
164
178
  doorApiAuth: () => doorApiAuth,
165
179
  doorApiNodeHandlerOf: () => doorApiNodeHandlerOf,
166
180
  effectiveWindow: () => effectiveWindow,
181
+ endRcSession: () => endRcSession,
167
182
  ensureCa: () => ensureCa,
168
183
  ensureFrontDoor: () => ensureFrontDoor,
169
184
  ensureHeadroom: () => ensureHeadroom,
@@ -180,6 +195,9 @@ __export(index_exports, {
180
195
  frontDoorRcApiClient: () => frontDoorRcApiClient,
181
196
  frontDoorRcControl: () => frontDoorRcControl,
182
197
  generateCa: () => generateCa,
198
+ getRcSessionContextUsage: () => getRcSessionContextUsage,
199
+ getRcSessionMcpStatus: () => getRcSessionMcpStatus,
200
+ getRcSessionUsage: () => getRcSessionUsage,
183
201
  headroomSocketTarget: () => headroomSocketTarget,
184
202
  identityExists: () => identityExists,
185
203
  injectRcUserMessage: () => injectRcUserMessage,
@@ -187,7 +205,9 @@ __export(index_exports, {
187
205
  isIdentityDirectoryName: () => isIdentityDirectoryName,
188
206
  isInterceptedHost: () => isInterceptedHost,
189
207
  isPrivateAddress: () => isPrivateAddress,
208
+ isRcContextUsageDetail: () => isRcContextUsageDetail,
190
209
  isRcPermissionMode: () => isRcPermissionMode,
210
+ isRcReadFileEncoding: () => isRcReadFileEncoding,
191
211
  lateRcEventDial: () => lateRcEventDial,
192
212
  lateRcStreamDial: () => lateRcStreamDial,
193
213
  listDirectoryRules: () => listDirectoryRules,
@@ -221,6 +241,7 @@ __export(index_exports, {
221
241
  readProfile: () => readProfile,
222
242
  readProvider: () => readProvider,
223
243
  readRcSelfHostRecord: () => readRcSelfHostRecord,
244
+ readRcSessionFile: () => readRcSessionFile,
224
245
  readUsageSnapshot: () => readUsageSnapshot,
225
246
  realConnectCertStore: () => realConnectCertStore,
226
247
  realConnectEffects: () => realConnectEffects,
@@ -228,6 +249,7 @@ __export(index_exports, {
228
249
  realRcEventDial: () => realRcEventDial,
229
250
  realRcStreamDial: () => realRcStreamDial,
230
251
  realRcWebFetch: () => realRcWebFetch,
252
+ reconnectRcSessionMcpServer: () => reconnectRcSessionMcpServer,
231
253
  recoverFarm: () => recoverFarm,
232
254
  recoveryDiagnostics: () => recoveryDiagnostics,
233
255
  removeDirectoryRule: () => removeDirectoryRule,
@@ -247,6 +269,7 @@ __export(index_exports, {
247
269
  runFrontDoorSupervisor: () => runFrontDoorSupervisor,
248
270
  runLauncher: () => runLauncher,
249
271
  runSupervisor: () => runSupervisor,
272
+ sendRcKeepAlive: () => sendRcKeepAlive,
250
273
  servedByPipeline: () => servedByPipeline,
251
274
  setAllowAmbientCredential: () => setAllowAmbientCredential,
252
275
  setDefaultConfigProfile: () => setDefaultConfigProfile,
@@ -262,6 +285,8 @@ __export(index_exports, {
262
285
  snapshotPath: () => snapshotPath,
263
286
  spawnClaude: () => spawnClaude,
264
287
  startConnectServer: () => startConnectServer,
288
+ submitRcSessionMcpOAuthCallbackUrl: () => submitRcSessionMcpOAuthCallbackUrl,
289
+ suggestRcSessionFiles: () => suggestRcSessionFiles,
265
290
  topLevelNames: () => topLevelNames,
266
291
  updateDirectoryRule: () => updateDirectoryRule,
267
292
  updateProvider: () => updateProvider,
@@ -640,6 +665,11 @@ var FrontDoorGlobalConfigSchema = import_zod.z.strictObject({
640
665
  /** How long the front door may sit with no registered sessions before it closes and exits. Defaults to FRONTDOOR_DEFAULT_IDLE_SHUTDOWN_MINUTES. */
641
666
  idleShutdownMinutes: import_zod.z.number().int().positive().optional()
642
667
  });
668
+ var UPDATE_MODES = ["off", "notify", "auto"];
669
+ var UpdateGlobalConfigSchema = import_zod.z.strictObject({
670
+ /** What a launch does about a newer release of agent-shim itself; see `UPDATE_MODES`. Absent means `off`. */
671
+ mode: import_zod.z.enum(UPDATE_MODES).optional()
672
+ });
643
673
  var GlobalConfigSchema = import_zod.z.strictObject({
644
674
  $schema: import_zod.z.string().optional(),
645
675
  defaultConfigProfile: import_zod.z.string().min(1).optional(),
@@ -649,6 +679,7 @@ var GlobalConfigSchema = import_zod.z.strictObject({
649
679
  launch: LaunchSchema.optional(),
650
680
  headroom: HeadroomGlobalConfigSchema.optional(),
651
681
  frontdoor: FrontDoorGlobalConfigSchema.optional(),
682
+ update: UpdateGlobalConfigSchema.optional(),
652
683
  pools: import_zod.z.record(PoolNameSchema, PoolSchema).optional()
653
684
  });
654
685
  var IdentitySchema = import_zod.z.strictObject({
@@ -3896,6 +3927,8 @@ function rcObserverAsPassthrough(observer) {
3896
3927
  }
3897
3928
  };
3898
3929
  }
3930
+
3931
+ // src/frontdoor/rcWrites.ts
3899
3932
  function buildRcUserMessagePayload(uuid, sessionId, text) {
3900
3933
  return {
3901
3934
  type: "user",
@@ -3996,14 +4029,55 @@ var RC_PERMISSION_MODES = ["default", "acceptEdits", "bypassPermissions", "plan"
3996
4029
  function isRcPermissionMode(value) {
3997
4030
  return RC_PERMISSION_MODES.some((mode) => mode === value);
3998
4031
  }
4032
+ function buildRcControlRequestPayload(requestId, request4) {
4033
+ return { type: "control_request", request_id: requestId, request: request4 };
4034
+ }
3999
4035
  function buildRcInterruptPayload(requestId) {
4000
- return { type: "control_request", request_id: requestId, request: { subtype: "interrupt" } };
4036
+ return buildRcControlRequestPayload(requestId, { subtype: "interrupt" });
4001
4037
  }
4002
4038
  function buildRcSetModelPayload(requestId, model) {
4003
- return { type: "control_request", request_id: requestId, request: { subtype: "set_model", model } };
4039
+ return buildRcControlRequestPayload(requestId, { subtype: "set_model", model });
4004
4040
  }
4005
4041
  function buildRcSetPermissionModePayload(requestId, mode) {
4006
- return { type: "control_request", request_id: requestId, request: { subtype: "set_permission_mode", mode } };
4042
+ return buildRcControlRequestPayload(requestId, { subtype: "set_permission_mode", mode });
4043
+ }
4044
+ function buildRcEndSessionPayload(requestId, reason) {
4045
+ return buildRcControlRequestPayload(requestId, { subtype: "end_session", ...reason === void 0 ? {} : { reason } });
4046
+ }
4047
+ function buildRcGetUsagePayload(requestId, skipBehaviors) {
4048
+ return buildRcControlRequestPayload(requestId, { subtype: "get_usage", ...skipBehaviors === true ? { skip_behaviors: true } : {} });
4049
+ }
4050
+ var RC_CONTEXT_USAGE_DETAILS = ["summary", "full"];
4051
+ function isRcContextUsageDetail(value) {
4052
+ return RC_CONTEXT_USAGE_DETAILS.some((detail) => detail === value);
4053
+ }
4054
+ function buildRcGetContextUsagePayload(requestId, detail) {
4055
+ return buildRcControlRequestPayload(requestId, { subtype: "get_context_usage", ...detail === void 0 ? {} : { detail } });
4056
+ }
4057
+ var RC_READ_FILE_ENCODINGS = ["utf-8", "base64"];
4058
+ function isRcReadFileEncoding(value) {
4059
+ return RC_READ_FILE_ENCODINGS.some((encoding) => encoding === value);
4060
+ }
4061
+ function buildRcReadFilePayload(requestId, path32, options) {
4062
+ return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path32, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
4063
+ }
4064
+ function buildRcFileSuggestionsPayload(requestId, query) {
4065
+ return buildRcControlRequestPayload(requestId, { subtype: "file_suggestions", query });
4066
+ }
4067
+ function buildRcMcpStatusPayload(requestId) {
4068
+ return buildRcControlRequestPayload(requestId, { subtype: "mcp_status" });
4069
+ }
4070
+ function buildRcMcpReconnectPayload(requestId, serverName) {
4071
+ return buildRcControlRequestPayload(requestId, { subtype: "mcp_reconnect", serverName });
4072
+ }
4073
+ function buildRcMcpAuthenticatePayload(requestId, serverName, redirectUri) {
4074
+ return buildRcControlRequestPayload(requestId, { subtype: "mcp_authenticate", serverName, redirectUri });
4075
+ }
4076
+ function buildRcMcpOAuthCallbackUrlPayload(requestId, serverName, callbackUrl) {
4077
+ return buildRcControlRequestPayload(requestId, { subtype: "mcp_oauth_callback_url", serverName, callbackUrl });
4078
+ }
4079
+ function buildRcKeepAlivePayload() {
4080
+ return { type: "keep_alive" };
4007
4081
  }
4008
4082
  async function answerRcControlRequest(deps, sessionId, requestId, decision) {
4009
4083
  const credential = deps.credentialOf(sessionId);
@@ -4030,7 +4104,7 @@ async function answerRcControlRequest(deps, sessionId, requestId, decision) {
4030
4104
  }
4031
4105
  return result;
4032
4106
  }
4033
- async function interruptRcSession(deps, sessionId) {
4107
+ async function deliverRcClientPayload(deps, sessionId, payload, action) {
4034
4108
  const credential = deps.credentialOf(sessionId);
4035
4109
  if (credential === void 0) {
4036
4110
  return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
@@ -4038,12 +4112,12 @@ async function interruptRcSession(deps, sessionId) {
4038
4112
  if (credential.authorization === void 0) {
4039
4113
  return noOAuthCredentialResult(sessionId);
4040
4114
  }
4041
- const body = JSON.stringify(buildRcEventWriteBody(buildRcInterruptPayload(deps.newUuid())));
4115
+ const body = JSON.stringify(buildRcEventWriteBody(payload));
4042
4116
  let answer;
4043
4117
  try {
4044
4118
  answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
4045
4119
  } catch (error) {
4046
- return { ok: false, message: `the door could not reach the API host to interrupt session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
4120
+ return { ok: false, message: `the door could not reach the API host to ${action}: ${error instanceof Error ? error.message : String(error)}` };
4047
4121
  }
4048
4122
  const result = rcEventWriteResultFromAnswer(answer);
4049
4123
  if (result.ok) {
@@ -4051,50 +4125,89 @@ async function interruptRcSession(deps, sessionId) {
4051
4125
  }
4052
4126
  return result;
4053
4127
  }
4128
+ async function interruptRcSession(deps, sessionId) {
4129
+ const requestId = deps.newUuid();
4130
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcInterruptPayload(requestId), `interrupt session ${sessionId}`);
4131
+ return result.ok ? { ...result, requestId } : result;
4132
+ }
4054
4133
  async function setRcSessionModel(deps, sessionId, model) {
4055
4134
  if (model === "") {
4056
4135
  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" };
4057
4136
  }
4058
- const credential = deps.credentialOf(sessionId);
4059
- if (credential === void 0) {
4060
- return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
4061
- }
4062
- if (credential.authorization === void 0) {
4063
- return noOAuthCredentialResult(sessionId);
4137
+ const requestId = deps.newUuid();
4138
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcSetModelPayload(requestId, model), `set the model on session ${sessionId}`);
4139
+ return result.ok ? { ...result, requestId } : result;
4140
+ }
4141
+ async function setRcSessionPermissionMode(deps, sessionId, mode) {
4142
+ const requestId = deps.newUuid();
4143
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcSetPermissionModePayload(requestId, mode), `set the permission mode on session ${sessionId}`);
4144
+ return result.ok ? { ...result, requestId } : result;
4145
+ }
4146
+ async function endRcSession(deps, sessionId, reason) {
4147
+ if (reason === "") {
4148
+ 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" };
4064
4149
  }
4065
- const body = JSON.stringify(buildRcEventWriteBody(buildRcSetModelPayload(deps.newUuid(), model)));
4066
- let answer;
4067
- try {
4068
- answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
4069
- } catch (error) {
4070
- 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)}` };
4150
+ const requestId = deps.newUuid();
4151
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcEndSessionPayload(requestId, reason), `end session ${sessionId}`);
4152
+ return result.ok ? { ...result, requestId } : result;
4153
+ }
4154
+ async function getRcSessionUsage(deps, sessionId, skipBehaviors) {
4155
+ const requestId = deps.newUuid();
4156
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcGetUsagePayload(requestId, skipBehaviors), `request the usage of session ${sessionId}`);
4157
+ return result.ok ? { ...result, requestId } : result;
4158
+ }
4159
+ async function getRcSessionContextUsage(deps, sessionId, detail) {
4160
+ const requestId = deps.newUuid();
4161
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcGetContextUsagePayload(requestId, detail), `request the context usage of session ${sessionId}`);
4162
+ return result.ok ? { ...result, requestId } : result;
4163
+ }
4164
+ async function readRcSessionFile(deps, sessionId, path32, options) {
4165
+ if (path32 === "") {
4166
+ 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" };
4071
4167
  }
4072
- const result = rcEventWriteResultFromAnswer(answer);
4073
- if (result.ok) {
4074
- deps.noteSequenceNums(sessionId, result.sequenceNums);
4168
+ if (options?.maxBytes !== void 0 && (!Number.isInteger(options.maxBytes) || options.maxBytes <= 0)) {
4169
+ 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" };
4075
4170
  }
4076
- return result;
4171
+ const requestId = deps.newUuid();
4172
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path32, options), `read ${path32} from session ${sessionId}`);
4173
+ return result.ok ? { ...result, requestId } : result;
4077
4174
  }
4078
- async function setRcSessionPermissionMode(deps, sessionId, mode) {
4079
- const credential = deps.credentialOf(sessionId);
4080
- if (credential === void 0) {
4081
- return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
4082
- }
4083
- if (credential.authorization === void 0) {
4084
- return noOAuthCredentialResult(sessionId);
4175
+ async function suggestRcSessionFiles(deps, sessionId, query) {
4176
+ const requestId = deps.newUuid();
4177
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcFileSuggestionsPayload(requestId, query), `request file suggestions from session ${sessionId}`);
4178
+ return result.ok ? { ...result, requestId } : result;
4179
+ }
4180
+ async function getRcSessionMcpStatus(deps, sessionId) {
4181
+ const requestId = deps.newUuid();
4182
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpStatusPayload(requestId), `request the MCP status of session ${sessionId}`);
4183
+ return result.ok ? { ...result, requestId } : result;
4184
+ }
4185
+ async function reconnectRcSessionMcpServer(deps, sessionId, serverName) {
4186
+ if (serverName === "") {
4187
+ 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" };
4085
4188
  }
4086
- const body = JSON.stringify(buildRcEventWriteBody(buildRcSetPermissionModePayload(deps.newUuid(), mode)));
4087
- let answer;
4088
- try {
4089
- answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
4090
- } catch (error) {
4091
- 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)}` };
4189
+ const requestId = deps.newUuid();
4190
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpReconnectPayload(requestId, serverName), `reconnect MCP server ${serverName} on session ${sessionId}`);
4191
+ return result.ok ? { ...result, requestId } : result;
4192
+ }
4193
+ async function authenticateRcSessionMcpServer(deps, sessionId, serverName, redirectUri) {
4194
+ if (serverName === "" || redirectUri === "") {
4195
+ 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" };
4092
4196
  }
4093
- const result = rcEventWriteResultFromAnswer(answer);
4094
- if (result.ok) {
4095
- deps.noteSequenceNums(sessionId, result.sequenceNums);
4197
+ const requestId = deps.newUuid();
4198
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpAuthenticatePayload(requestId, serverName, redirectUri), `authenticate MCP server ${serverName} on session ${sessionId}`);
4199
+ return result.ok ? { ...result, requestId } : result;
4200
+ }
4201
+ async function submitRcSessionMcpOAuthCallbackUrl(deps, sessionId, serverName, callbackUrl) {
4202
+ if (serverName === "" || callbackUrl === "") {
4203
+ 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" };
4096
4204
  }
4097
- return result;
4205
+ const requestId = deps.newUuid();
4206
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcMcpOAuthCallbackUrlPayload(requestId, serverName, callbackUrl), `hand MCP server ${serverName}'s OAuth callback to session ${sessionId}`);
4207
+ return result.ok ? { ...result, requestId } : result;
4208
+ }
4209
+ async function sendRcKeepAlive(deps, sessionId) {
4210
+ return await deliverRcClientPayload(deps, sessionId, buildRcKeepAlivePayload(), `keep session ${sessionId} alive`);
4098
4211
  }
4099
4212
 
4100
4213
  // src/frontdoor/rcControl.ts
@@ -4106,6 +4219,25 @@ function answerJson(response, status, body) {
4106
4219
  response.writeHead(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")) });
4107
4220
  response.end(text);
4108
4221
  }
4222
+ function isJsonObject(value) {
4223
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4224
+ }
4225
+ function bodyString(body, field) {
4226
+ const value = body[field];
4227
+ return typeof value === "string" && value !== "" ? value : void 0;
4228
+ }
4229
+ function bodyStringAllowingEmpty(body, field) {
4230
+ const value = body[field];
4231
+ return typeof value === "string" ? value : void 0;
4232
+ }
4233
+ function bodyBoolean(body, field) {
4234
+ const value = body[field];
4235
+ return typeof value === "boolean" ? value : void 0;
4236
+ }
4237
+ function bodyPositiveInteger(body, field) {
4238
+ const value = body[field];
4239
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
4240
+ }
4109
4241
  async function readBody(request4) {
4110
4242
  return await new Promise((resolve, reject) => {
4111
4243
  const chunks = [];
@@ -4133,9 +4265,127 @@ function createRcControlHandler(deps) {
4133
4265
  const pendingPath = `${CONTROL_PATH_PREFIX}/pending`;
4134
4266
  const injectPath = `${CONTROL_PATH_PREFIX}/inject`;
4135
4267
  const answerPath = `${CONTROL_PATH_PREFIX}/answer`;
4136
- const interruptPath = `${CONTROL_PATH_PREFIX}/interrupt`;
4137
- const setModelPath = `${CONTROL_PATH_PREFIX}/set-model`;
4138
- const setPermissionModePath = `${CONTROL_PATH_PREFIX}/set-permission-mode`;
4268
+ const controlWrites = [
4269
+ {
4270
+ name: "interrupt",
4271
+ path: "interrupt",
4272
+ read: (body) => {
4273
+ const session = bodyString(body, "session");
4274
+ return session === void 0 ? { error: "the interrupt body must be JSON naming a non-empty session id" } : { session, run: async () => await deps.interrupt(session) };
4275
+ }
4276
+ },
4277
+ {
4278
+ name: "set-model",
4279
+ path: "set-model",
4280
+ read: (body) => {
4281
+ const session = bodyString(body, "session");
4282
+ const model = bodyString(body, "model");
4283
+ 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) };
4284
+ }
4285
+ },
4286
+ {
4287
+ name: "set-permission-mode",
4288
+ path: "set-permission-mode",
4289
+ read: (body) => {
4290
+ const session = bodyString(body, "session");
4291
+ const mode = body.mode;
4292
+ 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) };
4293
+ }
4294
+ },
4295
+ {
4296
+ name: "end-session",
4297
+ path: "end-session",
4298
+ read: (body) => {
4299
+ const session = bodyString(body, "session");
4300
+ const reason = "reason" in body ? bodyString(body, "reason") : void 0;
4301
+ 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) };
4302
+ }
4303
+ },
4304
+ {
4305
+ name: "get-usage",
4306
+ path: "get-usage",
4307
+ read: (body) => {
4308
+ const session = bodyString(body, "session");
4309
+ const skipBehaviors = "skipBehaviors" in body ? bodyBoolean(body, "skipBehaviors") : void 0;
4310
+ 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) };
4311
+ }
4312
+ },
4313
+ {
4314
+ name: "get-context-usage",
4315
+ path: "get-context-usage",
4316
+ read: (body) => {
4317
+ const session = bodyString(body, "session");
4318
+ const detail = "detail" in body && isRcContextUsageDetail(body.detail) ? body.detail : void 0;
4319
+ 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) };
4320
+ }
4321
+ },
4322
+ {
4323
+ name: "read-file",
4324
+ path: "read-file",
4325
+ read: (body) => {
4326
+ const session = bodyString(body, "session");
4327
+ const path32 = bodyString(body, "path");
4328
+ const maxBytes = "maxBytes" in body ? bodyPositiveInteger(body, "maxBytes") : void 0;
4329
+ const encoding = "encoding" in body && isRcReadFileEncoding(body.encoding) ? body.encoding : void 0;
4330
+ const refused2 = session === void 0 || path32 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
4331
+ 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 } }) };
4332
+ }
4333
+ },
4334
+ {
4335
+ name: "file-suggestions",
4336
+ path: "file-suggestions",
4337
+ read: (body) => {
4338
+ const session = bodyString(body, "session");
4339
+ const query = bodyStringAllowingEmpty(body, "query");
4340
+ 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) };
4341
+ }
4342
+ },
4343
+ {
4344
+ name: "keep-alive",
4345
+ path: "keep-alive",
4346
+ read: (body) => {
4347
+ const session = bodyString(body, "session");
4348
+ 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) };
4349
+ }
4350
+ },
4351
+ {
4352
+ name: "mcp-status",
4353
+ path: "mcp-status",
4354
+ read: (body) => {
4355
+ const session = bodyString(body, "session");
4356
+ 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) };
4357
+ }
4358
+ },
4359
+ {
4360
+ name: "mcp-reconnect",
4361
+ path: "mcp-reconnect",
4362
+ read: (body) => {
4363
+ const session = bodyString(body, "session");
4364
+ const serverName = bodyString(body, "serverName");
4365
+ 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) };
4366
+ }
4367
+ },
4368
+ {
4369
+ name: "mcp-authenticate",
4370
+ path: "mcp-authenticate",
4371
+ read: (body) => {
4372
+ const session = bodyString(body, "session");
4373
+ const serverName = bodyString(body, "serverName");
4374
+ const redirectUri = bodyString(body, "redirectUri");
4375
+ 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) };
4376
+ }
4377
+ },
4378
+ {
4379
+ name: "mcp-oauth-callback-url",
4380
+ path: "mcp-oauth-callback-url",
4381
+ read: (body) => {
4382
+ const session = bodyString(body, "session");
4383
+ const serverName = bodyString(body, "serverName");
4384
+ const callbackUrl = bodyString(body, "callbackUrl");
4385
+ 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) };
4386
+ }
4387
+ }
4388
+ ];
4139
4389
  return (request4, response) => {
4140
4390
  const presented = request4.headers.authorization;
4141
4391
  const bearerPrefix = "bearer ".length;
@@ -4220,73 +4470,33 @@ function createRcControlHandler(deps) {
4220
4470
  answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
4221
4471
  return;
4222
4472
  }
4223
- if (url.pathname === interruptPath) {
4224
- if (request4.method !== "POST") {
4225
- answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the interrupt operation is a POST" });
4226
- return;
4227
- }
4228
- let parsed;
4229
- try {
4230
- parsed = JSON.parse(await readBody(request4));
4231
- } catch {
4232
- answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body is not readable JSON within the control body cap" });
4233
- return;
4234
- }
4235
- if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || typeof parsed.session !== "string" || parsed.session === "") {
4236
- answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body must be JSON naming a non-empty session id" });
4237
- return;
4238
- }
4239
- const result = await deps.interrupt(parsed.session);
4240
- if (result.ok) {
4241
- answerJson(response, HTTP_STATUS.ok, { session: parsed.session, sequenceNums: result.sequenceNums });
4242
- return;
4473
+ for (const route of controlWrites) {
4474
+ if (url.pathname !== `${CONTROL_PATH_PREFIX}/${route.path}`) {
4475
+ continue;
4243
4476
  }
4244
- answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
4245
- return;
4246
- }
4247
- if (url.pathname === setModelPath) {
4248
4477
  if (request4.method !== "POST") {
4249
- answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-model operation is a POST" });
4478
+ answerJson(response, HTTP_STATUS.methodNotAllowed, { error: `the ${route.name} operation is a POST` });
4250
4479
  return;
4251
4480
  }
4252
4481
  let parsed;
4253
4482
  try {
4254
4483
  parsed = JSON.parse(await readBody(request4));
4255
4484
  } catch {
4256
- answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body is not readable JSON within the control body cap" });
4257
- return;
4258
- }
4259
- if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("model" in parsed) || typeof parsed.session !== "string" || typeof parsed.model !== "string" || parsed.session === "" || parsed.model === "") {
4260
- answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body must be JSON naming a non-empty session id and model id" });
4485
+ answerJson(response, HTTP_STATUS.badRequest, { error: `the ${route.name} body is not readable JSON within the control body cap` });
4261
4486
  return;
4262
4487
  }
4263
- const result = await deps.setModel(parsed.session, parsed.model);
4264
- if (result.ok) {
4265
- answerJson(response, HTTP_STATUS.ok, { session: parsed.session, model: parsed.model, sequenceNums: result.sequenceNums });
4266
- return;
4267
- }
4268
- answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
4269
- return;
4270
- }
4271
- if (url.pathname === setPermissionModePath) {
4272
- if (request4.method !== "POST") {
4273
- answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-permission-mode operation is a POST" });
4488
+ if (!isJsonObject(parsed)) {
4489
+ answerJson(response, HTTP_STATUS.badRequest, { error: `the ${route.name} body must be a JSON object` });
4274
4490
  return;
4275
4491
  }
4276
- let parsed;
4277
- try {
4278
- parsed = JSON.parse(await readBody(request4));
4279
- } catch {
4280
- answerJson(response, HTTP_STATUS.badRequest, { error: "the set-permission-mode body is not readable JSON within the control body cap" });
4492
+ const read = route.read(parsed);
4493
+ if ("error" in read) {
4494
+ answerJson(response, HTTP_STATUS.badRequest, { error: read.error });
4281
4495
  return;
4282
4496
  }
4283
- if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("mode" in parsed) || typeof parsed.session !== "string" || parsed.session === "" || !isRcPermissionMode(parsed.mode)) {
4284
- 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})` });
4285
- return;
4286
- }
4287
- const result = await deps.setPermissionMode(parsed.session, parsed.mode);
4497
+ const result = await read.run();
4288
4498
  if (result.ok) {
4289
- answerJson(response, HTTP_STATUS.ok, { session: parsed.session, mode: parsed.mode, sequenceNums: result.sequenceNums });
4499
+ answerJson(response, HTTP_STATUS.ok, { session: read.session, ...result.requestId === void 0 ? {} : { request: result.requestId }, sequenceNums: result.sequenceNums });
4290
4500
  return;
4291
4501
  }
4292
4502
  answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
@@ -4404,6 +4614,19 @@ function sequenceNumsOfBody(body) {
4404
4614
  }
4405
4615
  return sequenceNums;
4406
4616
  }
4617
+ function requestIdOfBody(body) {
4618
+ let parsed;
4619
+ try {
4620
+ parsed = JSON.parse(body);
4621
+ } catch {
4622
+ return void 0;
4623
+ }
4624
+ if (typeof parsed !== "object" || parsed === null || !("request" in parsed)) {
4625
+ return void 0;
4626
+ }
4627
+ const value = parsed.request;
4628
+ return typeof value === "string" && value !== "" ? value : void 0;
4629
+ }
4407
4630
  function frontDoorRcControl(transport, token) {
4408
4631
  const headers = { authorization: `Bearer ${token}` };
4409
4632
  const listAnswerOf = async (path32, what) => {
@@ -4492,7 +4715,8 @@ function frontDoorRcControl(transport, token) {
4492
4715
  if (sequenceNums === void 0) {
4493
4716
  return { ok: false, message: `the door accepted the event but its answer named no sequence numbers: ${answer.body}` };
4494
4717
  }
4495
- return { ok: true, sequenceNums };
4718
+ const requestId = requestIdOfBody(answer.body);
4719
+ return requestId === void 0 ? { ok: true, sequenceNums } : { ok: true, sequenceNums, requestId };
4496
4720
  }
4497
4721
  return { ok: false, message: describeFailure(answer.status, controlErrorOf(answer.body) ?? answer.body) };
4498
4722
  };
@@ -4509,7 +4733,21 @@ function frontDoorRcControl(transport, token) {
4509
4733
  ),
4510
4734
  interruptSession: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/interrupt`, JSON.stringify({ session: sessionId }), (status, message) => `interrupting ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4511
4735
  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}`),
4512
- 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}`)
4736
+ 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}`),
4737
+ 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}`),
4738
+ 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}`),
4739
+ 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}`),
4740
+ readFile: async (sessionId, path32, options) => await writeOf(
4741
+ `${CONTROL_PATH_PREFIX}/read-file`,
4742
+ JSON.stringify({ session: sessionId, path: path32, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
4743
+ (status, message) => `reading ${path32} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
4744
+ ),
4745
+ 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}`),
4746
+ keepAlive: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/keep-alive`, JSON.stringify({ session: sessionId }), (status, message) => `keeping ${sessionId} alive failed (HTTP ${String(status)}): ${message}`),
4747
+ 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}`),
4748
+ 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}`),
4749
+ 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}`),
4750
+ 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}`)
4513
4751
  };
4514
4752
  }
4515
4753
 
@@ -5131,6 +5369,48 @@ var RcSetPermissionModeInputSchema = import_zod4.z.strictObject({
5131
5369
  session: import_zod4.z.string().min(1),
5132
5370
  mode: RcPermissionModeSchema
5133
5371
  });
5372
+ var RcEndSessionInputSchema = import_zod4.z.strictObject({
5373
+ session: import_zod4.z.string().min(1),
5374
+ reason: import_zod4.z.string().min(1).optional()
5375
+ });
5376
+ var RcGetUsageInputSchema = import_zod4.z.strictObject({
5377
+ session: import_zod4.z.string().min(1),
5378
+ skipBehaviors: import_zod4.z.boolean().optional()
5379
+ });
5380
+ var RcGetContextUsageInputSchema = import_zod4.z.strictObject({
5381
+ session: import_zod4.z.string().min(1),
5382
+ detail: import_zod4.z.enum(RC_CONTEXT_USAGE_DETAILS).optional()
5383
+ });
5384
+ var RcReadFileInputSchema = import_zod4.z.strictObject({
5385
+ session: import_zod4.z.string().min(1),
5386
+ path: import_zod4.z.string().min(1),
5387
+ maxBytes: import_zod4.z.number().int().positive().optional(),
5388
+ encoding: import_zod4.z.enum(RC_READ_FILE_ENCODINGS).optional()
5389
+ });
5390
+ var RcFileSuggestionsInputSchema = import_zod4.z.strictObject({
5391
+ session: import_zod4.z.string().min(1),
5392
+ query: import_zod4.z.string()
5393
+ });
5394
+ var RcKeepAliveInputSchema = import_zod4.z.strictObject({
5395
+ session: import_zod4.z.string().min(1)
5396
+ });
5397
+ var RcMcpStatusInputSchema = import_zod4.z.strictObject({
5398
+ session: import_zod4.z.string().min(1)
5399
+ });
5400
+ var RcMcpReconnectInputSchema = import_zod4.z.strictObject({
5401
+ session: import_zod4.z.string().min(1),
5402
+ serverName: import_zod4.z.string().min(1)
5403
+ });
5404
+ var RcMcpAuthenticateInputSchema = import_zod4.z.strictObject({
5405
+ session: import_zod4.z.string().min(1),
5406
+ serverName: import_zod4.z.string().min(1),
5407
+ redirectUri: import_zod4.z.string().min(1)
5408
+ });
5409
+ var RcMcpOAuthCallbackUrlInputSchema = import_zod4.z.strictObject({
5410
+ session: import_zod4.z.string().min(1),
5411
+ serverName: import_zod4.z.string().min(1),
5412
+ callbackUrl: import_zod4.z.string().min(1)
5413
+ });
5134
5414
  var RcListOutputSchema = import_zod4.z.strictObject({ sessions: import_zod4.z.readonly(import_zod4.z.array(RcSessionSummarySchema)) });
5135
5415
  var RcStatusOutputSchema = import_zod4.z.strictObject({ statuses: import_zod4.z.readonly(import_zod4.z.array(RcSessionStatusSchema)) });
5136
5416
  var RcPendingOutputSchema = import_zod4.z.strictObject({ pending: import_zod4.z.readonly(import_zod4.z.array(RcPendingRequestSummarySchema)) });
@@ -5138,6 +5418,11 @@ var RcWriteOutputSchema = import_zod4.z.strictObject({
5138
5418
  session: import_zod4.z.string(),
5139
5419
  sequenceNums: import_zod4.z.readonly(import_zod4.z.array(import_zod4.z.number()))
5140
5420
  });
5421
+ var RcControlWriteOutputSchema = import_zod4.z.strictObject({
5422
+ session: import_zod4.z.string(),
5423
+ request: import_zod4.z.string(),
5424
+ sequenceNums: import_zod4.z.readonly(import_zod4.z.array(import_zod4.z.number()))
5425
+ });
5141
5426
  var RcAnswerOutputSchema = import_zod4.z.strictObject({
5142
5427
  session: import_zod4.z.string(),
5143
5428
  request: import_zod4.z.string(),
@@ -5166,6 +5451,15 @@ function createRcApiRouter(deps) {
5166
5451
  }
5167
5452
  return result.sequenceNums;
5168
5453
  };
5454
+ const deliveredControl = (sessionId, result) => {
5455
+ if (!result.ok) {
5456
+ throw new import_client.ORPCError("BAD_GATEWAY", { message: result.message });
5457
+ }
5458
+ if (result.requestId === void 0) {
5459
+ throw new import_client.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" });
5460
+ }
5461
+ return { request: result.requestId, sequenceNums: result.sequenceNums };
5462
+ };
5169
5463
  return {
5170
5464
  rc: {
5171
5465
  list: authed.output(RcListOutputSchema).handler(() => ({ sessions: deps.list() })),
@@ -5188,9 +5482,19 @@ function createRcApiRouter(deps) {
5188
5482
  const decision = { approve: input.approve, message: input.approve || input.text === void 0 ? void 0 : input.text };
5189
5483
  return { session: input.session, request: input.request, sequenceNums: delivered(input.session, await deps.answer(input.session, input.request, decision)) };
5190
5484
  }),
5191
- interrupt: authed.input(RcInterruptInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.interrupt(input.session)) })),
5192
- setModel: authed.input(RcSetModelInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.setModel(input.session, input.model)) })),
5193
- setPermissionMode: authed.input(RcSetPermissionModeInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.setPermissionMode(input.session, input.mode)) })),
5485
+ interrupt: authed.input(RcInterruptInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.interrupt(input.session)) })),
5486
+ setModel: authed.input(RcSetModelInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.setModel(input.session, input.model)) })),
5487
+ setPermissionMode: authed.input(RcSetPermissionModeInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.setPermissionMode(input.session, input.mode)) })),
5488
+ endSession: authed.input(RcEndSessionInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.endSession(input.session, input.reason)) })),
5489
+ getUsage: authed.input(RcGetUsageInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.getUsage(input.session, input.skipBehaviors)) })),
5490
+ getContextUsage: authed.input(RcGetContextUsageInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.getContextUsage(input.session, input.detail)) })),
5491
+ 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 } })) })),
5492
+ fileSuggestions: authed.input(RcFileSuggestionsInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.fileSuggestions(input.session, input.query)) })),
5493
+ keepAlive: authed.input(RcKeepAliveInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.keepAlive(input.session)) })),
5494
+ mcpStatus: authed.input(RcMcpStatusInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpStatus(input.session)) })),
5495
+ mcpReconnect: authed.input(RcMcpReconnectInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpReconnect(input.session, input.serverName)) })),
5496
+ mcpAuthenticate: authed.input(RcMcpAuthenticateInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpAuthenticate(input.session, input.serverName, input.redirectUri)) })),
5497
+ mcpOAuthCallbackUrl: authed.input(RcMcpOAuthCallbackUrlInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpOAuthCallbackUrl(input.session, input.serverName, input.callbackUrl)) })),
5194
5498
  subscribe: authed.input(RcSessionQuerySchema).output((0, import_server2.eventIterator)(RcStreamEventSchema)).handler(async function* ({ input, signal }) {
5195
5499
  const queue = [];
5196
5500
  let wake;
@@ -11457,6 +11761,106 @@ function removeDirectoryRule(paths, rulePath) {
11457
11761
  var import_node_crypto10 = require("node:crypto");
11458
11762
  var import_node_path30 = __toESM(require("node:path"), 1);
11459
11763
 
11764
+ // package.json
11765
+ var package_default = {
11766
+ name: "agent-shim",
11767
+ version: "8.24.0",
11768
+ 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.",
11769
+ license: "MIT",
11770
+ author: "Joseph Mearman <joseph@mearman.co.uk>",
11771
+ repository: {
11772
+ type: "git",
11773
+ url: "git+https://github.com/ExaDev/agent-shim.git"
11774
+ },
11775
+ homepage: "https://github.com/ExaDev/agent-shim#readme",
11776
+ bugs: "https://github.com/ExaDev/agent-shim/issues",
11777
+ keywords: [
11778
+ "claude-code",
11779
+ "claude",
11780
+ "cli"
11781
+ ],
11782
+ packageManager: "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c",
11783
+ type: "module",
11784
+ engines: {
11785
+ node: "^22.18 || >=24"
11786
+ },
11787
+ bin: {
11788
+ "agent-shim": "dist/cli.cjs",
11789
+ "claude-use": "dist/cli.cjs"
11790
+ },
11791
+ files: [
11792
+ "dist/cli.cjs",
11793
+ "dist/index.mjs",
11794
+ "dist/index.cjs",
11795
+ "dist/types"
11796
+ ],
11797
+ scripts: {
11798
+ prepare: "husky && node scripts/build.mts --bundle-only",
11799
+ typecheck: "turbo run _typecheck",
11800
+ _typecheck: "tsc --noEmit",
11801
+ lint: "turbo run _lint",
11802
+ _lint: "eslint . --cache --cache-location .eslintcache --max-warnings 0",
11803
+ test: "turbo run _test",
11804
+ _test: "vitest run",
11805
+ "test:coverage": "turbo run _test:coverage",
11806
+ "_test:coverage": "vitest run --coverage",
11807
+ knip: "turbo run _knip",
11808
+ _knip: "knip --cache",
11809
+ build: "turbo run _build",
11810
+ _build: "node scripts/build.mts",
11811
+ "build:bundle": "node scripts/build.mts --bundle-only",
11812
+ prepublishOnly: "pnpm build:bundle",
11813
+ schema: "node scripts/gen-schema.mts"
11814
+ },
11815
+ dependencies: {
11816
+ "@clack/prompts": "^1.8.0",
11817
+ "@orpc/client": "1.15.4",
11818
+ "@orpc/server": "1.15.4",
11819
+ commander: "^15.0.0",
11820
+ cosmiconfig: "^10.0.1",
11821
+ picomatch: "^4.0.7",
11822
+ undici: "7.29.1",
11823
+ zod: "^4.6.1"
11824
+ },
11825
+ devDependencies: {
11826
+ "@commitlint/cli": "^21.2.2",
11827
+ "@commitlint/config-conventional": "^21.2.2",
11828
+ "@exadev/eslint-config": "2.12.1",
11829
+ "@semantic-release/changelog": "^7.0.0",
11830
+ "@semantic-release/git": "^11.0.1",
11831
+ "@semantic-release/npm": "^13.1.5",
11832
+ "@types/node": "^26.5.1",
11833
+ "@types/picomatch": "^4.0.3",
11834
+ "@vitest/coverage-v8": "^4.1.11",
11835
+ "conventional-changelog-conventionalcommits": "^10.4.0",
11836
+ esbuild: "^0.28.2",
11837
+ eslint: "^10.10.0",
11838
+ globals: "^17.12.0",
11839
+ husky: "^9.1.7",
11840
+ jiti: "^2.7.0",
11841
+ knip: "^6.35.1",
11842
+ "lint-staged": "^17.5.0",
11843
+ rollup: "4.64.0",
11844
+ "rollup-plugin-dts": "6.5.1",
11845
+ "semantic-release": "^25.0.9",
11846
+ turbo: "^2.11.5",
11847
+ typescript: "^6.0.3",
11848
+ "typescript-eslint": "^8.70.0",
11849
+ vitest: "^4.1.11"
11850
+ },
11851
+ main: "dist/index.cjs",
11852
+ module: "dist/index.mjs",
11853
+ types: "dist/types/index.d.ts",
11854
+ exports: {
11855
+ ".": {
11856
+ types: "./dist/types/index.d.ts",
11857
+ import: "./dist/index.mjs",
11858
+ require: "./dist/index.cjs"
11859
+ },
11860
+ "./package.json": "./package.json"
11861
+ }
11862
+ };
11863
+
11460
11864
  // src/launcher/argv.ts
11461
11865
  var ConflictingIdentityError = class extends UsageError {
11462
11866
  constructor(positional, flag) {
@@ -11929,6 +12333,88 @@ function spawnClaude(params) {
11929
12333
  return params.proc.exit(exitCodeFor(result));
11930
12334
  }
11931
12335
 
12336
+ // src/update/update.ts
12337
+ var LATEST_RELEASE_URL = "https://github.com/ExaDev/agent-shim/releases/latest";
12338
+ function compareVersions3(left, right) {
12339
+ const parse = (version) => {
12340
+ const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
12341
+ if (match === null) {
12342
+ throw new Error(`not a plain release version: ${version}`);
12343
+ }
12344
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
12345
+ };
12346
+ const leftParts = parse(left);
12347
+ const rightParts = parse(right);
12348
+ return leftParts.map((part, index) => part - (rightParts[index] ?? 0)).find((difference) => difference !== 0) ?? 0;
12349
+ }
12350
+ function releaseTagFromUrl(url) {
12351
+ const segment = url.split("?")[0]?.split("/").at(-1);
12352
+ const candidate = segment?.startsWith("v") === true ? segment.slice(1) : void 0;
12353
+ return candidate !== void 0 && /^\d+\.\d+\.\d+$/.test(candidate) ? candidate : void 0;
12354
+ }
12355
+
12356
+ // src/update/launchHook.ts
12357
+ var UPDATE_CHECK_COOLDOWN_MS = 864e5;
12358
+ function resolveUpdateMode(globalConfig) {
12359
+ return globalConfig?.update?.mode ?? "off";
12360
+ }
12361
+ function updateCheckDue(contents, nowMs, cooldownMs = UPDATE_CHECK_COOLDOWN_MS) {
12362
+ if (contents === void 0) {
12363
+ return true;
12364
+ }
12365
+ const checkedAt = Date.parse(contents.trim());
12366
+ if (Number.isNaN(checkedAt)) {
12367
+ return true;
12368
+ }
12369
+ const age = nowMs - checkedAt;
12370
+ return age < 0 || age >= cooldownMs;
12371
+ }
12372
+ var NOOP_HANDLE = { markChildStarted: () => void 0 };
12373
+ function updateNotifyLine(currentVersion, latest) {
12374
+ return `agent-shim: update available: ${currentVersion} -> ${latest} (run \`agent-shim update\` to install it)`;
12375
+ }
12376
+ function runLaunchUpdateCheck(params) {
12377
+ const { mode, checkPath, currentVersion, port } = params;
12378
+ if (mode === "off" || port === void 0) {
12379
+ return NOOP_HANDLE;
12380
+ }
12381
+ let childStarted = false;
12382
+ try {
12383
+ if (!updateCheckDue(port.readStamp(checkPath), port.now())) {
12384
+ return NOOP_HANDLE;
12385
+ }
12386
+ port.writeStamp(checkPath, new Date(port.now()).toISOString());
12387
+ } catch {
12388
+ return NOOP_HANDLE;
12389
+ }
12390
+ const answer = (url) => {
12391
+ try {
12392
+ const latest = releaseTagFromUrl(url);
12393
+ if (latest === void 0 || compareVersions3(latest, currentVersion) <= 0) {
12394
+ return;
12395
+ }
12396
+ if (mode === "auto") {
12397
+ port.spawnDetached(["update"]);
12398
+ }
12399
+ if (!childStarted) {
12400
+ port.writeErr(updateNotifyLine(currentVersion, latest));
12401
+ }
12402
+ } catch {
12403
+ }
12404
+ };
12405
+ port.effectiveUrl(LATEST_RELEASE_URL).then(
12406
+ (url) => {
12407
+ answer(url);
12408
+ },
12409
+ () => void 0
12410
+ );
12411
+ return {
12412
+ markChildStarted: () => {
12413
+ childStarted = true;
12414
+ }
12415
+ };
12416
+ }
12417
+
11932
12418
  // src/launcher.ts
11933
12419
  function resolveIdentityCredential(identity, credential, env, port) {
11934
12420
  if (port === void 0) {
@@ -12094,6 +12580,12 @@ function prepareLaunch(params) {
12094
12580
  log.info(
12095
12581
  `agent-shim: identity ${identityDecision.name ?? "(none)"} (${identityDecision.source}${poolExplanation === void 0 ? "" : `, ${poolExplanation}`}), config profile ${configProfileDecision.name ?? "(none)"} (${configProfileDecision.source})` + (resolvedProvider === void 0 ? "" : `, provider ${resolvedProvider.name} (credential ${resolvedProvider.credential.target} from ${describeSource(resolvedProvider.credential.source)})`) + (identityCredential === void 0 ? "" : `, identity credential ${identityCredential.target} from ${describeSource(identityCredential.source)}`)
12096
12582
  );
12583
+ const updateCheck = runLaunchUpdateCheck({
12584
+ mode: params.updateMode ?? "off",
12585
+ checkPath: import_node_path30.default.join(paths.root, "update.check"),
12586
+ currentVersion: package_default.version,
12587
+ port: params.update
12588
+ });
12097
12589
  let cascadeLaunch;
12098
12590
  if (farmContext !== void 0) {
12099
12591
  const { farm: resyncFarmRuntime, identity: resyncIdentity, cascade } = farmContext;
@@ -12199,11 +12691,12 @@ function prepareLaunch(params) {
12199
12691
  registered();
12200
12692
  }
12201
12693
  };
12202
- return { bin: discovered.path, args: finalArgv, env: finalEnv, release };
12694
+ return { bin: discovered.path, args: finalArgv, env: finalEnv, release, markChildStarted: updateCheck.markChildStarted };
12203
12695
  }
12204
12696
  function runLauncher(params) {
12205
12697
  const plan = prepareLaunch(params);
12206
12698
  try {
12699
+ plan.markChildStarted();
12207
12700
  spawnClaude({ bin: plan.bin, args: plan.args, env: plan.env, spawn: params.spawn, proc: params.proc, beforeExit: plan.release });
12208
12701
  } finally {
12209
12702
  plan.release();
@@ -12366,6 +12859,7 @@ function buildFarmRuntime(paths, cwd) {
12366
12859
  const loaded = loadCascadeInput({ paths, home, cwd, read });
12367
12860
  const selections = readDirectorySelections(loaded);
12368
12861
  const git = resolveGitBranch(realRunPort, cwd);
12862
+ const updateMode = resolveUpdateMode(loaded.globalConfig);
12369
12863
  return {
12370
12864
  runtime: {
12371
12865
  fs: realFarmFs,
@@ -12391,7 +12885,8 @@ function buildFarmRuntime(paths, cwd) {
12391
12885
  ...selections.identity === void 0 ? {} : { directoryIdentity: selections.identity },
12392
12886
  ...selections.configProfile === void 0 ? {} : { directoryConfigProfile: selections.configProfile },
12393
12887
  ...loaded.globalConfig?.defaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: loaded.globalConfig.defaultConfigProfile },
12394
- ...loaded.globalConfig?.pools === void 0 ? {} : { pools: loaded.globalConfig.pools }
12888
+ ...loaded.globalConfig?.pools === void 0 ? {} : { pools: loaded.globalConfig.pools },
12889
+ ...updateMode === "off" ? {} : { updateMode }
12395
12890
  };
12396
12891
  }
12397
12892
  function realClaudeBinaryResolver(paths) {
@@ -12413,6 +12908,7 @@ function realPrepareLaunchParams(paths, options) {
12413
12908
  ...farm.directoryConfigProfile === void 0 ? {} : { directoryRuleConfigProfile: farm.directoryConfigProfile },
12414
12909
  ...farm.globalDefaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: farm.globalDefaultConfigProfile },
12415
12910
  ...farm.pools === void 0 ? {} : { pools: farm.pools },
12911
+ ...farm.updateMode === void 0 ? {} : { updateMode: farm.updateMode },
12416
12912
  ...options.allowMissingConfigProfile === void 0 ? {} : { allowMissingConfigProfile: options.allowMissingConfigProfile }
12417
12913
  };
12418
12914
  }
@@ -12511,10 +13007,12 @@ function prepareClaudeLaunch(options) {
12511
13007
  ProviderKindMismatchError,
12512
13008
  ProviderNotFoundError,
12513
13009
  ProviderSchema,
13010
+ RC_CONTEXT_USAGE_DETAILS,
12514
13011
  RC_IDLE_EXPIRY_MS,
12515
13012
  RC_ORPC_PATH_PREFIX,
12516
13013
  RC_PENDING_DEADLINE_MS,
12517
13014
  RC_PERMISSION_MODES,
13015
+ RC_READ_FILE_ENCODINGS,
12518
13016
  RC_REQUEST_PARSE_CAP_BYTES,
12519
13017
  RC_SELF_HOST_ENV,
12520
13018
  RC_SELF_HOST_KEEPALIVE_MS,
@@ -12545,11 +13043,23 @@ function prepareClaudeLaunch(options) {
12545
13043
  addPool,
12546
13044
  addProvider,
12547
13045
  answerRcControlRequest,
13046
+ authenticateRcSessionMcpServer,
12548
13047
  buildEntryFacts,
12549
13048
  buildLayoutPaths,
13049
+ buildRcControlRequestPayload,
12550
13050
  buildRcControlResponsePayload,
13051
+ buildRcEndSessionPayload,
12551
13052
  buildRcEventWriteBody,
13053
+ buildRcFileSuggestionsPayload,
13054
+ buildRcGetContextUsagePayload,
13055
+ buildRcGetUsagePayload,
12552
13056
  buildRcInterruptPayload,
13057
+ buildRcKeepAlivePayload,
13058
+ buildRcMcpAuthenticatePayload,
13059
+ buildRcMcpOAuthCallbackUrlPayload,
13060
+ buildRcMcpReconnectPayload,
13061
+ buildRcMcpStatusPayload,
13062
+ buildRcReadFilePayload,
12553
13063
  buildRcSetModelPayload,
12554
13064
  buildRcSetPermissionModePayload,
12555
13065
  buildRcUserMessagePayload,
@@ -12582,6 +13092,7 @@ function prepareClaudeLaunch(options) {
12582
13092
  doorApiAuth,
12583
13093
  doorApiNodeHandlerOf,
12584
13094
  effectiveWindow,
13095
+ endRcSession,
12585
13096
  ensureCa,
12586
13097
  ensureFrontDoor,
12587
13098
  ensureHeadroom,
@@ -12598,6 +13109,9 @@ function prepareClaudeLaunch(options) {
12598
13109
  frontDoorRcApiClient,
12599
13110
  frontDoorRcControl,
12600
13111
  generateCa,
13112
+ getRcSessionContextUsage,
13113
+ getRcSessionMcpStatus,
13114
+ getRcSessionUsage,
12601
13115
  headroomSocketTarget,
12602
13116
  identityExists,
12603
13117
  injectRcUserMessage,
@@ -12605,7 +13119,9 @@ function prepareClaudeLaunch(options) {
12605
13119
  isIdentityDirectoryName,
12606
13120
  isInterceptedHost,
12607
13121
  isPrivateAddress,
13122
+ isRcContextUsageDetail,
12608
13123
  isRcPermissionMode,
13124
+ isRcReadFileEncoding,
12609
13125
  lateRcEventDial,
12610
13126
  lateRcStreamDial,
12611
13127
  listDirectoryRules,
@@ -12639,6 +13155,7 @@ function prepareClaudeLaunch(options) {
12639
13155
  readProfile,
12640
13156
  readProvider,
12641
13157
  readRcSelfHostRecord,
13158
+ readRcSessionFile,
12642
13159
  readUsageSnapshot,
12643
13160
  realConnectCertStore,
12644
13161
  realConnectEffects,
@@ -12646,6 +13163,7 @@ function prepareClaudeLaunch(options) {
12646
13163
  realRcEventDial,
12647
13164
  realRcStreamDial,
12648
13165
  realRcWebFetch,
13166
+ reconnectRcSessionMcpServer,
12649
13167
  recoverFarm,
12650
13168
  recoveryDiagnostics,
12651
13169
  removeDirectoryRule,
@@ -12665,6 +13183,7 @@ function prepareClaudeLaunch(options) {
12665
13183
  runFrontDoorSupervisor,
12666
13184
  runLauncher,
12667
13185
  runSupervisor,
13186
+ sendRcKeepAlive,
12668
13187
  servedByPipeline,
12669
13188
  setAllowAmbientCredential,
12670
13189
  setDefaultConfigProfile,
@@ -12680,6 +13199,8 @@ function prepareClaudeLaunch(options) {
12680
13199
  snapshotPath,
12681
13200
  spawnClaude,
12682
13201
  startConnectServer,
13202
+ submitRcSessionMcpOAuthCallbackUrl,
13203
+ suggestRcSessionFiles,
12683
13204
  topLevelNames,
12684
13205
  updateDirectoryRule,
12685
13206
  updateProvider,