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.mjs CHANGED
@@ -3207,9 +3207,6 @@ var HTTP_STATUS = {
3207
3207
  badGateway: 502
3208
3208
  };
3209
3209
 
3210
- // src/frontdoor/rcControl.ts
3211
- import * as https from "node:https";
3212
-
3213
3210
  // src/frontdoor/rcSessions.ts
3214
3211
  var RC_SESSIONS_PATH_PREFIX = "/v1/code/sessions";
3215
3212
  var RC_SESSION_ID_PREFIX = "cse_";
@@ -3807,7 +3804,7 @@ async function answerRcControlRequest(deps, sessionId, requestId, decision) {
3807
3804
  }
3808
3805
  return result;
3809
3806
  }
3810
- async function deliverRcClientPayload(deps, sessionId, payload, action) {
3807
+ async function deliverRcClientPayload(deps, sessionId, payload, action, to = "events") {
3811
3808
  const credential = deps.credentialOf(sessionId);
3812
3809
  if (credential === void 0) {
3813
3810
  return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
@@ -3818,7 +3815,7 @@ async function deliverRcClientPayload(deps, sessionId, payload, action) {
3818
3815
  const body = JSON.stringify(buildRcEventWriteBody(payload));
3819
3816
  let answer;
3820
3817
  try {
3821
- answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3818
+ answer = await (to === "teleport-events" ? deps.dial.writeTeleportEvents(sessionId, rcWriteHeaders(credential), body) : deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body));
3822
3819
  } catch (error) {
3823
3820
  return { ok: false, message: `the door could not reach the API host to ${action}: ${error instanceof Error ? error.message : String(error)}` };
3824
3821
  }
@@ -3912,8 +3909,18 @@ async function submitRcSessionMcpOAuthCallbackUrl(deps, sessionId, serverName, c
3912
3909
  async function sendRcKeepAlive(deps, sessionId) {
3913
3910
  return await deliverRcClientPayload(deps, sessionId, buildRcKeepAlivePayload(), `keep session ${sessionId} alive`);
3914
3911
  }
3912
+ async function teleportRcSession(deps, sessionId, marker) {
3913
+ if (marker === "") {
3914
+ return { ok: false, message: "a teleport write needs a non-empty marker: the teleport relay anchors on a marker line's uuid and string content, and an empty string carries neither" };
3915
+ }
3916
+ return await deliverRcClientPayload(deps, sessionId, buildRcUserMessagePayload(deps.newUuid(), sessionId, marker), `teleport a marker into session ${sessionId}`, "teleport-events");
3917
+ }
3918
+ function buildRcMarkReadBody(eventId) {
3919
+ return eventId === void 0 ? {} : { event_id: eventId };
3920
+ }
3915
3921
 
3916
3922
  // src/frontdoor/rcControl.ts
3923
+ import * as https from "node:https";
3917
3924
  var RC_PERMISSION_MODE_LIST = RC_PERMISSION_MODES.join(", ");
3918
3925
  var CONTROL_PATH_PREFIX = "/__agent-shim/rc";
3919
3926
  var CONTROL_BODY_CAP_BYTES = 10485760;
@@ -4087,6 +4094,15 @@ function createRcControlHandler(deps) {
4087
4094
  const callbackUrl = bodyString(body, "callbackUrl");
4088
4095
  return session === void 0 || serverName === void 0 || callbackUrl === void 0 ? { error: "the mcp-oauth-callback-url body must be JSON naming a non-empty session id, server name, and callback URL" } : { session, run: async () => await deps.mcpOAuthCallbackUrl(session, serverName, callbackUrl) };
4089
4096
  }
4097
+ },
4098
+ {
4099
+ name: "teleport",
4100
+ path: "teleport",
4101
+ read: (body) => {
4102
+ const session = bodyString(body, "session");
4103
+ const marker = bodyString(body, "marker");
4104
+ return session === void 0 || marker === void 0 ? { error: "the teleport body must be JSON naming a non-empty session id and marker" } : { session, run: async () => await deps.teleport(session, marker) };
4105
+ }
4090
4106
  }
4091
4107
  ];
4092
4108
  return (request4, response) => {
@@ -4450,7 +4466,8 @@ function frontDoorRcControl(transport, token) {
4450
4466
  mcpStatus: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-status`, JSON.stringify({ session: sessionId }), (status, message) => `requesting the MCP status of ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4451
4467
  mcpReconnect: async (sessionId, serverName) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-reconnect`, JSON.stringify({ session: sessionId, serverName }), (status, message) => `reconnecting MCP server ${serverName} on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4452
4468
  mcpAuthenticate: async (sessionId, serverName, redirectUri) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-authenticate`, JSON.stringify({ session: sessionId, serverName, redirectUri }), (status, message) => `authenticating MCP server ${serverName} on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4453
- 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}`)
4469
+ mcpOAuthCallbackUrl: async (sessionId, serverName, callbackUrl) => await writeOf(`${CONTROL_PATH_PREFIX}/mcp-oauth-callback-url`, JSON.stringify({ session: sessionId, serverName, callbackUrl }), (status, message) => `handing MCP server ${serverName}'s OAuth callback to ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4470
+ teleport: async (sessionId, marker) => await writeOf(`${CONTROL_PATH_PREFIX}/teleport`, JSON.stringify({ session: sessionId, marker }), (status, message) => `teleporting a marker into ${sessionId} failed (HTTP ${String(status)}): ${message}`)
4454
4471
  };
4455
4472
  }
4456
4473
 
@@ -4597,36 +4614,38 @@ function tlsFacts(options) {
4597
4614
  }
4598
4615
  function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
4599
4616
  const agent = new ExemptTlsAgent();
4600
- return {
4601
- writeEvents: async (sessionId, headers, body) => await new Promise((resolve, reject) => {
4602
- const options = {
4603
- host: target.host,
4604
- port: target.port,
4605
- method: "POST",
4606
- path: `/v1/code/sessions/${encodeURIComponent(sessionId)}/events`,
4607
- // 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.
4608
- headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
4609
- agent
4610
- };
4611
- options.servername = target.servername ?? target.host;
4612
- if (target.ca !== void 0) {
4613
- options.ca = [...target.ca];
4614
- }
4615
- if (target.rejectUnauthorized !== void 0) {
4616
- options.rejectUnauthorized = target.rejectUnauthorized;
4617
- }
4618
- const request4 = https2.request(options, (response) => {
4619
- const chunks = [];
4620
- response.on("data", (chunk) => {
4621
- chunks.push(chunk);
4622
- });
4623
- response.on("end", () => {
4624
- resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
4625
- });
4617
+ const post = async (path32, headers, body) => await new Promise((resolve, reject) => {
4618
+ const options = {
4619
+ host: target.host,
4620
+ port: target.port,
4621
+ method: "POST",
4622
+ path: path32,
4623
+ // The Host header names the host whose API this is, not the address dialled: a redirected test dials a loopback stand-in while still speaking to the API host by name, exactly as the forwarded paths present their SNI.
4624
+ headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
4625
+ agent
4626
+ };
4627
+ options.servername = target.servername ?? target.host;
4628
+ if (target.ca !== void 0) {
4629
+ options.ca = [...target.ca];
4630
+ }
4631
+ if (target.rejectUnauthorized !== void 0) {
4632
+ options.rejectUnauthorized = target.rejectUnauthorized;
4633
+ }
4634
+ const request4 = https2.request(options, (response) => {
4635
+ const chunks = [];
4636
+ response.on("data", (chunk) => {
4637
+ chunks.push(chunk);
4626
4638
  });
4627
- request4.once("error", reject);
4628
- request4.end(body);
4629
- })
4639
+ response.on("end", () => {
4640
+ resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
4641
+ });
4642
+ });
4643
+ request4.once("error", reject);
4644
+ request4.end(body);
4645
+ });
4646
+ return {
4647
+ writeEvents: async (sessionId, headers, body) => await post(`/v1/code/sessions/${encodeURIComponent(sessionId)}/events`, headers, body),
4648
+ writeTeleportEvents: async (sessionId, headers, body) => await post(`/v1/code/sessions/${encodeURIComponent(sessionId)}/teleport-events`, headers, body)
4630
4649
  };
4631
4650
  }
4632
4651
  var NO_CHUNKS = {
@@ -4644,7 +4663,8 @@ function lateRcEventDial(resolve) {
4644
4663
  return built.dial;
4645
4664
  };
4646
4665
  return {
4647
- writeEvents: async (sessionId, headers, body) => await dialFor().writeEvents(sessionId, headers, body)
4666
+ writeEvents: async (sessionId, headers, body) => await dialFor().writeEvents(sessionId, headers, body),
4667
+ writeTeleportEvents: async (sessionId, headers, body) => await dialFor().writeTeleportEvents(sessionId, headers, body)
4648
4668
  };
4649
4669
  }
4650
4670
  function lateRcStreamDial(resolve) {
@@ -4657,7 +4677,8 @@ function lateRcStreamDial(resolve) {
4657
4677
  return built.dial;
4658
4678
  };
4659
4679
  return {
4660
- announcePresence: async (sessionId, headers, clientId) => await dialFor().announcePresence(sessionId, headers, clientId),
4680
+ announcePresence: async (sessionId, headers, clientId, clear) => await dialFor().announcePresence(sessionId, headers, clientId, clear),
4681
+ markRead: async (sessionId, headers, eventId) => await dialFor().markRead(sessionId, headers, eventId),
4661
4682
  openStream: async (sessionId, headers, resume, signal) => await dialFor().openStream(sessionId, headers, resume, signal)
4662
4683
  };
4663
4684
  }
@@ -4699,11 +4720,16 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
4699
4720
  return Buffer.concat(chunks).toString("utf8");
4700
4721
  };
4701
4722
  return {
4702
- announcePresence: async (sessionId, headers, clientId) => {
4703
- const body = JSON.stringify({ client_id: clientId, clear: false });
4723
+ announcePresence: async (sessionId, headers, clientId, clear) => {
4724
+ const body = JSON.stringify({ client_id: clientId, clear });
4704
4725
  const { status, response } = await request4("POST", `/v1/code/sessions/${encodeURIComponent(sessionId)}/client/presence`, { ...headers, "content-type": "application/json" }, body, new AbortController().signal);
4705
4726
  return { status, body: await readAll(response) };
4706
4727
  },
4728
+ markRead: async (sessionId, headers, eventId) => {
4729
+ const body = JSON.stringify(buildRcMarkReadBody(eventId));
4730
+ const { status, response } = await request4("POST", `/v1/code/sessions/${encodeURIComponent(sessionId)}/mark_read`, { ...headers, "content-type": "application/json" }, body, new AbortController().signal);
4731
+ return { status, body: await readAll(response) };
4732
+ },
4707
4733
  openStream: async (sessionId, headers, resume, signal) => {
4708
4734
  const path32 = resume === void 0 ? `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream` : `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream?from_sequence_num=${String(resume.fromSequenceNum)}`;
4709
4735
  const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
@@ -5048,6 +5074,35 @@ var RcStreamEventSchema = z4.strictObject({
5048
5074
  session: z4.string(),
5049
5075
  envelope: RcStreamEnvelopeSchema
5050
5076
  });
5077
+ var RcRateLimitWindowSchema = z4.looseObject({
5078
+ /** 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). */
5079
+ utilization: z4.number().optional(),
5080
+ /** 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. */
5081
+ resetsAt: z4.number().optional()
5082
+ });
5083
+ var RcRateLimitInfoSchema = z4.looseObject({
5084
+ /** The limiting window's own status (`allowed`, `allowed_warning`, `rejected`), asserted only as a string because the vocabulary grows with the CLI. */
5085
+ status: z4.string(),
5086
+ /** When the limiting window resets, in unix epoch seconds. */
5087
+ resetsAt: z4.number().optional(),
5088
+ /** Which window the top-level fields describe (`five_hour`, `seven_day`, and kin), the payload's own name for it. */
5089
+ rateLimitType: z4.string().optional(),
5090
+ /** The fraction of the limiting window used, on the same scale the per-window utilizations carry. */
5091
+ utilization: z4.number().optional(),
5092
+ /** Whether usage beyond the plan (extra usage) is currently covering the overflow, the payload's own flag for it. */
5093
+ isUsingOverage: z4.boolean().optional(),
5094
+ /** The overage's own status, the same vocabulary the limiting window's `status` carries. */
5095
+ overageStatus: z4.string().optional(),
5096
+ /** When the overage window resets, in unix epoch seconds. */
5097
+ overageResetsAt: z4.number().optional(),
5098
+ /** 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. */
5099
+ unifiedWindows: z4.looseObject({ five_hour: RcRateLimitWindowSchema.optional(), seven_day: RcRateLimitWindowSchema.optional(), seven_day_overage_included: RcRateLimitWindowSchema.optional() }).optional()
5100
+ });
5101
+ var RcLiveRateLimitSchema = z4.strictObject({
5102
+ session: z4.string(),
5103
+ observedAt: z4.number(),
5104
+ rateLimit: RcRateLimitInfoSchema
5105
+ });
5051
5106
  var RcSessionQuerySchema = z4.strictObject({
5052
5107
  session: z4.string().min(1).optional()
5053
5108
  });
@@ -5114,6 +5169,10 @@ var RcMcpOAuthCallbackUrlInputSchema = z4.strictObject({
5114
5169
  serverName: z4.string().min(1),
5115
5170
  callbackUrl: z4.string().min(1)
5116
5171
  });
5172
+ var RcTeleportInputSchema = z4.strictObject({
5173
+ session: z4.string().min(1),
5174
+ marker: z4.string().min(1)
5175
+ });
5117
5176
  var RcListOutputSchema = z4.strictObject({ sessions: z4.readonly(z4.array(RcSessionSummarySchema)) });
5118
5177
  var RcStatusOutputSchema = z4.strictObject({ statuses: z4.readonly(z4.array(RcSessionStatusSchema)) });
5119
5178
  var RcPendingOutputSchema = z4.strictObject({ pending: z4.readonly(z4.array(RcPendingRequestSummarySchema)) });
@@ -5198,6 +5257,7 @@ function createRcApiRouter(deps) {
5198
5257
  mcpReconnect: authed.input(RcMcpReconnectInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpReconnect(input.session, input.serverName)) })),
5199
5258
  mcpAuthenticate: authed.input(RcMcpAuthenticateInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpAuthenticate(input.session, input.serverName, input.redirectUri)) })),
