agent-shim 8.24.0 → 8.26.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
@@ -114,11 +114,15 @@ __export(index_exports, {
114
114
  RC_WEB_FETCH_MAX_BYTES: () => RC_WEB_FETCH_MAX_BYTES,
115
115
  RC_WEB_FETCH_MAX_REDIRECTS: () => RC_WEB_FETCH_MAX_REDIRECTS,
116
116
  ROUTED_PATH_PREFIX: () => ROUTED_PATH_PREFIX,
117
+ RcLiveRateLimitSchema: () => RcLiveRateLimitSchema,
117
118
  RcPermissionModeSchema: () => RcPermissionModeSchema,
119
+ RcRateLimitInfoSchema: () => RcRateLimitInfoSchema,
120
+ RcRateLimitWindowSchema: () => RcRateLimitWindowSchema,
118
121
  RcStreamEnvelopeSchema: () => RcStreamEnvelopeSchema,
119
122
  RcStreamEventSchema: () => RcStreamEventSchema,
120
123
  UsageError: () => UsageError,
121
124
  UsageListOutputSchema: () => UsageListOutputSchema,
125
+ UsageLiveOutputSchema: () => UsageLiveOutputSchema,
122
126
  UsageSnapshotError: () => UsageSnapshotError,
123
127
  UsageSnapshotSchema: () => UsageSnapshotSchema,
124
128
  UsageWindowsInputSchema: () => UsageWindowsInputSchema,
@@ -141,6 +145,7 @@ __export(index_exports, {
141
145
  buildRcGetUsagePayload: () => buildRcGetUsagePayload,
142
146
  buildRcInterruptPayload: () => buildRcInterruptPayload,
143
147
  buildRcKeepAlivePayload: () => buildRcKeepAlivePayload,
148
+ buildRcMarkReadBody: () => buildRcMarkReadBody,
144
149
  buildRcMcpAuthenticatePayload: () => buildRcMcpAuthenticatePayload,
145
150
  buildRcMcpOAuthCallbackUrlPayload: () => buildRcMcpOAuthCallbackUrlPayload,
146
151
  buildRcMcpReconnectPayload: () => buildRcMcpReconnectPayload,
@@ -169,6 +174,7 @@ __export(index_exports, {
169
174
  createRcControlHandler: () => createRcControlHandler,
170
175
  createRcCredentialStore: () => createRcCredentialStore,
171
176
  createRcEventFanout: () => createRcEventFanout,
177
+ createRcLiveUsage: () => createRcLiveUsage,
172
178
  createRcSelfHostSurface: () => createRcSelfHostSurface,
173
179
  createRcSessionTracker: () => createRcSessionTracker,
174
180
  createRcStreamHub: () => createRcStreamHub,
@@ -230,6 +236,7 @@ __export(index_exports, {
230
236
  rcEventWriteResultFromAnswer: () => rcEventWriteResultFromAnswer,
231
237
  rcFanoutOnDoorHub: () => rcFanoutOnDoorHub,
232
238
  rcObserverAsPassthrough: () => rcObserverAsPassthrough,
239
+ rcRateLimitInfoOf: () => rcRateLimitInfoOf,
233
240
  rcSelfHostFromEnv: () => rcSelfHostFromEnv,
234
241
  rcWebFetchAllowsPrivateFromEnv: () => rcWebFetchAllowsPrivateFromEnv,
235
242
  readActiveIdentity: () => readActiveIdentity,
@@ -287,6 +294,7 @@ __export(index_exports, {
287
294
  startConnectServer: () => startConnectServer,
288
295
  submitRcSessionMcpOAuthCallbackUrl: () => submitRcSessionMcpOAuthCallbackUrl,
289
296
  suggestRcSessionFiles: () => suggestRcSessionFiles,
297
+ teleportRcSession: () => teleportRcSession,
290
298
  topLevelNames: () => topLevelNames,
291
299
  updateDirectoryRule: () => updateDirectoryRule,
292
300
  updateProvider: () => updateProvider,
@@ -3504,9 +3512,6 @@ var HTTP_STATUS = {
3504
3512
  badGateway: 502
3505
3513
  };
3506
3514
 
3507
- // src/frontdoor/rcControl.ts
3508
- var https = __toESM(require("node:https"), 1);
3509
-
3510
3515
  // src/frontdoor/rcSessions.ts
3511
3516
  var RC_SESSIONS_PATH_PREFIX = "/v1/code/sessions";
3512
3517
  var RC_SESSION_ID_PREFIX = "cse_";
@@ -4104,7 +4109,7 @@ async function answerRcControlRequest(deps, sessionId, requestId, decision) {
4104
4109
  }
4105
4110
  return result;
4106
4111
  }
4107
- async function deliverRcClientPayload(deps, sessionId, payload, action) {
4112
+ async function deliverRcClientPayload(deps, sessionId, payload, action, to = "events") {
4108
4113
  const credential = deps.credentialOf(sessionId);
4109
4114
  if (credential === void 0) {
4110
4115
  return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
@@ -4115,7 +4120,7 @@ async function deliverRcClientPayload(deps, sessionId, payload, action) {
4115
4120
  const body = JSON.stringify(buildRcEventWriteBody(payload));
4116
4121
  let answer;
4117
4122
  try {
4118
- answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
4123
+ answer = await (to === "teleport-events" ? deps.dial.writeTeleportEvents(sessionId, rcWriteHeaders(credential), body) : deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body));
4119
4124
  } catch (error) {
4120
4125
  return { ok: false, message: `the door could not reach the API host to ${action}: ${error instanceof Error ? error.message : String(error)}` };
4121
4126
  }
@@ -4209,8 +4214,18 @@ async function submitRcSessionMcpOAuthCallbackUrl(deps, sessionId, serverName, c
4209
4214
  async function sendRcKeepAlive(deps, sessionId) {
4210
4215
  return await deliverRcClientPayload(deps, sessionId, buildRcKeepAlivePayload(), `keep session ${sessionId} alive`);
4211
4216
  }
4217
+ async function teleportRcSession(deps, sessionId, marker) {
4218
+ if (marker === "") {
4219
+ 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" };
4220
+ }
4221
+ return await deliverRcClientPayload(deps, sessionId, buildRcUserMessagePayload(deps.newUuid(), sessionId, marker), `teleport a marker into session ${sessionId}`, "teleport-events");
4222
+ }
4223
+ function buildRcMarkReadBody(eventId) {
4224
+ return eventId === void 0 ? {} : { event_id: eventId };
4225
+ }
4212
4226
 
4213
4227
  // src/frontdoor/rcControl.ts
4228
+ var https = __toESM(require("node:https"), 1);
4214
4229
  var RC_PERMISSION_MODE_LIST = RC_PERMISSION_MODES.join(", ");
4215
4230
  var CONTROL_PATH_PREFIX = "/__agent-shim/rc";
4216
4231
  var CONTROL_BODY_CAP_BYTES = 10485760;
@@ -4384,6 +4399,15 @@ function createRcControlHandler(deps) {
4384
4399
  const callbackUrl = bodyString(body, "callbackUrl");
4385
4400
  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
4401
  }
4402
+ },
4403
+ {
4404
+ name: "teleport",
4405
+ path: "teleport",
4406
+ read: (body) => {
4407
+ const session = bodyString(body, "session");
4408
+ const marker = bodyString(body, "marker");
4409
+ 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) };
4410
+ }
4387
4411
  }
4388
4412
  ];
4389
4413
  return (request4, response) => {
@@ -4747,7 +4771,8 @@ function frontDoorRcControl(transport, token) {
4747
4771
  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
4772
  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
4773
  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}`)
4774
+ 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}`),
4775
+ 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}`)
4751
4776
  };
4752
4777
  }
4753
4778
 
@@ -4894,36 +4919,38 @@ function tlsFacts(options) {
4894
4919
  }
4895
4920
  function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
4896
4921
  const agent = new ExemptTlsAgent();
4897
- return {
4898
- writeEvents: async (sessionId, headers, body) => await new Promise((resolve, reject) => {
4899
- const options = {
4900
- host: target.host,
4901
- port: target.port,
4902
- method: "POST",
4903
- path: `/v1/code/sessions/${encodeURIComponent(sessionId)}/events`,
4904
- // 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.
4905
- headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
4906
- agent
4907
- };
4908
- options.servername = target.servername ?? target.host;
4909
- if (target.ca !== void 0) {
4910
- options.ca = [...target.ca];
4911
- }
4912
- if (target.rejectUnauthorized !== void 0) {
4913
- options.rejectUnauthorized = target.rejectUnauthorized;
4914
- }
4915
- const request4 = https2.request(options, (response) => {
4916
- const chunks = [];
4917
- response.on("data", (chunk) => {
4918
- chunks.push(chunk);
4919
- });
4920
- response.on("end", () => {
4921
- resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
4922
- });
4922
+ const post = async (path32, headers, body) => await new Promise((resolve, reject) => {
4923
+ const options = {
4924
+ host: target.host,
4925
+ port: target.port,
4926
+ method: "POST",
4927
+ path: path32,
4928
+ // 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.
4929
+ headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
4930
+ agent
4931
+ };
4932
+ options.servername = target.servername ?? target.host;
4933
+ if (target.ca !== void 0) {
4934
+ options.ca = [...target.ca];
4935
+ }
4936
+ if (target.rejectUnauthorized !== void 0) {
4937
+ options.rejectUnauthorized = target.rejectUnauthorized;
4938
+ }
4939
+ const request4 = https2.request(options, (response) => {
4940
+ const chunks = [];
4941
+ response.on("data", (chunk) => {
4942
+ chunks.push(chunk);
4923
4943
  });
4924
- request4.once("error", reject);
4925
- request4.end(body);
4926
- })
4944
+ response.on("end", () => {
4945
+ resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
4946
+ });
4947
+ });
4948
+ request4.once("error", reject);
4949
+ request4.end(body);
4950
+ });
4951
+ return {
4952
+ writeEvents: async (sessionId, headers, body) => await post(`/v1/code/sessions/${encodeURIComponent(sessionId)}/events`, headers, body),
4953
+ writeTeleportEvents: async (sessionId, headers, body) => await post(`/v1/code/sessions/${encodeURIComponent(sessionId)}/teleport-events`, headers, body)
4927
4954
  };
4928
4955
  }
4929
4956
  var NO_CHUNKS = {
@@ -4941,7 +4968,8 @@ function lateRcEventDial(resolve) {
4941
4968
  return built.dial;
4942
4969
  };
4943
4970
  return {
4944
- writeEvents: async (sessionId, headers, body) => await dialFor().writeEvents(sessionId, headers, body)
4971
+ writeEvents: async (sessionId, headers, body) => await dialFor().writeEvents(sessionId, headers, body),
4972
+ writeTeleportEvents: async (sessionId, headers, body) => await dialFor().writeTeleportEvents(sessionId, headers, body)
4945
4973
  };
4946
4974
  }
4947
4975
  function lateRcStreamDial(resolve) {
@@ -4954,7 +4982,8 @@ function lateRcStreamDial(resolve) {
4954
4982
  return built.dial;
4955
4983
  };
4956
4984
  return {
4957
- announcePresence: async (sessionId, headers, clientId) => await dialFor().announcePresence(sessionId, headers, clientId),
4985
+ announcePresence: async (sessionId, headers, clientId, clear) => await dialFor().announcePresence(sessionId, headers, clientId, clear),
4986
+ markRead: async (sessionId, headers, eventId) => await dialFor().markRead(sessionId, headers, eventId),
4958
4987
  openStream: async (sessionId, headers, resume, signal) => await dialFor().openStream(sessionId, headers, resume, signal)
4959
4988
  };
4960
4989
  }
@@ -4996,11 +5025,16 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
4996
5025
  return Buffer.concat(chunks).toString("utf8");
4997
5026
  };
4998
5027
  return {
4999
- announcePresence: async (sessionId, headers, clientId) => {
5000
- const body = JSON.stringify({ client_id: clientId, clear: false });
5028
+ announcePresence: async (sessionId, headers, clientId, clear) => {
5029
+ const body = JSON.stringify({ client_id: clientId, clear });
5001
5030
  const { status, response } = await request4("POST", `/v1/code/sessions/${encodeURIComponent(sessionId)}/client/presence`, { ...headers, "content-type": "application/json" }, body, new AbortController().signal);
5002
5031
  return { status, body: await readAll(response) };
5003
5032
  },
5033
+ markRead: async (sessionId, headers, eventId) => {
5034
+ const body = JSON.stringify(buildRcMarkReadBody(eventId));
5035
+ const { status, response } = await request4("POST", `/v1/code/sessions/${encodeURIComponent(sessionId)}/mark_read`, { ...headers, "content-type": "application/json" }, body, new AbortController().signal);
5036
+ return { status, body: await readAll(response) };
5037
+ },
5004
5038
  openStream: async (sessionId, headers, resume, signal) => {
5005
5039
  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)}`;
5006
5040
  const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
@@ -5345,6 +5379,35 @@ var RcStreamEventSchema = import_zod4.z.strictObject({
5345
5379
  session: import_zod4.z.string(),
5346
5380
  envelope: RcStreamEnvelopeSchema
5347
5381
  });
5382
+ var RcRateLimitWindowSchema = import_zod4.z.looseObject({
5383
+ /** The fraction of the window used, usually between 0 and 1, above 1 when usage legitimately runs past the window's cap (the CLI's own field description states both). */
5384
+ utilization: import_zod4.z.number().optional(),
5385
+ /** When the window resets, in unix epoch seconds: the unit is the CLI's own field description, stated in the 2.1.289 bundle's schema. */
5386
+ resetsAt: import_zod4.z.number().optional()
5387
+ });
5388
+ var RcRateLimitInfoSchema = import_zod4.z.looseObject({
5389
+ /** The limiting window's own status (`allowed`, `allowed_warning`, `rejected`), asserted only as a string because the vocabulary grows with the CLI. */
5390
+ status: import_zod4.z.string(),
5391
+ /** When the limiting window resets, in unix epoch seconds. */
5392
+ resetsAt: import_zod4.z.number().optional(),
5393
+ /** Which window the top-level fields describe (`five_hour`, `seven_day`, and kin), the payload's own name for it. */
5394
+ rateLimitType: import_zod4.z.string().optional(),
5395
+ /** The fraction of the limiting window used, on the same scale the per-window utilizations carry. */
5396
+ utilization: import_zod4.z.number().optional(),
5397
+ /** Whether usage beyond the plan (extra usage) is currently covering the overflow, the payload's own flag for it. */
5398
+ isUsingOverage: import_zod4.z.boolean().optional(),
5399
+ /** The overage's own status, the same vocabulary the limiting window's `status` carries. */
5400
+ overageStatus: import_zod4.z.string().optional(),
5401
+ /** When the overage window resets, in unix epoch seconds. */
5402
+ overageResetsAt: import_zod4.z.number().optional(),
5403
+ /** Each subscription window the account's response headers carried: the session (five-hour), weekly (seven-day), and overage-included weekly windows, each present only when the account's responses carry it. */
5404
+ unifiedWindows: import_zod4.z.looseObject({ five_hour: RcRateLimitWindowSchema.optional(), seven_day: RcRateLimitWindowSchema.optional(), seven_day_overage_included: RcRateLimitWindowSchema.optional() }).optional()
5405
+ });
5406
+ var RcLiveRateLimitSchema = import_zod4.z.strictObject({
5407
+ session: import_zod4.z.string(),
5408
+ observedAt: import_zod4.z.number(),
5409
+ rateLimit: RcRateLimitInfoSchema
5410
+ });
5348
5411
  var RcSessionQuerySchema = import_zod4.z.strictObject({
5349
5412
  session: import_zod4.z.string().min(1).optional()
5350
5413
  });
@@ -5411,6 +5474,10 @@ var RcMcpOAuthCallbackUrlInputSchema = import_zod4.z.strictObject({
5411
5474
  serverName: import_zod4.z.string().min(1),
5412
5475
  callbackUrl: import_zod4.z.string().min(1)
5413
5476
  });
5477
+ var RcTeleportInputSchema = import_zod4.z.strictObject({
5478
+ session: import_zod4.z.string().min(1),
5479
+ marker: import_zod4.z.string().min(1)
5480
+ });
5414
5481
  var RcListOutputSchema = import_zod4.z.strictObject({ sessions: import_zod4.z.readonly(import_zod4.z.array(RcSessionSummarySchema)) });
5415
5482
  var RcStatusOutputSchema = import_zod4.z.strictObject({ statuses: import_zod4.z.readonly(import_zod4.z.array(RcSessionStatusSchema)) });
5416
5483
  var RcPendingOutputSchema = import_zod4.z.strictObject({ pending: import_zod4.z.readonly(import_zod4.z.array(RcPendingRequestSummarySchema)) });
@@ -5495,6 +5562,7 @@ function createRcApiRouter(deps) {
5495
5562
  mcpReconnect: authed.input(RcMcpReconnectInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpReconnect(input.session, input.serverName)) })),
5496
5563
  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
5564
  mcpOAuthCallbackUrl: authed.input(RcMcpOAuthCallbackUrlInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpOAuthCallbackUrl(input.session, input.serverName, input.callbackUrl)) })),
5565
+ teleport: authed.input(RcTeleportInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.teleport(input.session, input.marker)) })),
5498
5566
  subscribe: authed.input(RcSessionQuerySchema).output((0, import_server2.eventIterator)(RcStreamEventSchema)).handler(async function* ({ input, signal }) {
5499
5567
  const queue = [];
5500
5568
  let wake;
@@ -9950,6 +10018,12 @@ var UsageWindowsOutputSchema = import_zod19.z.strictObject({
9950
10018
  fiveHour: EffectiveWindowSchema.optional(),
9951
10019
  sevenDay: EffectiveWindowSchema.optional()
9952
10020
  });
10021
+ var UsageLiveOutputSchema = import_zod19.z.strictObject({
10022
+ /** The freshest observation the door holds, never narrowed by the read's session filter because it is the door's single latest statement of the account's quota; absent while no rate_limit_event has been filed at all. */
10023
+ latest: RcLiveRateLimitSchema.optional(),
10024
+ /** One observation per session whose stream has filed a rate_limit_event this door generation. */
10025
+ sessions: import_zod19.z.readonly(import_zod19.z.array(RcLiveRateLimitSchema))
10026
+ });
9953
10027
  var FrontDoorSessionStatusSchema = import_zod19.z.strictObject({
9954
10028
  pid: import_zod19.z.int().positive(),
9955
10029
  startedAt: import_zod19.z.number(),
@@ -10064,6 +10138,16 @@ function createControlApiRouter(deps) {
10064
10138
  ...unified?.fiveHour === void 0 ? {} : { fiveHour: effectiveWindow(unified.fiveHour, now) },
10065
10139
  ...unified?.sevenDay === void 0 ? {} : { sevenDay: effectiveWindow(unified.sevenDay, now) }
10066
10140
  };
10141
+ }),
10142
+ live: authed.input(RcSessionQuerySchema).output(UsageLiveOutputSchema).handler(({ input }) => {
10143
+ const sessions = deps.liveRateLimits(input.session);
10144
+ if (input.session !== void 0 && sessions.length === 0) {
10145
+ if (deps.list().some((session) => session.id === input.session)) {
10146
+ throw new import_client3.ORPCError("NOT_FOUND", { message: `session ${input.session} has filed no rate_limit_event on its stream yet: the worker emits one only once a turn has completed, so retry after this session's next turn` });
10147
+ }
10148
+ throw new import_client3.ORPCError("NOT_FOUND", { message: rcSessionNotObservedMessage(input.session) });
10149
+ }
10150
+ return { latest: deps.latestRateLimit(), sessions };
10067
10151
  })
10068
10152
  },
10069
10153
  frontdoor: {
@@ -10284,6 +10368,42 @@ function createLaunchEventPublisher(hub, now) {
10284
10368
  };
10285
10369
  }
10286
10370
 
10371
+ // src/frontdoor/rcLiveUsage.ts
10372
+ var RC_RATE_LIMIT_EVENT_TYPE = "rate_limit_event";
10373
+ function isRecord5(value) {
10374
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10375
+ }
10376
+ function rcRateLimitInfoOf(event) {
10377
+ const payload = event.envelope.payload;
10378
+ if (!isRecord5(payload) || payload.type !== RC_RATE_LIMIT_EVENT_TYPE) {
10379
+ return void 0;
10380
+ }
10381
+ if (!isRecord5(payload.rate_limit_info)) {
10382
+ return void 0;
10383
+ }
10384
+ const parsed = RcRateLimitInfoSchema.safeParse(payload.rate_limit_info);
10385
+ return parsed.success ? parsed.data : void 0;
10386
+ }
10387
+ function createRcLiveUsage(deps) {
10388
+ const observed = /* @__PURE__ */ new Map();
10389
+ deps.hub.subscribe([DOOR_EVENT_SOURCE_RC], (event) => {
10390
+ const stream = RcStreamEventSchema.safeParse(event.payload);
10391
+ if (!stream.success) {
10392
+ return;
10393
+ }
10394
+ const rateLimit = rcRateLimitInfoOf(stream.data);
10395
+ if (rateLimit === void 0) {
10396
+ return;
10397
+ }
10398
+ observed.set(stream.data.session, { session: stream.data.session, observedAt: deps.now(), rateLimit });
10399
+ });
10400
+ const sorted = () => [...observed.values()].sort((left, right) => left.observedAt - right.observedAt || (left.session < right.session ? -1 : 1));
10401
+ return {
10402
+ liveOf: (sessionId) => sorted().filter((entry) => sessionId === void 0 || entry.session === sessionId),
10403
+ latestOf: () => sorted().at(-1)
10404
+ };
10405
+ }
10406
+
10287
10407
  // src/frontdoor/rcStream.ts
10288
10408
  var RC_STREAM_EVENT_NAME = "client_event";
10289
10409
  var SSE_BYTE_ORDER_MARK = 65279;
@@ -10346,7 +10466,7 @@ function createSseParser() {
10346
10466
  }
10347
10467
  };