5200
5259
  mcpOAuthCallbackUrl: authed.input(RcMcpOAuthCallbackUrlInputSchema).output(RcControlWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, ...deliveredControl(input.session, await deps.mcpOAuthCallbackUrl(input.session, input.serverName, input.callbackUrl)) })),
5260
+ teleport: authed.input(RcTeleportInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.teleport(input.session, input.marker)) })),
5201
5261
  subscribe: authed.input(RcSessionQuerySchema).output(eventIterator(RcStreamEventSchema)).handler(async function* ({ input, signal }) {
5202
5262
  const queue = [];
5203
5263
  let wake;
@@ -9653,6 +9713,12 @@ var UsageWindowsOutputSchema = z19.strictObject({
9653
9713
  fiveHour: EffectiveWindowSchema.optional(),
9654
9714
  sevenDay: EffectiveWindowSchema.optional()
9655
9715
  });
9716
+ var UsageLiveOutputSchema = z19.strictObject({
9717
+ /** 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. */
9718
+ latest: RcLiveRateLimitSchema.optional(),
9719
+ /** One observation per session whose stream has filed a rate_limit_event this door generation. */
9720
+ sessions: z19.readonly(z19.array(RcLiveRateLimitSchema))
9721
+ });
9656
9722
  var FrontDoorSessionStatusSchema = z19.strictObject({
9657
9723
  pid: z19.int().positive(),
9658
9724
  startedAt: z19.number(),
@@ -9767,6 +9833,16 @@ function createControlApiRouter(deps) {
9767
9833
  ...unified?.fiveHour === void 0 ? {} : { fiveHour: effectiveWindow(unified.fiveHour, now) },
9768
9834
  ...unified?.sevenDay === void 0 ? {} : { sevenDay: effectiveWindow(unified.sevenDay, now) }
9769
9835
  };
9836
+ }),
9837
+ live: authed.input(RcSessionQuerySchema).output(UsageLiveOutputSchema).handler(({ input }) => {
9838
+ const sessions = deps.liveRateLimits(input.session);
9839
+ if (input.session !== void 0 && sessions.length === 0) {
9840
+ if (deps.list().some((session) => session.id === input.session)) {
9841
+ throw new ORPCError3("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` });
9842
+ }
9843
+ throw new ORPCError3("NOT_FOUND", { message: rcSessionNotObservedMessage(input.session) });
9844
+ }
9845
+ return { latest: deps.latestRateLimit(), sessions };
9770
9846
  })
9771
9847
  },
9772
9848
  frontdoor: {
@@ -9987,6 +10063,42 @@ function createLaunchEventPublisher(hub, now) {
9987
10063
  };
9988
10064
  }
9989
10065
 
10066
+ // src/frontdoor/rcLiveUsage.ts
10067
+ var RC_RATE_LIMIT_EVENT_TYPE = "rate_limit_event";
10068
+ function isRecord5(value) {
10069
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10070
+ }
10071
+ function rcRateLimitInfoOf(event) {
10072
+ const payload = event.envelope.payload;
10073
+ if (!isRecord5(payload) || payload.type !== RC_RATE_LIMIT_EVENT_TYPE) {
10074
+ return void 0;
10075
+ }
10076
+ if (!isRecord5(payload.rate_limit_info)) {
10077
+ return void 0;
10078
+ }
10079
+ const parsed = RcRateLimitInfoSchema.safeParse(payload.rate_limit_info);
10080
+ return parsed.success ? parsed.data : void 0;
10081
+ }
10082
+ function createRcLiveUsage(deps) {
10083
+ const observed = /* @__PURE__ */ new Map();
10084
+ deps.hub.subscribe([DOOR_EVENT_SOURCE_RC], (event) => {
10085
+ const stream = RcStreamEventSchema.safeParse(event.payload);
10086
+ if (!stream.success) {
10087
+ return;
10088
+ }
10089
+ const rateLimit = rcRateLimitInfoOf(stream.data);
10090
+ if (rateLimit === void 0) {
10091
+ return;
10092
+ }
10093
+ observed.set(stream.data.session, { session: stream.data.session, observedAt: deps.now(), rateLimit });
10094
+ });
10095
+ const sorted = () => [...observed.values()].sort((left, right) => left.observedAt - right.observedAt || (left.session < right.session ? -1 : 1));
10096
+ return {
10097
+ liveOf: (sessionId) => sorted().filter((entry) => sessionId === void 0 || entry.session === sessionId),
10098
+ latestOf: () => sorted().at(-1)
10099
+ };
10100
+ }
10101
+
9990
10102
  // src/frontdoor/rcStream.ts
9991
10103
  var RC_STREAM_EVENT_NAME = "client_event";
9992
10104
  var SSE_BYTE_ORDER_MARK = 65279;
@@ -10049,7 +10161,7 @@ function createSseParser() {
10049
10161
  }
10050
10162
  };
10051
10163
  }
10052
- function isRecord5(value) {
10164
+ function isRecord6(value) {
10053
10165
  return typeof value === "object" && value !== null && !Array.isArray(value);
10054
10166
  }
10055
10167
  function sequenceNumOf(value) {
@@ -10071,7 +10183,7 @@ function parseRcStreamEnvelope(parsed) {
10071
10183
  } catch {
10072
10184
  return void 0;
10073
10185
  }
10074
- if (!isRecord5(data)) {
10186
+ if (!isRecord6(data)) {
10075
10187
  return void 0;
10076
10188
  }
10077
10189
  const eventType = data.event_type;
@@ -10154,9 +10266,26 @@ function createRcStreamHub(deps) {
10154
10266
  }
10155
10267
  return headers;
10156
10268
  };
10269
+ let receiptFailureLogged = false;
10270
+ const sendReceipt = (attachment, headers, eventId) => {
10271
+ if (eventId === void 0) {
10272
+ return;
10273
+ }
10274
+ deps.dial.markRead(attachment.sessionId, headers, eventId).then(
10275
+ () => {
10276
+ receiptFailureLogged = false;
10277
+ },
10278
+ (error) => {
10279
+ if (!receiptFailureLogged) {
10280
+ receiptFailureLogged = true;
10281
+ 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`);
10282
+ }
10283
+ }
10284
+ );
10285
+ };
10157
10286
  const attempt = async (attachment, credential) => {
10158
10287
  const headers = streamHeaders(credential);
10159
- const presence = await deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId);
10288
+ const presence = await deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId, false);
10160
10289
  if (presence.status === HTTP_STATUS.unauthorized) {
10161
10290
  return "unauthorized";
10162
10291
  }
@@ -10176,6 +10305,7 @@ function createRcStreamHub(deps) {
10176
10305
  const parser = createSseParser();
10177
10306
  for await (const chunk of answer.chunks) {
10178
10307
  attachment.controller.signal.throwIfAborted();
10308
+ let lastEventId;
10179
10309
  for (const parsed of parser.write(Buffer.from(chunk).toString("utf8"))) {
10180
10310
  const envelope = parseRcStreamEnvelope(parsed);
10181
10311
  if (envelope === void 0) {
@@ -10183,10 +10313,21 @@ function createRcStreamHub(deps) {
10183
10313
  }
10184
10314
  deps.fileStreamEvent(attachment.sessionId, envelope);
10185
10315
  deps.fanout.publish({ session: attachment.sessionId, envelope });
10316
+ if (typeof envelope.event_id === "string" && envelope.event_type !== "mark_read") {
10317
+ lastEventId = envelope.event_id;
10318
+ }
10186
10319
  }
10320
+ sendReceipt(attachment, headers, lastEventId);
10187
10321
  }
10188
10322
  return "dropped";
10189
10323
  };
10324
+ const clearPresence = (attachment, headers) => {
10325
+ if (headers === void 0) {
10326
+ return;
10327
+ }
10328
+ void deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId, true).catch(() => {
10329
+ });
10330
+ };
10190
10331
  const runLoop = async (attachment) => {
10191
10332
  let retriedUnauthorized = false;
10192
10333
  for (; ; ) {
@@ -10198,6 +10339,7 @@ function createRcStreamHub(deps) {
10198
10339
  if (!live || credential?.authorization === void 0) {
10199
10340
  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"})`);
10200
10341
  persistCursor(attachment.sessionId);
10342
+ clearPresence(attachment, credential === void 0 ? void 0 : streamHeaders(credential));
10201
10343
  if (attachments.get(attachment.sessionId) === attachment) {
10202
10344
  attachments.delete(attachment.sessionId);
10203
10345
  }
@@ -10260,6 +10402,8 @@ function createRcStreamHub(deps) {
10260
10402
  close: () => {
10261
10403
  for (const attachment of attachments.values()) {
10262
10404
  persistCursor(attachment.sessionId);
10405
+ const credential = deps.credentialOf(attachment.sessionId);
10406
+ clearPresence(attachment, credential?.authorization === void 0 ? void 0 : streamHeaders(credential));
10263
10407
  attachment.stop();
10264
10408
  }
10265
10409
  attachments.clear();
@@ -10267,6 +10411,193 @@ function createRcStreamHub(deps) {
10267
10411
  };
10268
10412
  }
10269
10413
 
10414
+ // src/frontdoor/rcSelfHostEndpoints.ts
10415
+ var RC_SOURCE_WORKER = "worker";
10416
+ var RC_SOURCE_CLIENT = "client";
10417
+ function isRecord7(value) {
10418
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10419
+ }
10420
+ function parseJsonObject2(body) {
10421
+ let parsed;
10422
+ try {
10423
+ parsed = JSON.parse(body);
10424
+ } catch {
10425
+ return void 0;
10426
+ }
10427
+ return isRecord7(parsed) ? parsed : void 0;
10428
+ }
10429
+ function nonEmptyString2(record, field) {
10430
+ const value = record === void 0 ? void 0 : record[field];
10431
+ return typeof value === "string" && value !== "" ? value : void 0;
10432
+ }
10433
+ function numberOf(record, field) {
10434
+ const value = record === void 0 ? void 0 : record[field];
10435
+ if (typeof value === "number" && Number.isFinite(value)) {
10436
+ return value;
10437
+ }
10438
+ if (typeof value === "string" && /^[0-9]+$/.test(value)) {
10439
+ return Number(value);
10440
+ }
10441
+ return void 0;
10442
+ }
10443
+ function pageOfRcChannel(url, rows, idOf, rowOf) {
10444
+ const query = new URL(url, "http://127.0.0.1").searchParams;
10445
+ const anchor = query.get("after_event_id") ?? query.get("cursor");
10446
+ const rawLimit = query.get("limit");
10447
+ const limit = rawLimit === null ? void 0 : numberOf({ value: rawLimit }, "value");
10448
+ if (limit !== void 0 && limit < 1) {
10449
+ return { error: "the limit names a whole number of rows of at least one" };
10450
+ }
10451
+ const from = anchor === null ? 0 : rows.findIndex((row) => idOf(row) === anchor) + 1;
10452
+ if (anchor !== null && from === 0) {
10453
+ return { error: `the anchor names an event id this surface holds no row for: ${anchor}` };
10454
+ }
10455
+ const page = limit === void 0 ? rows.slice(from) : rows.slice(from, from + limit);
10456
+ const last = page[page.length - 1];
10457
+ return { data: page.map(rowOf), ...last === void 0 ? {} : { next_cursor: idOf(last) } };
10458
+ }
10459
+ async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
10460
+ const { request: request4, response, session } = ctx;
10461
+ if (tail === "client/presence" && method === "POST") {
10462
+ const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10463
+ const clientId = nonEmptyString2(body, "client_id");
10464
+ const clear = body?.clear;
10465
+ if (clientId === void 0 || clear !== void 0 && typeof clear !== "boolean") {
10466
+ 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" } });
10467
+ return true;
10468
+ }
10469
+ if (clear === true) {
10470
+ session.clients.delete(clientId);
10471
+ ctx.log?.(`rc selfhost ${session.id}: client ${clientId} cleared its presence`);
10472
+ } else {
10473
+ session.clients.set(clientId, ctx.now());
10474
+ ctx.log?.(`rc selfhost ${session.id}: client ${clientId} announced its presence${session.clients.size === 1 ? "" : ` (${String(session.clients.size)} clients now announced)`}`);
10475
+ }
10476
+ await ctx.answerJson(response, HTTP_STATUS.ok, { refresh_after_seconds: ctx.presenceRefreshSeconds });
10477
+ return true;
10478
+ }
10479
+ if (tail === "mark_read" && method === "POST") {
10480
+ const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10481
+ const eventId = nonEmptyString2(body, "event_id");
10482
+ const clientId = nonEmptyString2(body, "client_id");
10483
+ if (body !== void 0 && "event_id" in body && eventId === void 0 || "client_id" in (body ?? {}) && clientId === void 0) {
10484
+ 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" } });
10485
+ return true;
10486
+ }
10487
+ 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);
10488
+ 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)}`);
10489
+ await ctx.answerJson(response, HTTP_STATUS.ok, {});
10490
+ return true;
10491
+ }
10492
+ if (tail === "teleport-events" && method === "POST") {
10493
+ const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10494
+ const events = body?.events;
10495
+ if (!Array.isArray(events) || events.length === 0 || events.length > maxBatchEvents) {
10496
+ 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` } });
10497
+ return true;
10498
+ }
10499
+ const results = [];
10500
+ for (const event of events) {
10501
+ if (!isRecord7(event) || !isRecord7(event.payload)) {
10502
+ 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" } });
10503
+ return true;
10504
+ }
10505
+ const published = ctx.publish(session, RC_SOURCE_CLIENT, event.payload);
10506
+ results.push({ sequence_num: String(published.sequenceNum), duplicate: published.duplicate });
10507
+ }
10508
+ ctx.sweep();
10509
+ ctx.log?.(`rc selfhost ${session.id}: teleport write published ${String(results.length)} marker event(s)`);
10510
+ await ctx.answerJson(response, HTTP_STATUS.ok, { results });
10511
+ return true;
10512
+ }
10513
+ if (tail === "teleport-events" && method === "GET") {
10514
+ const page = pageOfRcChannel(request4.url, session.events, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.payload }));
10515
+ if ("error" in page) {
10516
+ await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: page.error } });
10517
+ return true;
10518
+ }
10519
+ await ctx.answerJson(response, HTTP_STATUS.ok, page);
10520
+ return true;
10521
+ }
10522
+ return false;
10523
+ }
10524
+ 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" });
10525
+ async function serveRcCompatSessions(ctx, pathname, compatPath) {
10526
+ const { request: request4, response } = ctx;
10527
+ const method = request4.method.toUpperCase();
10528
+ if (pathname === compatPath) {
10529
+ if (method !== "GET") {
10530
+ await ctx.answerJson(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
10531
+ return;
10532
+ }
10533
+ const listRefusal = ctx.credentialRefusal(request4.headers);
10534
+ if (listRefusal !== void 0) {
10535
+ await ctx.unauthorized(response, listRefusal);
10536
+ return;
10537
+ }
10538
+ await ctx.answerJson(response, HTTP_STATUS.ok, { data: [...ctx.sessions.values()].map((session2) => compatSessionRow(session2, ctx.now)) });
10539
+ return;
10540
+ }
10541
+ const rest = pathname.slice(`${compatPath}/`.length);
10542
+ const slash = rest.indexOf("/");
10543
+ const address = slash === -1 ? rest : rest.slice(0, slash);
10544
+ const compatTail = slash === -1 ? "" : rest.slice(slash + 1);
10545
+ const refusal = ctx.credentialRefusal(request4.headers);
10546
+ if (refusal !== void 0) {
10547
+ await ctx.unauthorized(response, refusal);
10548
+ return;
10549
+ }
10550
+ const session = ctx.sessions.get(address) ?? ctx.sessions.get(`cse_${address}`);
10551
+ if (compatTail === "") {
10552
+ if (method === "GET") {
10553
+ if (session === void 0) {
10554
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
10555
+ return;
10556
+ }
10557
+ await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
10558
+ return;
10559
+ }
10560
+ if (method === "PATCH" || method === "PUT") {
10561
+ if (session === void 0) {
10562
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
10563
+ return;
10564
+ }
10565
+ const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10566
+ const title = nonEmptyString2(body, "title");
10567
+ if (title === void 0) {
10568
+ 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" } });
10569
+ return;
10570
+ }
10571
+ session.title = title;
10572
+ session.lastTrafficAt = ctx.now();
10573
+ ctx.log?.(`rc selfhost ${session.id}: retitled through the compatibility family`);
10574
+ await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
10575
+ return;
10576
+ }
10577
+ 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" } });
10578
+ return;
10579
+ }
10580
+ if ((compatTail === "archive" || compatTail === "unarchive") && method === "POST") {
10581
+ await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES);
10582
+ if (session === void 0) {
10583
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `Session not found: ${address}` } });
10584
+ return;
10585
+ }
10586
+ if (compatTail === "unarchive") {
10587
+ await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
10588
+ return;
10589
+ }
10590
+ for (const sink of [...session.workerStreams, ...session.clientStreams]) {
10591
+ sink.retire();
10592
+ }
10593
+ ctx.sessions.delete(session.id);
10594
+ ctx.log?.(`rc selfhost ${session.id}: archived through the compatibility family`);
10595
+ await ctx.answerJson(response, HTTP_STATUS.ok, {});
10596
+ return;
10597
+ }
10598
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the compatibility session family serves no path "${compatTail}"` } });
10599
+ }
10600
+
10270
10601
  // src/frontdoor/rcWebFetch.ts
10271
10602
  import * as dns2 from "node:dns";
10272
10603
  import * as http2 from "node:http";
@@ -10535,21 +10866,19 @@ var RC_PRESENCE_REFRESH_SECONDS = 60;
10535
10866
  var RC_SELF_HOST_MAX_BATCH_EVENTS = 100;
10536
10867
  var RC_SELF_HOST_WORKER_EPOCH = 1;
10537
10868
  var RC_STREAM_EVENT_NAME2 = "client_event";
10538
- var RC_SOURCE_WORKER = "worker";
10539
- var RC_SOURCE_CLIENT = "client";
10540
10869
  var CONFLICT_REASON_HEADER2 = "x-ccr-conflict-reason";
10541
10870
  var CONFLICT_REASON_EPOCH_STALE = "epoch_stale";
10542
- function isRecord6(value) {
10871
+ function isRecord8(value) {
10543
10872
  return typeof value === "object" && value !== null && !Array.isArray(value);
10544
10873
  }
10545
10874
  function isWebSearchRefusal(value) {
10546
10875
  return !Array.isArray(value);
10547
10876
  }
10548
- function nonEmptyString2(record, field) {
10877
+ function nonEmptyString3(record, field) {
10549
10878
  const value = record === void 0 ? void 0 : record[field];
10550
10879
  return typeof value === "string" && value !== "" ? value : void 0;
10551
10880
  }
10552
- function numberOf(record, field) {
10881
+ function numberOf2(record, field) {
10553
10882
  const value = record === void 0 ? void 0 : record[field];
10554
10883
  if (typeof value === "number" && Number.isFinite(value)) {
10555
10884
  return value;
@@ -10616,14 +10945,14 @@ async function readBody2(request4, capBytes) {
10616
10945
  });
10617
10946
  });
10618
10947
  }
10619
- function parseJsonObject2(body) {
10948
+ function parseJsonObject3(body) {
10620
10949
  let parsed;
10621
10950
  try {
10622
10951
  parsed = JSON.parse(body);
10623
10952
  } catch {
10624
10953
  return void 0;
10625
10954
  }
10626
- return isRecord6(parsed) ? parsed : void 0;
10955
+ return isRecord8(parsed) ? parsed : void 0;
10627
10956
  }
10628
10957
  function mintWorkerJwt(randomToken, issuedAt) {
10629
10958
  const encode = (value) => Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
@@ -10646,11 +10975,16 @@ function createRcSelfHostSurface(deps) {
10646
10975
  for (const session of sessions.values()) {
10647
10976
  while (session.events[0] !== void 0 && now - session.events[0].storedAt > RC_SELF_HOST_RETENTION_MS) {
10648
10977
  const dropped = session.events.shift();
10649
- const uuid = dropped === void 0 ? void 0 : nonEmptyString2(dropped.payload, "uuid");
10978
+ const uuid = dropped === void 0 ? void 0 : nonEmptyString3(dropped.payload, "uuid");
10650
10979
  if (uuid !== void 0) {
10651
10980
  session.seenPayloadUuids.delete(uuid);
10652
10981
  }
10653
10982
  }
10983
+ for (const [clientId, lastSeenAt] of session.clients) {
10984
+ if (now - lastSeenAt > RC_SELF_HOST_RETENTION_MS) {
10985
+ session.clients.delete(clientId);
10986
+ }
10987
+ }
10654
10988
  if (session.workerStreams.size === 0 && session.clientStreams.size === 0 && now - session.lastTrafficAt > RC_SELF_HOST_RETENTION_MS) {
10655
10989
  sessions.delete(session.id);
10656
10990
  }
@@ -10689,7 +11023,7 @@ function createRcSelfHostSurface(deps) {
10689
11023
  const frameOf = (event) => {
10690
11024
  const envelope = {
10691
11025
  event_id: event.eventId,
10692
- event_type: nonEmptyString2(event.payload, "type") ?? "event",
11026
+ event_type: nonEmptyString3(event.payload, "type") ?? "event",
10693
11027
  sequence_num: event.sequenceNum,
10694
11028
  source: event.source,
10695
11029
  payload: event.payload,
@@ -10703,7 +11037,7 @@ data: ${JSON.stringify(envelope)}
10703
11037
  };
10704
11038
  const holdStream = async (session, which, wantsReplay, request4, response) => {
10705
11039
  const url = new URL(request4.url, "http://127.0.0.1");
10706
- const cursor = numberOf({ value: url.searchParams.get("from_sequence_num") }, "value") ?? numberOf({ value: singleHeader2(request4.headers, "last-event-id") }, "value");
11040
+ const cursor = numberOf2({ value: url.searchParams.get("from_sequence_num") }, "value") ?? numberOf2({ value: singleHeader2(request4.headers, "last-event-id") }, "value");
10707
11041
  const replay = cursor === void 0 ? [] : session.events.filter((event) => wantsReplay(event) && event.sequenceNum > cursor);
10708
11042
  response.start(HTTP_STATUS.ok, { "content-type": "text/event-stream", "cache-control": "no-cache" });
10709
11043
  response.flush();
@@ -10732,8 +11066,8 @@ data: ${JSON.stringify(envelope)}
10732
11066
  sink.retire();
10733
11067
  response.end();
10734
11068
  };
10735
- const publish = (session, source, payload) => {
10736
- const uuid = nonEmptyString2(payload, "uuid");
11069
+ const publish = (session, source, payload, toWorker = source === RC_SOURCE_CLIENT) => {
11070
+ const uuid = nonEmptyString3(payload, "uuid");
10737
11071
  const duplicate = uuid !== void 0 && session.seenPayloadUuids.has(uuid);
10738
11072
  session.sequenceNum += 1;
10739
11073
  const event = {
@@ -10741,9 +11075,9 @@ data: ${JSON.stringify(envelope)}
10741
11075
  eventId: uuid ?? deps.newUuid(),
10742
11076
  source,
10743
11077
  // 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.
10744
- payload: source === RC_SOURCE_CLIENT ? { ...payload, server_received_wall_ms: deps.now() } : payload,
11078
+ payload: source === RC_SOURCE_WORKER ? payload : { ...payload, server_received_wall_ms: deps.now() },
10745
11079
  createdAt: new Date(deps.now()).toISOString(),
10746
- toWorker: source === RC_SOURCE_CLIENT,
11080
+ toWorker,
10747
11081
  storedAt: deps.now()
10748
11082
  };
10749
11083
  session.events.push(event);
@@ -10791,8 +11125,26 @@ data: ${JSON.stringify(envelope)}
10791
11125
  }
10792
11126
  session.lastTrafficAt = deps.now();
10793
11127
  if (tail === "") {
11128
+ if (method === "PATCH" || method === "PUT") {
11129
+ const refusal = credentialRefusal(request4.headers);
11130
+ if (refusal !== void 0) {
11131
+ await unauthorized(response, refusal);
11132
+ return;
11133
+ }
11134
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11135
+ const title = nonEmptyString3(body, "title");
11136
+ if (title === void 0) {
11137
+ await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a session update is JSON naming a non-empty title" } });
11138
+ return;
11139
+ }
11140
+ session.title = title;
11141
+ session.lastTrafficAt = deps.now();
11142
+ deps.log?.(`rc selfhost ${session.id}: retitled at its client's request`);
11143
+ await answerJson2(response, HTTP_STATUS.ok, {});
11144
+ return;
11145
+ }
10794
11146
  if (method !== "GET") {
10795
- await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the session read is a GET" } });
11147
+ 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" } });
10796
11148
  return;
10797
11149
  }