10348
10468
  }
10349
- function isRecord5(value) {
10469
+ function isRecord6(value) {
10350
10470
  return typeof value === "object" && value !== null && !Array.isArray(value);
10351
10471
  }
10352
10472
  function sequenceNumOf(value) {
@@ -10368,7 +10488,7 @@ function parseRcStreamEnvelope(parsed) {
10368
10488
  } catch {
10369
10489
  return void 0;
10370
10490
  }
10371
- if (!isRecord5(data)) {
10491
+ if (!isRecord6(data)) {
10372
10492
  return void 0;
10373
10493
  }
10374
10494
  const eventType = data.event_type;
@@ -10451,9 +10571,26 @@ function createRcStreamHub(deps) {
10451
10571
  }
10452
10572
  return headers;
10453
10573
  };
10574
+ let receiptFailureLogged = false;
10575
+ const sendReceipt = (attachment, headers, eventId) => {
10576
+ if (eventId === void 0) {
10577
+ return;
10578
+ }
10579
+ deps.dial.markRead(attachment.sessionId, headers, eventId).then(
10580
+ () => {
10581
+ receiptFailureLogged = false;
10582
+ },
10583
+ (error) => {
10584
+ if (!receiptFailureLogged) {
10585
+ receiptFailureLogged = true;
10586
+ 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`);
10587
+ }
10588
+ }
10589
+ );
10590
+ };
10454
10591
  const attempt = async (attachment, credential) => {
10455
10592
  const headers = streamHeaders(credential);
10456
- const presence = await deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId);
10593
+ const presence = await deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId, false);
10457
10594
  if (presence.status === HTTP_STATUS.unauthorized) {
10458
10595
  return "unauthorized";
10459
10596
  }
@@ -10473,6 +10610,7 @@ function createRcStreamHub(deps) {
10473
10610
  const parser = createSseParser();
10474
10611
  for await (const chunk of answer.chunks) {
10475
10612
  attachment.controller.signal.throwIfAborted();
10613
+ let lastEventId;
10476
10614
  for (const parsed of parser.write(Buffer.from(chunk).toString("utf8"))) {
10477
10615
  const envelope = parseRcStreamEnvelope(parsed);
10478
10616
  if (envelope === void 0) {
@@ -10480,10 +10618,21 @@ function createRcStreamHub(deps) {
10480
10618
  }
10481
10619
  deps.fileStreamEvent(attachment.sessionId, envelope);
10482
10620
  deps.fanout.publish({ session: attachment.sessionId, envelope });
10621
+ if (typeof envelope.event_id === "string" && envelope.event_type !== "mark_read") {
10622
+ lastEventId = envelope.event_id;
10623
+ }
10483
10624
  }
10625
+ sendReceipt(attachment, headers, lastEventId);
10484
10626
  }
10485
10627
  return "dropped";
10486
10628
  };
10629
+ const clearPresence = (attachment, headers) => {
10630
+ if (headers === void 0) {
10631
+ return;
10632
+ }
10633
+ void deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId, true).catch(() => {
10634
+ });
10635
+ };
10487
10636
  const runLoop = async (attachment) => {
10488
10637
  let retriedUnauthorized = false;
10489
10638
  for (; ; ) {
@@ -10495,6 +10644,7 @@ function createRcStreamHub(deps) {
10495
10644
  if (!live || credential?.authorization === void 0) {
10496
10645
  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"})`);
10497
10646
  persistCursor(attachment.sessionId);
10647
+ clearPresence(attachment, credential === void 0 ? void 0 : streamHeaders(credential));
10498
10648
  if (attachments.get(attachment.sessionId) === attachment) {
10499
10649
  attachments.delete(attachment.sessionId);
10500
10650
  }
@@ -10557,6 +10707,8 @@ function createRcStreamHub(deps) {
10557
10707
  close: () => {
10558
10708
  for (const attachment of attachments.values()) {
10559
10709
  persistCursor(attachment.sessionId);
10710
+ const credential = deps.credentialOf(attachment.sessionId);
10711
+ clearPresence(attachment, credential?.authorization === void 0 ? void 0 : streamHeaders(credential));
10560
10712
  attachment.stop();
10561
10713
  }
10562
10714
  attachments.clear();
@@ -10564,6 +10716,193 @@ function createRcStreamHub(deps) {
10564
10716
  };
10565
10717
  }
10566
10718
 
10719
+ // src/frontdoor/rcSelfHostEndpoints.ts
10720
+ var RC_SOURCE_WORKER = "worker";
10721
+ var RC_SOURCE_CLIENT = "client";
10722
+ function isRecord7(value) {
10723
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10724
+ }
10725
+ function parseJsonObject2(body) {
10726
+ let parsed;
10727
+ try {
10728
+ parsed = JSON.parse(body);
10729
+ } catch {
10730
+ return void 0;
10731
+ }
10732
+ return isRecord7(parsed) ? parsed : void 0;
10733
+ }
10734
+ function nonEmptyString2(record, field) {
10735
+ const value = record === void 0 ? void 0 : record[field];
10736
+ return typeof value === "string" && value !== "" ? value : void 0;
10737
+ }
10738
+ function numberOf(record, field) {
10739
+ const value = record === void 0 ? void 0 : record[field];
10740
+ if (typeof value === "number" && Number.isFinite(value)) {
10741
+ return value;
10742
+ }
10743
+ if (typeof value === "string" && /^[0-9]+$/.test(value)) {
10744
+ return Number(value);
10745
+ }
10746
+ return void 0;
10747
+ }
10748
+ function pageOfRcChannel(url, rows, idOf, rowOf) {
10749
+ const query = new URL(url, "http://127.0.0.1").searchParams;
10750
+ const anchor = query.get("after_event_id") ?? query.get("cursor");
10751
+ const rawLimit = query.get("limit");
10752
+ const limit = rawLimit === null ? void 0 : numberOf({ value: rawLimit }, "value");
10753
+ if (limit !== void 0 && limit < 1) {
10754
+ return { error: "the limit names a whole number of rows of at least one" };
10755
+ }
10756
+ const from = anchor === null ? 0 : rows.findIndex((row) => idOf(row) === anchor) + 1;
10757
+ if (anchor !== null && from === 0) {
10758
+ return { error: `the anchor names an event id this surface holds no row for: ${anchor}` };
10759
+ }
10760
+ const page = limit === void 0 ? rows.slice(from) : rows.slice(from, from + limit);
10761
+ const last = page[page.length - 1];
10762
+ return { data: page.map(rowOf), ...last === void 0 ? {} : { next_cursor: idOf(last) } };
10763
+ }
10764
+ async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
10765
+ const { request: request4, response, session } = ctx;
10766
+ if (tail === "client/presence" && method === "POST") {
10767
+ const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10768
+ const clientId = nonEmptyString2(body, "client_id");
10769
+ const clear = body?.clear;
10770
+ if (clientId === void 0 || clear !== void 0 && typeof clear !== "boolean") {
10771
+ 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" } });
10772
+ return true;
10773
+ }
10774
+ if (clear === true) {
10775
+ session.clients.delete(clientId);
10776
+ ctx.log?.(`rc selfhost ${session.id}: client ${clientId} cleared its presence`);
10777
+ } else {
10778
+ session.clients.set(clientId, ctx.now());
10779
+ ctx.log?.(`rc selfhost ${session.id}: client ${clientId} announced its presence${session.clients.size === 1 ? "" : ` (${String(session.clients.size)} clients now announced)`}`);
10780
+ }
10781
+ await ctx.answerJson(response, HTTP_STATUS.ok, { refresh_after_seconds: ctx.presenceRefreshSeconds });
10782
+ return true;
10783
+ }
10784
+ if (tail === "mark_read" && method === "POST") {
10785
+ const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10786
+ const eventId = nonEmptyString2(body, "event_id");
10787
+ const clientId = nonEmptyString2(body, "client_id");
10788
+ if (body !== void 0 && "event_id" in body && eventId === void 0 || "client_id" in (body ?? {}) && clientId === void 0) {
10789
+ 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" } });
10790
+ return true;
10791
+ }
10792
+ 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);
10793
+ 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)}`);
10794
+ await ctx.answerJson(response, HTTP_STATUS.ok, {});
10795
+ return true;
10796
+ }
10797
+ if (tail === "teleport-events" && method === "POST") {
10798
+ const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10799
+ const events = body?.events;
10800
+ if (!Array.isArray(events) || events.length === 0 || events.length > maxBatchEvents) {
10801
+ 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` } });
10802
+ return true;
10803
+ }
10804
+ const results = [];
10805
+ for (const event of events) {
10806
+ if (!isRecord7(event) || !isRecord7(event.payload)) {
10807
+ 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" } });
10808
+ return true;
10809
+ }
10810
+ const published = ctx.publish(session, RC_SOURCE_CLIENT, event.payload);
10811
+ results.push({ sequence_num: String(published.sequenceNum), duplicate: published.duplicate });
10812
+ }
10813
+ ctx.sweep();
10814
+ ctx.log?.(`rc selfhost ${session.id}: teleport write published ${String(results.length)} marker event(s)`);
10815
+ await ctx.answerJson(response, HTTP_STATUS.ok, { results });
10816
+ return true;
10817
+ }
10818
+ if (tail === "teleport-events" && method === "GET") {
10819
+ const page = pageOfRcChannel(request4.url, session.events, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.payload }));
10820
+ if ("error" in page) {
10821
+ await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: page.error } });
10822
+ return true;
10823
+ }
10824
+ await ctx.answerJson(response, HTTP_STATUS.ok, page);
10825
+ return true;
10826
+ }
10827
+ return false;
10828
+ }
10829
+ 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" });
10830
+ async function serveRcCompatSessions(ctx, pathname, compatPath) {
10831
+ const { request: request4, response } = ctx;
10832
+ const method = request4.method.toUpperCase();
10833
+ if (pathname === compatPath) {
10834
+ if (method !== "GET") {
10835
+ await ctx.answerJson(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
10836
+ return;
10837
+ }
10838
+ const listRefusal = ctx.credentialRefusal(request4.headers);
10839
+ if (listRefusal !== void 0) {
10840
+ await ctx.unauthorized(response, listRefusal);
10841
+ return;
10842
+ }
10843
+ await ctx.answerJson(response, HTTP_STATUS.ok, { data: [...ctx.sessions.values()].map((session2) => compatSessionRow(session2, ctx.now)) });
10844
+ return;
10845
+ }
10846
+ const rest = pathname.slice(`${compatPath}/`.length);
10847
+ const slash = rest.indexOf("/");
10848
+ const address = slash === -1 ? rest : rest.slice(0, slash);
10849
+ const compatTail = slash === -1 ? "" : rest.slice(slash + 1);
10850
+ const refusal = ctx.credentialRefusal(request4.headers);
10851
+ if (refusal !== void 0) {
10852
+ await ctx.unauthorized(response, refusal);
10853
+ return;
10854
+ }
10855
+ const session = ctx.sessions.get(address) ?? ctx.sessions.get(`cse_${address}`);
10856
+ if (compatTail === "") {
10857
+ if (method === "GET") {
10858
+ if (session === void 0) {
10859
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
10860
+ return;
10861
+ }
10862
+ await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
10863
+ return;
10864
+ }
10865
+ if (method === "PATCH" || method === "PUT") {
10866
+ if (session === void 0) {
10867
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
10868
+ return;
10869
+ }
10870
+ const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10871
+ const title = nonEmptyString2(body, "title");
10872
+ if (title === void 0) {
10873
+ 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" } });
10874
+ return;
10875
+ }
10876
+ session.title = title;
10877
+ session.lastTrafficAt = ctx.now();
10878
+ ctx.log?.(`rc selfhost ${session.id}: retitled through the compatibility family`);
10879
+ await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
10880
+ return;
10881
+ }
10882
+ 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" } });
10883
+ return;
10884
+ }
10885
+ if ((compatTail === "archive" || compatTail === "unarchive") && method === "POST") {
10886
+ await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES);
10887
+ if (session === void 0) {
10888
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
10889
+ return;
10890
+ }
10891
+ if (compatTail === "unarchive") {
10892
+ await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
10893
+ return;
10894
+ }
10895
+ for (const sink of [...session.workerStreams, ...session.clientStreams]) {
10896
+ sink.retire();
10897
+ }
10898
+ ctx.sessions.delete(session.id);
10899
+ ctx.log?.(`rc selfhost ${session.id}: archived through the compatibility family`);
10900
+ await ctx.answerJson(response, HTTP_STATUS.ok, {});
10901
+ return;
10902
+ }
10903
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the compatibility session family serves no path "${compatTail}"` } });
10904
+ }
10905
+
10567
10906
  // src/frontdoor/rcWebFetch.ts
10568
10907
  var dns2 = __toESM(require("node:dns"), 1);
10569
10908
  var http2 = __toESM(require("node:http"), 1);
@@ -10832,21 +11171,19 @@ var RC_PRESENCE_REFRESH_SECONDS = 60;
10832
11171
  var RC_SELF_HOST_MAX_BATCH_EVENTS = 100;
10833
11172
  var RC_SELF_HOST_WORKER_EPOCH = 1;
10834
11173
  var RC_STREAM_EVENT_NAME2 = "client_event";
10835
- var RC_SOURCE_WORKER = "worker";
10836
- var RC_SOURCE_CLIENT = "client";
10837
11174
  var CONFLICT_REASON_HEADER2 = "x-ccr-conflict-reason";
10838
11175
  var CONFLICT_REASON_EPOCH_STALE = "epoch_stale";
10839
- function isRecord6(value) {
11176
+ function isRecord8(value) {
10840
11177
  return typeof value === "object" && value !== null && !Array.isArray(value);
10841
11178
  }
10842
11179
  function isWebSearchRefusal(value) {
10843
11180
  return !Array.isArray(value);
10844
11181
  }
10845
- function nonEmptyString2(record, field) {
11182
+ function nonEmptyString3(record, field) {
10846
11183
  const value = record === void 0 ? void 0 : record[field];
10847
11184
  return typeof value === "string" && value !== "" ? value : void 0;
10848
11185
  }
10849
- function numberOf(record, field) {
11186
+ function numberOf2(record, field) {
10850
11187
  const value = record === void 0 ? void 0 : record[field];
10851
11188
  if (typeof value === "number" && Number.isFinite(value)) {
10852
11189
  return value;
@@ -10913,14 +11250,14 @@ async function readBody2(request4, capBytes) {
10913
11250
  });
10914
11251
  });
10915
11252
  }
10916
- function parseJsonObject2(body) {
11253
+ function parseJsonObject3(body) {
10917
11254
  let parsed;
10918
11255
  try {
10919
11256
  parsed = JSON.parse(body);
10920
11257
  } catch {
10921
11258
  return void 0;
10922
11259
  }
10923
- return isRecord6(parsed) ? parsed : void 0;
11260
+ return isRecord8(parsed) ? parsed : void 0;
10924
11261
  }
10925
11262
  function mintWorkerJwt(randomToken, issuedAt) {
10926
11263
  const encode = (value) => Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
@@ -10943,11 +11280,16 @@ function createRcSelfHostSurface(deps) {
10943
11280
  for (const session of sessions.values()) {
10944
11281
  while (session.events[0] !== void 0 && now - session.events[0].storedAt > RC_SELF_HOST_RETENTION_MS) {
10945
11282
  const dropped = session.events.shift();
10946
- const uuid = dropped === void 0 ? void 0 : nonEmptyString2(dropped.payload, "uuid");
11283
+ const uuid = dropped === void 0 ? void 0 : nonEmptyString3(dropped.payload, "uuid");
10947
11284
  if (uuid !== void 0) {
10948
11285
  session.seenPayloadUuids.delete(uuid);
10949
11286
  }
10950
11287
  }
11288
+ for (const [clientId, lastSeenAt] of session.clients) {
11289
+ if (now - lastSeenAt > RC_SELF_HOST_RETENTION_MS) {
11290
+ session.clients.delete(clientId);
11291
+ }
11292
+ }
10951
11293
  if (session.workerStreams.size === 0 && session.clientStreams.size === 0 && now - session.lastTrafficAt > RC_SELF_HOST_RETENTION_MS) {
10952
11294
  sessions.delete(session.id);
10953
11295
  }
@@ -10986,7 +11328,7 @@ function createRcSelfHostSurface(deps) {
10986
11328
  const frameOf = (event) => {
10987
11329
  const envelope = {
10988
11330
  event_id: event.eventId,
10989
- event_type: nonEmptyString2(event.payload, "type") ?? "event",
11331
+ event_type: nonEmptyString3(event.payload, "type") ?? "event",
10990
11332
  sequence_num: event.sequenceNum,
10991
11333
  source: event.source,
10992
11334
  payload: event.payload,
@@ -11000,7 +11342,7 @@ data: ${JSON.stringify(envelope)}
11000
11342
  };
11001
11343
  const holdStream = async (session, which, wantsReplay, request4, response) => {
11002
11344
  const url = new URL(request4.url, "http://127.0.0.1");
11003
- const cursor = numberOf({ value: url.searchParams.get("from_sequence_num") }, "value") ?? numberOf({ value: singleHeader2(request4.headers, "last-event-id") }, "value");
11345
+ const cursor = numberOf2({ value: url.searchParams.get("from_sequence_num") }, "value") ?? numberOf2({ value: singleHeader2(request4.headers, "last-event-id") }, "value");
11004
11346
  const replay = cursor === void 0 ? [] : session.events.filter((event) => wantsReplay(event) && event.sequenceNum > cursor);
11005
11347
  response.start(HTTP_STATUS.ok, { "content-type": "text/event-stream", "cache-control": "no-cache" });
11006
11348
  response.flush();
@@ -11029,8 +11371,8 @@ data: ${JSON.stringify(envelope)}
11029
11371
  sink.retire();
11030
11372
  response.end();
11031
11373
  };
11032
- const publish = (session, source, payload) => {
11033
- const uuid = nonEmptyString2(payload, "uuid");
11374
+ const publish = (session, source, payload, toWorker = source === RC_SOURCE_CLIENT) => {
11375
+ const uuid = nonEmptyString3(payload, "uuid");
11034
11376
  const duplicate = uuid !== void 0 && session.seenPayloadUuids.has(uuid);
11035
11377
  session.sequenceNum += 1;
11036
11378
  const event = {
@@ -11038,9 +11380,9 @@ data: ${JSON.stringify(envelope)}
11038
11380
  eventId: uuid ?? deps.newUuid(),
11039
11381
  source,
11040
11382
  // 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.
11041
- payload: source === RC_SOURCE_CLIENT ? { ...payload, server_received_wall_ms: deps.now() } : payload,
11383
+ payload: source === RC_SOURCE_WORKER ? payload : { ...payload, server_received_wall_ms: deps.now() },
11042
11384
  createdAt: new Date(deps.now()).toISOString(),
11043
- toWorker: source === RC_SOURCE_CLIENT,
11385
+ toWorker,
11044
11386
  storedAt: deps.now()
11045
11387
  };
11046
11388
  session.events.push(event);
@@ -11088,8 +11430,26 @@ data: ${JSON.stringify(envelope)}
11088
11430
  }
11089
11431
  session.lastTrafficAt = deps.now();
11090
11432
  if (tail === "") {
11433
+ if (method === "PATCH" || method === "PUT") {
11434
+ const refusal = credentialRefusal(request4.headers);
11435
+ if (refusal !== void 0) {
11436
+ await unauthorized(response, refusal);
11437
+ return;
11438
+ }
11439
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11440
+ const title = nonEmptyString3(body, "title");
11441
+ if (title === void 0) {
11442
+ await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a session update is JSON naming a non-empty title" } });
11443
+ return;
11444
+ }
11445
+ session.title = title;
11446
+ session.lastTrafficAt = deps.now();
11447
+ deps.log?.(`rc selfhost ${session.id}: retitled at its client's request`);
11448
+ await answerJson2(response, HTTP_STATUS.ok, {});
11449
+ return;
11450
+ }
11091
11451
  if (method !== "GET") {
11092
- await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the session read is a GET" } });
11452
+ 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" } });
11093
11453
  return;
11094
11454
  }
11095
11455
  await answerJson2(response, HTTP_STATUS.ok, { session: { id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), status: "active" } });
@@ -11134,17 +11494,17 @@ data: ${JSON.stringify(envelope)}
11134
11494
  return;
11135
11495
  }
11136
11496
  if (workerTail === "" && method === "PUT") {
11137
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11138
- const claimedEpoch = numberOf(body, "worker_epoch");
11497
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11498
+ const claimedEpoch = numberOf2(body, "worker_epoch");
11139
11499
  if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
11140
11500
  await epochStale(response);
11141
11501
  return;
11142
11502
  }
11143
- const status = nonEmptyString2(body, "worker_status");
11503
+ const status = nonEmptyString3(body, "worker_status");
11144
11504
  if (status !== void 0) {
11145
11505
  session.workerStatus = status;
11146
11506
  }
11147
- if (isRecord6(body?.external_metadata)) {
11507
+ if (isRecord8(body?.external_metadata)) {
11148
11508
  session.externalMetadata = body.external_metadata;
11149
11509
  }
11150
11510
  await answerJson2(response, HTTP_STATUS.ok, {});
@@ -11161,8 +11521,8 @@ data: ${JSON.stringify(envelope)}
11161
11521
  return;
11162
11522
  }
11163
11523
  if (workerTail === "events" && method === "POST") {
11164
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11165
- const claimedEpoch = numberOf(body, "worker_epoch");
11524
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11525
+ const claimedEpoch = numberOf2(body, "worker_epoch");
11166
11526
  if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
11167
11527
  await epochStale(response);
11168
11528
  return;
@@ -11174,7 +11534,7 @@ data: ${JSON.stringify(envelope)}
11174
11534
  }