10798
11150
  await answerJson2(response, HTTP_STATUS.ok, { session: { id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), status: "active" } });
@@ -10837,17 +11189,17 @@ data: ${JSON.stringify(envelope)}
10837
11189
  return;
10838
11190
  }
10839
11191
  if (workerTail === "" && method === "PUT") {
10840
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
10841
- const claimedEpoch = numberOf(body, "worker_epoch");
11192
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11193
+ const claimedEpoch = numberOf2(body, "worker_epoch");
10842
11194
  if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
10843
11195
  await epochStale(response);
10844
11196
  return;
10845
11197
  }
10846
- const status = nonEmptyString2(body, "worker_status");
11198
+ const status = nonEmptyString3(body, "worker_status");
10847
11199
  if (status !== void 0) {
10848
11200
  session.workerStatus = status;
10849
11201
  }
10850
- if (isRecord6(body?.external_metadata)) {
11202
+ if (isRecord8(body?.external_metadata)) {
10851
11203
  session.externalMetadata = body.external_metadata;
10852
11204
  }
10853
11205
  await answerJson2(response, HTTP_STATUS.ok, {});
@@ -10864,8 +11216,8 @@ data: ${JSON.stringify(envelope)}
10864
11216
  return;
10865
11217
  }
10866
11218
  if (workerTail === "events" && method === "POST") {
10867
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
10868
- const claimedEpoch = numberOf(body, "worker_epoch");
11219
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11220
+ const claimedEpoch = numberOf2(body, "worker_epoch");
10869
11221
  if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
10870
11222
  await epochStale(response);
10871
11223
  return;
@@ -10877,7 +11229,7 @@ data: ${JSON.stringify(envelope)}
10877
11229
  }