11175
11535
  const results = [];
11176
11536
  for (const event of events) {
11177
- if (!isRecord6(event) || !isRecord6(event.payload)) {
11537
+ if (!isRecord8(event) || !isRecord8(event.payload)) {
11178
11538
  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" } });
11179
11539
  return;
11180
11540
  }
@@ -11195,8 +11555,8 @@ data: ${JSON.stringify(envelope)}
11195
11555
  return;
11196
11556
  }
11197
11557
  if (workerTail === "internal-events" && method === "POST") {
11198
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11199
- const claimedEpoch = numberOf(body, "worker_epoch");
11558
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11559
+ const claimedEpoch = numberOf2(body, "worker_epoch");
11200
11560
  if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
11201
11561
  await epochStale(response);
11202
11562
  return;
@@ -11207,19 +11567,26 @@ data: ${JSON.stringify(envelope)}
11207
11567
  return;
11208
11568
  }
11209
11569
  for (const event of events) {
11210
- if (isRecord6(event)) {
11211
- session.internalEvents.push({ cursor: deps.newUuid(), body: event });
11570
+ if (isRecord8(event)) {
11571
+ session.internalEvents.push({ eventId: deps.newUuid(), body: event });
11212
11572
  }
11213
11573
  }
11214
11574
  await answerJson2(response, HTTP_STATUS.ok, {});
11215
11575
  return;
11216
11576
  }
11217
11577
  if (workerTail === "internal-events" && method === "GET") {
11218
- const url = new URL(request4.url, "http://127.0.0.1");
11219
- const cursor = url.searchParams.get("cursor");
11220
- const from = cursor === null ? 0 : session.internalEvents.findIndex((event) => event.cursor === cursor) + 1;
11221
- const page = session.internalEvents.slice(from);
11222
- await answerJson2(response, HTTP_STATUS.ok, { data: page.map((event) => event.body), next_cursor: page.length === 0 ? void 0 : page[page.length - 1]?.cursor });
11578
+ const query = new URL(request4.url, "http://127.0.0.1").searchParams;
11579
+ if (query.get("session_agent_id") !== null || query.get("subagents") !== null) {
11580
+ 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" } });
11581
+ return;
11582
+ }
11583
+ const page = pageOfRcChannel(request4.url, session.internalEvents, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.body }));
11584
+ if ("error" in page) {
11585
+ const anchorUnknown = query.get("after_event_id") !== null && page.error.startsWith("the anchor names an event id");
11586
+ await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: anchorUnknown ? "after_event_id_not_found" : "invalid_request_error", message: page.error } });
11587
+ return;
11588
+ }
11589
+ await answerJson2(response, HTTP_STATUS.ok, page);
11223
11590
  return;