10878
11230
  const results = [];
10879
11231
  for (const event of events) {
10880
- if (!isRecord6(event) || !isRecord6(event.payload)) {
11232
+ if (!isRecord8(event) || !isRecord8(event.payload)) {
10881
11233
  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" } });
10882
11234
  return;
10883
11235
  }
@@ -10898,8 +11250,8 @@ data: ${JSON.stringify(envelope)}
10898
11250
  return;
10899
11251
  }
10900
11252
  if (workerTail === "internal-events" && method === "POST") {
10901
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
10902
- const claimedEpoch = numberOf(body, "worker_epoch");
11253
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11254
+ const claimedEpoch = numberOf2(body, "worker_epoch");
10903
11255
  if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
10904
11256
  await epochStale(response);
10905
11257
  return;
@@ -10910,19 +11262,26 @@ data: ${JSON.stringify(envelope)}
10910
11262
  return;
10911
11263
  }
10912
11264
  for (const event of events) {
10913
- if (isRecord6(event)) {
10914
- session.internalEvents.push({ cursor: deps.newUuid(), body: event });
11265
+ if (isRecord8(event)) {
11266
+ session.internalEvents.push({ eventId: deps.newUuid(), body: event });
10915
11267
  }
10916
11268
  }
10917
11269
  await answerJson2(response, HTTP_STATUS.ok, {});
10918
11270
  return;
10919
11271
  }
10920
11272
  if (workerTail === "internal-events" && method === "GET") {
10921
- const url = new URL(request4.url, "http://127.0.0.1");
10922
- const cursor = url.searchParams.get("cursor");
10923
- const from = cursor === null ? 0 : session.internalEvents.findIndex((event) => event.cursor === cursor) + 1;
10924
- const page = session.internalEvents.slice(from);
10925
- await answerJson2(response, HTTP_STATUS.ok, { data: page.map((event) => event.body), next_cursor: page.length === 0 ? void 0 : page[page.length - 1]?.cursor });
11273
+ const query = new URL(request4.url, "http://127.0.0.1").searchParams;
11274
+ if (query.get("session_agent_id") !== null || query.get("subagents") !== null) {
11275
+ 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" } });
11276
+ return;
11277
+ }
11278
+ const page = pageOfRcChannel(request4.url, session.internalEvents, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.body }));
11279
+ if ("error" in page) {
11280
+ const anchorUnknown = query.get("after_event_id") !== null && page.error.startsWith("the anchor names an event id");
11281
+ await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: anchorUnknown ? "after_event_id_not_found" : "invalid_request_error", message: page.error } });
11282
+ return;
11283
+ }
11284
+ await answerJson2(response, HTTP_STATUS.ok, page);
10926
11285
  return;
10927
11286
  }
10928
11287
  if (workerTail === "diagnostics" && method === "POST") {
@@ -10931,8 +11290,8 @@ data: ${JSON.stringify(envelope)}
10931
11290
  return;
10932
11291
  }
10933
11292
  if (workerTail === "web-fetch" && method === "POST") {
10934
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
10935
- const target = nonEmptyString2(body, "url");
11293
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11294
+ const target = nonEmptyString3(body, "url");
10936
11295
  if (target === void 0) {
10937
11296
  await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-fetch carries one url" } });
10938
11297
  return;
@@ -10954,8 +11313,8 @@ data: ${JSON.stringify(envelope)}
10954
11313
  return;
10955
11314
  }
10956
11315
  if (workerTail === "web-search" && method === "POST") {
10957
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
10958
- const query = nonEmptyString2(body, "query");
11316
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11317
+ const query = nonEmptyString3(body, "query");
10959
11318
  if (query === void 0) {
10960
11319
  await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-search carries one query" } });
10961
11320
  return;
@@ -10966,7 +11325,7 @@ data: ${JSON.stringify(envelope)}
10966
11325
  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" } });
10967
11326
  return;
10968
11327
  }
10969
- const searchProfile = nonEmptyString2(body, "search_profile");
11328
+ const searchProfile = nonEmptyString3(body, "search_profile");
10970
11329
  const backend = deps.webSearch;
10971
11330
  if (backend === void 0) {
10972
11331
  deps.log?.(`rc selfhost ${session.id}: worker web-search of "${query}" refused: no search backend serves this door`);
@@ -10991,7 +11350,7 @@ data: ${JSON.stringify(envelope)}
10991
11350
  return;
10992
11351
  }
10993
11352
  if (tail === "events" && method === "POST") {
10994
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11353
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
10995
11354
  const events = body?.events;
10996
11355
  if (!Array.isArray(events) || events.length === 0 || events.length > RC_SELF_HOST_MAX_BATCH_EVENTS) {
10997
11356
  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` } });
@@ -10999,7 +11358,7 @@ data: ${JSON.stringify(envelope)}
10999
11358
  }
11000
11359
  const results = [];
11001
11360
  for (const event of events) {
11002
- if (!isRecord6(event) || !isRecord6(event.payload)) {
11361
+ if (!isRecord8(event) || !isRecord8(event.payload)) {
11003
11362
  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" } });
11004
11363
  return;
11005
11364
  }
@@ -11011,26 +11370,14 @@ data: ${JSON.stringify(envelope)}
11011
11370
  return;
11012
11371
  }
11013
11372
  if (tail === "events" && method === "GET") {
11014
- 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 })) });
11373
+ 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 })) });
11015
11374
  return;
11016
11375
  }
11017
11376
  if (tail === "events/stream" && method === "GET") {
11018
11377
  await holdStream(session, "clientStreams", () => true, request4, response);
11019
11378
  return;
11020
11379
  }
11021
- if (tail === "client/presence" && method === "POST") {
11022
- await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
11023
- await answerJson2(response, HTTP_STATUS.ok, { refresh_after_seconds: RC_PRESENCE_REFRESH_SECONDS });
11024
- return;
11025
- }
11026
- if (tail === "mark_read" && method === "POST") {
11027
- await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
11028
- await answerJson2(response, HTTP_STATUS.ok, {});
11029
- return;
11030
- }
11031
- if (tail === "teleport-events" && method === "POST") {
11032
- await readBody2(request4.body, CONTROL_BODY_CAP_BYTES);
11033
- await answerJson2(response, HTTP_STATUS.ok, {});
11380
+ 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)) {
11034
11381
  return;
11035
11382
  }
11036
11383
  await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no path "${tail}"` } });
@@ -11047,17 +11394,8 @@ data: ${JSON.stringify(envelope)}
11047
11394
  await noCredential(response);
11048
11395
  return;
11049
11396
  }