11224
11591
  }
11225
11592
  if (workerTail === "diagnostics" && method === "POST") {
@@ -11228,8 +11595,8 @@ data: ${JSON.stringify(envelope)}
11228
11595
  return;
11229
11596
  }
11230
11597
  if (workerTail === "web-fetch" && method === "POST") {
11231
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11232
- const target = nonEmptyString2(body, "url");
11598
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11599
+ const target = nonEmptyString3(body, "url");
11233
11600
  if (target === void 0) {
11234
11601
  await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-fetch carries one url" } });
11235
11602
  return;
@@ -11251,8 +11618,8 @@ data: ${JSON.stringify(envelope)}
11251
11618
  return;
11252
11619
  }
11253
11620
  if (workerTail === "web-search" && method === "POST") {
11254
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11255
- const query = nonEmptyString2(body, "query");
11621
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11622
+ const query = nonEmptyString3(body, "query");
11256
11623
  if (query === void 0) {
11257
11624
  await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-search carries one query" } });
11258
11625
  return;
@@ -11263,7 +11630,7 @@ data: ${JSON.stringify(envelope)}
11263
11630
  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" } });
11264
11631
  return;
11265
11632
  }
11266
- const searchProfile = nonEmptyString2(body, "search_profile");
11633
+ const searchProfile = nonEmptyString3(body, "search_profile");
11267
11634
  const backend = deps.webSearch;
11268
11635
  if (backend === void 0) {
11269
11636
  deps.log?.(`rc selfhost ${session.id}: worker web-search of "${query}" refused: no search backend serves this door`);
@@ -11288,7 +11655,7 @@ data: ${JSON.stringify(envelope)}
11288
11655
  return;
11289
11656
  }
11290
11657
  if (tail === "events" && method === "POST") {
11291
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11658
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11292
11659
  const events = body?.events;
11293
11660
  if (!Array.isArray(events) || events.length === 0 || events.length > RC_SELF_HOST_MAX_BATCH_EVENTS) {
11294
11661
  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` } });
@@ -11296,7 +11663,7 @@ data: ${JSON.stringify(envelope)}
11296
11663
  }
11297
11664
  const results = [];
11298
11665
  for (const event of events) {
11299
- if (!isRecord6(event) || !isRecord6(event.payload)) {
11666
+ if (!isRecord8(event) || !isRecord8(event.payload)) {
11300
11667
  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" } });
11301
11668
  return;
11302
11669
  }
@@ -11308,26 +11675,14 @@ data: ${JSON.stringify(envelope)}
11308
11675
  return;
11309
11676
  }
11310
11677
  if (tail === "events" && method === "GET") {
11311
- 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 })) });
11678
+ 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 })) });
11312
11679
  return;
11313
11680
  }
11314
11681
  if (tail === "events/stream" && method === "GET") {
11315
11682
  await holdStream(session, "clientStreams", () => true, request4, response);
11316
11683
  return;
11317
11684
  }
11318
- if (tail === "client/presence" && method === "POST") {
11319
- await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
11320
- await answerJson2(response, HTTP_STATUS.ok, { refresh_after_seconds: RC_PRESENCE_REFRESH_SECONDS });
11321
- return;
11322
- }
11323
- if (tail === "mark_read" && method === "POST") {
11324
- await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
11325
- await answerJson2(response, HTTP_STATUS.ok, {});
11326
- return;
11327
- }
11328
- if (tail === "teleport-events" && method === "POST") {
11329
- await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
11330
- await answerJson2(response, HTTP_STATUS.ok, {});
11685
+ 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)) {
11331
11686
  return;
11332
11687
  }
11333
11688
  await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no path "${tail}"` } });
@@ -11344,17 +11699,8 @@ data: ${JSON.stringify(envelope)}
11344
11699
  await noCredential(response);
11345
11700
  return;
11346
11701
  }