11050
- if (pathname === RC_COMPAT_SESSIONS_PATH) {
11051
- if (method !== "GET") {
11052
- await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the compatibility session list is a GET" } });
11053
- return;
11054
- }
11055
- const listRefusal = credentialRefusal(request4.headers);
11056
- if (listRefusal !== void 0) {
11057
- await unauthorized(response, listRefusal);
11058
- return;
11059
- }
11060
- await answerJson2(response, HTTP_STATUS.ok, { data: [] });
11397
+ if (pathname === RC_COMPAT_SESSIONS_PATH || pathname.startsWith(`${RC_COMPAT_SESSIONS_PATH}/`)) {
11398
+ 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);
11061
11399
  return;
11062
11400
  }
11063
11401
  if (pathname !== RC_SESSIONS_PATH_PREFIX2 && !pathname.startsWith(`${RC_SESSIONS_PATH_PREFIX2}/`)) {
@@ -11071,11 +11409,11 @@ data: ${JSON.stringify(envelope)}
11071
11409
  await unauthorized(response, createRefusal);
11072
11410
  return;
11073
11411
  }
11074
- const body = parseJsonObject2(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11412
+ const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11075
11413
  const session = {
11076
11414
  id: `${RC_SESSION_ID_PREFIX2}${deps.newUuid()}`,
11077
11415
  createdAt: deps.now(),
11078
- title: nonEmptyString2(body, "title"),
11416
+ title: nonEmptyString3(body, "title"),
11079
11417
  workerJwts: /* @__PURE__ */ new Set(),
11080
11418
  sequenceNum: 0,
11081
11419
  events: [],
@@ -11083,6 +11421,7 @@ data: ${JSON.stringify(envelope)}
11083
11421
  workerStreams: /* @__PURE__ */ new Set(),
11084
11422
  clientStreams: /* @__PURE__ */ new Set(),
11085
11423
  internalEvents: [],
11424
+ clients: /* @__PURE__ */ new Map(),
11086
11425
  workerStatus: void 0,
11087
11426
  externalMetadata: void 0,
11088
11427
  lastTrafficAt: deps.now()
@@ -11127,7 +11466,7 @@ data: ${JSON.stringify(envelope)}
11127
11466
  const pathname = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
11128
11467
  if (host === RC_SELF_HOST_OAUTH_HOST) {
11129
11468
  if (method === "POST" && pathname === "/v1/oauth/token") {
11130
- const body = parseJsonObject2(await readBody2(request4, CONTROL_BODY_CAP_BYTES));
11469
+ const body = parseJsonObject3(await readBody2(request4, CONTROL_BODY_CAP_BYTES));
11131
11470
  const record = deps.credentialRecord();
11132
11471
  if (record === void 0) {
11133
11472
  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" });
@@ -11213,7 +11552,7 @@ var RC_SELF_HOST_RECORD_DIR = "rc-selfhost";
11213
11552
  var RC_SELF_HOST_RECORD_FILE = "credential.json";
11214
11553
  var SELF_HOST_EMAIL = "selfhost@agent-shim.invalid";
11215
11554
  var SELF_HOST_SUBSCRIPTION_TYPE = "max";
11216
- function isRecord7(value) {
11555
+ function isRecord9(value) {
11217
11556
  return typeof value === "object" && value !== null && !Array.isArray(value);
11218
11557
  }
11219
11558
  function readJsonObject(fs12, file) {
@@ -11227,7 +11566,7 @@ function readJsonObject(fs12, file) {
11227
11566
  } catch {
11228
11567
  return void 0;
11229
11568
  }
11230
- return isRecord7(parsed) ? parsed : void 0;
11569
+ return isRecord9(parsed) ? parsed : void 0;
11231
11570
  }
11232
11571
  function readRcSelfHostRecord(fs12, frontdoorDir) {
11233
11572
  const parsed = readJsonObject(fs12, path28.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
@@ -11251,7 +11590,7 @@ function mintRcSelfHostCredential(deps) {
11251
11590
  const recordFile = path28.join(recordDir, RC_SELF_HOST_RECORD_FILE);
11252
11591
  const previousRecord = readRcSelfHostRecord(deps.fs, deps.frontdoorDir);
11253
11592
  const existingCredentials = readJsonObject(deps.fs, credentialsFile);
11254
- const existingOauth = isRecord7(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
11593
+ const existingOauth = isRecord9(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
11255
11594
  const existingToken = typeof existingOauth?.accessToken === "string" && existingOauth.accessToken !== "" ? existingOauth.accessToken : void 0;
11256
11595
  const existingIsOwn = existingToken !== void 0 && previousRecord?.accessToken === existingToken;
11257
11596
  if (existingToken !== void 0 && !existingIsOwn && !deps.force) {
@@ -11268,7 +11607,7 @@ function mintRcSelfHostCredential(deps) {
11268
11607
  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)}
11269
11608
  `);
11270
11609
  const claudeJson = readJsonObject(deps.fs, claudeJsonFile) ?? {};
11271
- const existingBlock = (value) => isRecord7(value) ? value : {};
11610
+ const existingBlock = (value) => isRecord9(value) ? value : {};
11272
11611
  deps.fs.writeFilePrivate(claudeJsonFile, `${JSON.stringify({
11273
11612
  ...claudeJson,
11274
11613
  oauthAccount: {
@@ -11467,7 +11806,7 @@ import path30 from "node:path";
11467
11806
  // package.json
11468
11807
  var package_default = {
11469
11808
  name: "agent-shim",
11470
- version: "8.24.0",
11809
+ version: "8.26.0",
11471
11810
  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.",
11472
11811
  license: "MIT",
11473
11812
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -12730,11 +13069,15 @@ export {
12730
13069
  RC_WEB_FETCH_MAX_BYTES,
12731
13070
  RC_WEB_FETCH_MAX_REDIRECTS,
12732
13071
  ROUTED_PATH_PREFIX,
13072
+ RcLiveRateLimitSchema,
12733
13073
  RcPermissionModeSchema,
13074
+ RcRateLimitInfoSchema,
13075
+ RcRateLimitWindowSchema,
12734
13076
  RcStreamEnvelopeSchema,
12735
13077
  RcStreamEventSchema,
12736
13078
  UsageError,
12737
13079
  UsageListOutputSchema,
13080
+ UsageLiveOutputSchema,
12738
13081
  UsageSnapshotError,
12739
13082
  UsageSnapshotSchema,
12740
13083
  UsageWindowsInputSchema,
@@ -12757,6 +13100,7 @@ export {
12757
13100
  buildRcGetUsagePayload,
12758
13101
  buildRcInterruptPayload,
12759
13102
  buildRcKeepAlivePayload,
13103
+ buildRcMarkReadBody,
12760
13104
  buildRcMcpAuthenticatePayload,
12761
13105
  buildRcMcpOAuthCallbackUrlPayload,
12762
13106
  buildRcMcpReconnectPayload,
@@ -12785,6 +13129,7 @@ export {
12785
13129
  createRcControlHandler,
12786
13130
  createRcCredentialStore,
12787
13131
  createRcEventFanout,
13132
+ createRcLiveUsage,
12788
13133
  createRcSelfHostSurface,
12789
13134
  createRcSessionTracker,
12790
13135
  createRcStreamHub,
@@ -12846,6 +13191,7 @@ export {
12846
13191
  rcEventWriteResultFromAnswer,
12847
13192
  rcFanoutOnDoorHub,
12848
13193
  rcObserverAsPassthrough,
13194
+ rcRateLimitInfoOf,
12849
13195
  rcSelfHostFromEnv,
12850
13196
  rcWebFetchAllowsPrivateFromEnv,
12851
13197
  readActiveIdentity,
@@ -12903,6 +13249,7 @@ export {
12903
13249
  startConnectServer,
12904
13250
  submitRcSessionMcpOAuthCallbackUrl,
12905
13251
  suggestRcSessionFiles,
13252
+ teleportRcSession,
12906
13253
  topLevelNames,
12907
13254
  updateDirectoryRule,
12908
13255
  updateProvider,