11347
- if (pathname === RC_COMPAT_SESSIONS_PATH) {
11348
- if (method !== "GET") {
11349
- await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
11350
- return;
11351
- }
11352
- const listRefusal = credentialRefusal(request4.headers);
11353
- if (listRefusal !== void 0) {
11354
- await unauthorized(response, listRefusal);
11355
- return;
11356
- }
11357
- await answerJson2(response, HTTP_STATUS.ok, { data: [] });
11702
+ if (pathname === RC_COMPAT_SESSIONS_PATH || pathname.startsWith(`${RC_COMPAT_SESSIONS_PATH}/`)) {
11703
+ 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);
11358
11704
  return;
11359
11705
  }
11360
11706
  if (pathname !== RC_SESSIONS_PATH_PREFIX2 && !pathname.startsWith(`${RC_SESSIONS_PATH_PREFIX2}/`)) {
@@ -11368,11 +11714,11 @@ data: ${JSON.stringify(envelope)}
11368
11714
  await unauthorized(response, createRefusal);
11369
11715
  return;
11370
11716
  }
11371
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11717
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11372
11718
  const session = {
11373
11719
  id: `${RC_SESSION_ID_PREFIX2}${deps.newUuid()}`,
11374
11720
  createdAt: deps.now(),
11375
- title: nonEmptyString2(body, "title"),
11721
+ title: nonEmptyString3(body, "title"),
11376
11722
  workerJwts: /* @__PURE__ */ new Set(),
11377
11723
  sequenceNum: 0,
11378
11724
  events: [],
@@ -11380,6 +11726,7 @@ data: ${JSON.stringify(envelope)}
11380
11726
  workerStreams: /* @__PURE__ */ new Set(),
11381
11727
  clientStreams: /* @__PURE__ */ new Set(),
11382
11728
  internalEvents: [],
11729
+ clients: /* @__PURE__ */ new Map(),
11383
11730
  workerStatus: void 0,
11384
11731
  externalMetadata: void 0,
11385
11732
  lastTrafficAt: deps.now()
@@ -11424,7 +11771,7 @@ data: ${JSON.stringify(envelope)}
11424
11771
  const pathname = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
11425
11772
  if (host === RC_SELF_HOST_OAUTH_HOST) {
11426
11773
  if (method === "POST" && pathname === "/v1/oauth/token") {
11427
- const body = parseJsonObject2(await readBody2(request4, CONTROL_BODY_CAP_BYTES));
11774
+ const body = parseJsonObject3(await readBody2(request4, CONTROL_BODY_CAP_BYTES));
11428
11775
  const record = deps.credentialRecord();
11429
11776
  if (record === void 0) {
11430
11777
  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" });
@@ -11510,7 +11857,7 @@ var RC_SELF_HOST_RECORD_DIR = "rc-selfhost";
11510
11857
  var RC_SELF_HOST_RECORD_FILE = "credential.json";
11511
11858
  var SELF_HOST_EMAIL = "selfhost@agent-shim.invalid";
11512
11859
  var SELF_HOST_SUBSCRIPTION_TYPE = "max";
11513
- function isRecord7(value) {
11860
+ function isRecord9(value) {
11514
11861
  return typeof value === "object" && value !== null && !Array.isArray(value);
11515
11862
  }
11516
11863
  function readJsonObject(fs12, file) {
@@ -11524,7 +11871,7 @@ function readJsonObject(fs12, file) {
11524
11871
  } catch {
11525
11872
  return void 0;
11526
11873
  }
11527
- return isRecord7(parsed) ? parsed : void 0;
11874
+ return isRecord9(parsed) ? parsed : void 0;
11528
11875
  }
11529
11876
  function readRcSelfHostRecord(fs12, frontdoorDir) {
11530
11877
  const parsed = readJsonObject(fs12, import_node_path28.default.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
@@ -11548,7 +11895,7 @@ function mintRcSelfHostCredential(deps) {
11548
11895
  const recordFile = import_node_path28.default.join(recordDir, RC_SELF_HOST_RECORD_FILE);
11549
11896
  const previousRecord = readRcSelfHostRecord(deps.fs, deps.frontdoorDir);
11550
11897
  const existingCredentials = readJsonObject(deps.fs, credentialsFile);
11551
- const existingOauth = isRecord7(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
11898
+ const existingOauth = isRecord9(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
11552
11899
  const existingToken = typeof existingOauth?.accessToken === "string" && existingOauth.accessToken !== "" ? existingOauth.accessToken : void 0;
11553
11900
  const existingIsOwn = existingToken !== void 0 && previousRecord?.accessToken === existingToken;
11554
11901
  if (existingToken !== void 0 && !existingIsOwn && !deps.force) {
@@ -11565,7 +11912,7 @@ function mintRcSelfHostCredential(deps) {
11565
11912
  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)}
11566
11913
  `);
11567
11914
  const claudeJson = readJsonObject(deps.fs, claudeJsonFile) ?? {};
11568
- const existingBlock = (value) => isRecord7(value) ? value : {};
11915
+ const existingBlock = (value) => isRecord9(value) ? value : {};
11569
11916
  deps.fs.writeFilePrivate(claudeJsonFile, `${JSON.stringify({
11570
11917
  ...claudeJson,
11571
11918
  oauthAccount: {
@@ -11764,7 +12111,7 @@ var import_node_path30 = __toESM(require("node:path"), 1);
11764
12111
  // package.json
11765
12112
  var package_default = {
11766
12113
  name: "agent-shim",
11767
- version: "8.24.0",
12114
+ version: "8.26.0",
11768
12115
  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
12116
  license: "MIT",
11770
12117
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -13028,11 +13375,15 @@ function prepareClaudeLaunch(options) {
13028
13375
  RC_WEB_FETCH_MAX_BYTES,
13029
13376
  RC_WEB_FETCH_MAX_REDIRECTS,
13030
13377
  ROUTED_PATH_PREFIX,
13378
+ RcLiveRateLimitSchema,
13031
13379
  RcPermissionModeSchema,
13380
+ RcRateLimitInfoSchema,
13381
+ RcRateLimitWindowSchema,
13032
13382
  RcStreamEnvelopeSchema,
13033
13383
  RcStreamEventSchema,
13034
13384
  UsageError,
13035
13385
  UsageListOutputSchema,
13386
+ UsageLiveOutputSchema,
13036
13387
  UsageSnapshotError,
13037
13388
  UsageSnapshotSchema,
13038
13389
  UsageWindowsInputSchema,
@@ -13055,6 +13406,7 @@ function prepareClaudeLaunch(options) {
13055
13406
  buildRcGetUsagePayload,
13056
13407
  buildRcInterruptPayload,
13057
13408
  buildRcKeepAlivePayload,
13409
+ buildRcMarkReadBody,
13058
13410
  buildRcMcpAuthenticatePayload,
13059
13411
  buildRcMcpOAuthCallbackUrlPayload,
13060
13412
  buildRcMcpReconnectPayload,
@@ -13083,6 +13435,7 @@ function prepareClaudeLaunch(options) {
13083
13435
  createRcControlHandler,
13084
13436
  createRcCredentialStore,
13085
13437
  createRcEventFanout,
13438
+ createRcLiveUsage,
13086
13439
  createRcSelfHostSurface,
13087
13440
  createRcSessionTracker,
13088
13441
  createRcStreamHub,
@@ -13144,6 +13497,7 @@ function prepareClaudeLaunch(options) {
13144
13497
  rcEventWriteResultFromAnswer,
13145
13498
  rcFanoutOnDoorHub,
13146
13499
  rcObserverAsPassthrough,
13500
+ rcRateLimitInfoOf,
13147
13501
  rcSelfHostFromEnv,
13148
13502
  rcWebFetchAllowsPrivateFromEnv,
13149
13503
  readActiveIdentity,
@@ -13201,6 +13555,7 @@ function prepareClaudeLaunch(options) {
13201
13555
  startConnectServer,
13202
13556
  submitRcSessionMcpOAuthCallbackUrl,
13203
13557
  suggestRcSessionFiles,
13558
+ teleportRcSession,
13204
13559
  topLevelNames,
13205
13560
  updateDirectoryRule,
13206
13561
  updateProvider,