agent-shim 8.25.0 → 8.27.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,
@@ -170,6 +174,7 @@ __export(index_exports, {
170
174
  createRcControlHandler: () => createRcControlHandler,
171
175
  createRcCredentialStore: () => createRcCredentialStore,
172
176
  createRcEventFanout: () => createRcEventFanout,
177
+ createRcLiveUsage: () => createRcLiveUsage,
173
178
  createRcSelfHostSurface: () => createRcSelfHostSurface,
174
179
  createRcSessionTracker: () => createRcSessionTracker,
175
180
  createRcStreamHub: () => createRcStreamHub,
@@ -231,6 +236,7 @@ __export(index_exports, {
231
236
  rcEventWriteResultFromAnswer: () => rcEventWriteResultFromAnswer,
232
237
  rcFanoutOnDoorHub: () => rcFanoutOnDoorHub,
233
238
  rcObserverAsPassthrough: () => rcObserverAsPassthrough,
239
+ rcRateLimitInfoOf: () => rcRateLimitInfoOf,
234
240
  rcSelfHostFromEnv: () => rcSelfHostFromEnv,
235
241
  rcWebFetchAllowsPrivateFromEnv: () => rcWebFetchAllowsPrivateFromEnv,
236
242
  readActiveIdentity: () => readActiveIdentity,
@@ -5373,6 +5379,35 @@ var RcStreamEventSchema = import_zod4.z.strictObject({
5373
5379
  session: import_zod4.z.string(),
5374
5380
  envelope: RcStreamEnvelopeSchema
5375
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
+ });
5376
5411
  var RcSessionQuerySchema = import_zod4.z.strictObject({
5377
5412
  session: import_zod4.z.string().min(1).optional()
5378
5413
  });
@@ -9983,6 +10018,12 @@ var UsageWindowsOutputSchema = import_zod19.z.strictObject({
9983
10018
  fiveHour: EffectiveWindowSchema.optional(),
9984
10019
  sevenDay: EffectiveWindowSchema.optional()
9985
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
+ });
9986
10027
  var FrontDoorSessionStatusSchema = import_zod19.z.strictObject({
9987
10028
  pid: import_zod19.z.int().positive(),
9988
10029
  startedAt: import_zod19.z.number(),
@@ -10097,6 +10138,16 @@ function createControlApiRouter(deps) {
10097
10138
  ...unified?.fiveHour === void 0 ? {} : { fiveHour: effectiveWindow(unified.fiveHour, now) },
10098
10139
  ...unified?.sevenDay === void 0 ? {} : { sevenDay: effectiveWindow(unified.sevenDay, now) }
10099
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 };
10100
10151
  })
10101
10152
  },
10102
10153
  frontdoor: {
@@ -10317,6 +10368,42 @@ function createLaunchEventPublisher(hub, now) {
10317
10368
  };
10318
10369
  }
10319
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
+
10320
10407
  // src/frontdoor/rcStream.ts
10321
10408
  var RC_STREAM_EVENT_NAME = "client_event";
10322
10409
  var SSE_BYTE_ORDER_MARK = 65279;
@@ -10379,7 +10466,7 @@ function createSseParser() {
10379
10466
  }
10380
10467
  };
10381
10468
  }
10382
- function isRecord5(value) {
10469
+ function isRecord6(value) {
10383
10470
  return typeof value === "object" && value !== null && !Array.isArray(value);
10384
10471
  }
10385
10472
  function sequenceNumOf(value) {
@@ -10401,7 +10488,7 @@ function parseRcStreamEnvelope(parsed) {
10401
10488
  } catch {
10402
10489
  return void 0;
10403
10490
  }
10404
- if (!isRecord5(data)) {
10491
+ if (!isRecord6(data)) {
10405
10492
  return void 0;
10406
10493
  }
10407
10494
  const eventType = data.event_type;
@@ -10632,7 +10719,7 @@ function createRcStreamHub(deps) {
10632
10719
  // src/frontdoor/rcSelfHostEndpoints.ts
10633
10720
  var RC_SOURCE_WORKER = "worker";
10634
10721
  var RC_SOURCE_CLIENT = "client";
10635
- function isRecord6(value) {
10722
+ function isRecord7(value) {
10636
10723
  return typeof value === "object" && value !== null && !Array.isArray(value);
10637
10724
  }
10638
10725
  function parseJsonObject2(body) {
@@ -10642,7 +10729,7 @@ function parseJsonObject2(body) {
10642
10729
  } catch {
10643
10730
  return void 0;
10644
10731
  }
10645
- return isRecord6(parsed) ? parsed : void 0;
10732
+ return isRecord7(parsed) ? parsed : void 0;
10646
10733
  }
10647
10734
  function nonEmptyString2(record, field) {
10648
10735
  const value = record === void 0 ? void 0 : record[field];
@@ -10675,7 +10762,7 @@ function pageOfRcChannel(url, rows, idOf, rowOf) {
10675
10762
  return { data: page.map(rowOf), ...last === void 0 ? {} : { next_cursor: idOf(last) } };
10676
10763
  }
10677
10764
  async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
10678
- const { request: request4, response, session } = ctx;
10765
+ const { request: request4, response, host } = ctx;
10679
10766
  if (tail === "client/presence" && method === "POST") {
10680
10767
  const body = parseJsonObject2(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10681
10768
  const clientId = nonEmptyString2(body, "client_id");
@@ -10685,11 +10772,11 @@ async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
10685
10772
  return true;
10686
10773
  }
10687
10774
  if (clear === true) {
10688
- session.clients.delete(clientId);
10689
- ctx.log?.(`rc selfhost ${session.id}: client ${clientId} cleared its presence`);
10775
+ host.clients.delete(clientId);
10776
+ ctx.log?.(`rc selfhost ${host.id}: client ${clientId} cleared its presence`);
10690
10777
  } else {
10691
- session.clients.set(clientId, ctx.now());
10692
- ctx.log?.(`rc selfhost ${session.id}: client ${clientId} announced its presence${session.clients.size === 1 ? "" : ` (${String(session.clients.size)} clients now announced)`}`);
10778
+ host.clients.set(clientId, ctx.now());
10779
+ ctx.log?.(`rc selfhost ${host.id}: client ${clientId} announced its presence${host.clients.size === 1 ? "" : ` (${String(host.clients.size)} clients now announced)`}`);
10693
10780
  }
10694
10781
  await ctx.answerJson(response, HTTP_STATUS.ok, { refresh_after_seconds: ctx.presenceRefreshSeconds });
10695
10782
  return true;
@@ -10702,8 +10789,8 @@ async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
10702
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" } });
10703
10790
  return true;
10704
10791
  }
10705
- 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);
10706
- 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)}`);
10792
+ const receipt = ctx.publish(host.conversation, host.writer, clientId ?? RC_SOURCE_CLIENT, { type: "mark_read", ...eventId === void 0 ? {} : { event_id: eventId }, ...clientId === void 0 ? {} : { client_id: clientId } });
10793
+ ctx.log?.(`rc selfhost ${host.id}: ${clientId === void 0 ? "an unnamed client" : `client ${clientId}`} marked ${eventId === void 0 ? "the session read" : `event ${eventId} read`} as sequence ${String(receipt.sequenceNum)}`);
10707
10794
  await ctx.answerJson(response, HTTP_STATUS.ok, {});
10708
10795
  return true;
10709
10796
  }
@@ -10716,20 +10803,20 @@ async function serveRcClientChannels(ctx, tail, method, maxBatchEvents) {
10716
10803
  }
10717
10804
  const results = [];
10718
10805
  for (const event of events) {
10719
- if (!isRecord6(event) || !isRecord6(event.payload)) {
10806
+ if (!isRecord7(event) || !isRecord7(event.payload)) {
10720
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" } });
10721
10808
  return true;
10722
10809
  }
10723
- const published = ctx.publish(session, RC_SOURCE_CLIENT, event.payload);
10810
+ const published = ctx.publish(host.conversation, host.writer, RC_SOURCE_CLIENT, event.payload);
10724
10811
  results.push({ sequence_num: String(published.sequenceNum), duplicate: published.duplicate });
10725
10812
  }
10726
10813
  ctx.sweep();
10727
- ctx.log?.(`rc selfhost ${session.id}: teleport write published ${String(results.length)} marker event(s)`);
10814
+ ctx.log?.(`rc selfhost ${host.id}: teleport write published ${String(results.length)} marker event(s)`);
10728
10815
  await ctx.answerJson(response, HTTP_STATUS.ok, { results });
10729
10816
  return true;
10730
10817
  }
10731
10818
  if (tail === "teleport-events" && method === "GET") {
10732
- const page = pageOfRcChannel(request4.url, session.events, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.payload }));
10819
+ const page = pageOfRcChannel(request4.url, host.conversation.events, (event) => event.eventId, (event) => ({ event_id: event.eventId, payload: event.payload }));
10733
10820
  if ("error" in page) {
10734
10821
  await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: page.error } });
10735
10822
  return true;
@@ -10805,10 +10892,7 @@ async function serveRcCompatSessions(ctx, pathname, compatPath) {
10805
10892
  await ctx.answerJson(response, HTTP_STATUS.ok, compatSessionRow(session, ctx.now));
10806
10893
  return;
10807
10894
  }
10808
- for (const sink of [...session.workerStreams, ...session.clientStreams]) {
10809
- sink.retire();
10810
- }
10811
- ctx.sessions.delete(session.id);
10895
+ ctx.retireSession(session);
10812
10896
  ctx.log?.(`rc selfhost ${session.id}: archived through the compatibility family`);
10813
10897
  await ctx.answerJson(response, HTTP_STATUS.ok, {});
10814
10898
  return;
@@ -10816,6 +10900,202 @@ async function serveRcCompatSessions(ctx, pathname, compatPath) {
10816
10900
  await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the compatibility session family serves no path "${compatTail}"` } });
10817
10901
  }
10818
10902
 
10903
+ // src/frontdoor/rcSelfHostConversations.ts
10904
+ var RC_CONVERSATIONS_PATH_PREFIX = "/v1/code/conversations";
10905
+ var RC_CONVERSATION_ID_PREFIX = "conv_";
10906
+ function isRecord8(value) {
10907
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10908
+ }
10909
+ function parseJsonObject3(body) {
10910
+ let parsed;
10911
+ try {
10912
+ parsed = JSON.parse(body);
10913
+ } catch {
10914
+ return void 0;
10915
+ }
10916
+ return isRecord8(parsed) ? parsed : void 0;
10917
+ }
10918
+ function nonEmptyString3(record, field) {
10919
+ const value = record === void 0 ? void 0 : record[field];
10920
+ return typeof value === "string" && value !== "" ? value : void 0;
10921
+ }
10922
+ function newRcSelfHostConversation(newUuid, now) {
10923
+ return { id: `${RC_CONVERSATION_ID_PREFIX}${newUuid()}`, createdAt: now(), sequenceNum: 0, events: [], seenPayloadUuids: /* @__PURE__ */ new Set(), workerStreams: /* @__PURE__ */ new Set(), clientStreams: /* @__PURE__ */ new Set(), clients: /* @__PURE__ */ new Map(), sessionIds: /* @__PURE__ */ new Set(), lastTrafficAt: now() };
10924
+ }
10925
+ function conversationRow(conversation) {
10926
+ return { id: conversation.id, created_at: new Date(conversation.createdAt).toISOString(), updated_at: new Date(conversation.lastTrafficAt).toISOString(), sessions: [...conversation.sessionIds] };
10927
+ }
10928
+ async function serveRcConversations(ctx, pathname, method) {
10929
+ const { request: request4, response, conversations } = ctx;
10930
+ const refusal = ctx.credentialRefusal(request4.headers);
10931
+ if (refusal !== void 0) {
10932
+ await ctx.unauthorized(response, refusal);
10933
+ return;
10934
+ }
10935
+ if (pathname === RC_CONVERSATIONS_PATH_PREFIX) {
10936
+ if (method !== "GET") {
10937
+ await ctx.answerJson(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the conversation collection is a GET; a conversation is born from a session create, never here" } });
10938
+ return;
10939
+ }
10940
+ await ctx.answerJson(response, HTTP_STATUS.ok, { data: [...conversations.values()].sort((left, right) => left.createdAt - right.createdAt || (left.id < right.id ? -1 : 1)).map(conversationRow) });
10941
+ return;
10942
+ }
10943
+ const rest = pathname.slice(`${RC_CONVERSATIONS_PATH_PREFIX}/`.length);
10944
+ const slash = rest.indexOf("/");
10945
+ const conversationId = slash === -1 ? rest : rest.slice(0, slash);
10946
+ const tail = slash === -1 ? "" : rest.slice(slash + 1);
10947
+ if (!conversationId.startsWith(RC_CONVERSATION_ID_PREFIX)) {
10948
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: "the conversation family names one conv_-prefixed conversation id" } });
10949
+ return;
10950
+ }
10951
+ const conversation = conversations.get(conversationId);
10952
+ if (conversation === void 0) {
10953
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: "no such Remote Control conversation on this door" } });
10954
+ return;
10955
+ }
10956
+ conversation.lastTrafficAt = ctx.now();
10957
+ if (tail === "" && method === "GET") {
10958
+ await ctx.answerJson(response, HTTP_STATUS.ok, { conversation: conversationRow(conversation) });
10959
+ return;
10960
+ }
10961
+ if (tail === "events" && method === "POST") {
10962
+ const body = parseJsonObject3(await ctx.readBody(request4.body, CONTROL_BODY_CAP_BYTES));
10963
+ const events = body?.events;
10964
+ if (!Array.isArray(events) || events.length === 0 || events.length > ctx.maxBatchEvents) {
10965
+ await ctx.answerJson(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(ctx.maxBatchEvents)} events` } });
10966
+ return;
10967
+ }
10968
+ const results = [];
10969
+ for (const event of events) {
10970
+ if (!isRecord8(event) || !isRecord8(event.payload)) {
10971
+ await ctx.answerJson(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "every event in a write is an object carrying one payload object" } });
10972
+ return;
10973
+ }
10974
+ const published = ctx.publish(conversation, { session: void 0, half: "client" }, RC_SOURCE_CLIENT, event.payload);
10975
+ results.push({ sequence_num: String(published.sequenceNum), duplicate: published.duplicate });
10976
+ }
10977
+ ctx.sweep();
10978
+ ctx.log?.(`rc selfhost ${conversation.id}: a conversation client wrote ${String(results.length)} event(s) into the shared space`);
10979
+ await ctx.answerJson(response, HTTP_STATUS.ok, { results });
10980
+ return;
10981
+ }
10982
+ if (tail === "events" && method === "GET") {
10983
+ await ctx.answerJson(response, HTTP_STATUS.ok, { data: conversation.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 })) });
10984
+ return;
10985
+ }
10986
+ if (tail === "events/stream" && method === "GET") {
10987
+ await ctx.holdStream(conversation, conversation.id, "clientStreams", () => true, request4, response);
10988
+ return;
10989
+ }
10990
+ if (await serveRcClientChannels({ request: request4, response, host: { id: conversation.id, clients: conversation.clients, conversation, writer: { session: void 0, half: "client" } }, answerJson: ctx.answerJson, readBody: ctx.readBody, publish: ctx.publish, sweep: ctx.sweep, now: ctx.now, presenceRefreshSeconds: ctx.presenceRefreshSeconds, ...ctx.log === void 0 ? {} : { log: ctx.log } }, tail, method, ctx.maxBatchEvents)) {
10991
+ return;
10992
+ }
10993
+ await ctx.answerJson(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the conversation family serves no path "${tail}"` } });
10994
+ }
10995
+
10996
+ // src/frontdoor/rcSelfHostLocal.ts
10997
+ var RC_SELF_HOST_OAUTH_HOST = "platform.claude.com";
10998
+ var RC_SELF_HOST_SCOPE_LIST = ["user:profile", "user:inference", "user:sessions:claude_code", "user:mcp_servers", "user:file_upload"];
10999
+ var RC_SELF_HOST_OAUTH_TOKEN_TTL_SECONDS = 3600;
11000
+ var MS_PER_SECOND5 = 1e3;
11001
+ var HTTP_SERVICE_UNAVAILABLE = 503;
11002
+ function isRecord9(value) {
11003
+ return typeof value === "object" && value !== null && !Array.isArray(value);
11004
+ }
11005
+ function parseJsonObject4(body) {
11006
+ let parsed;
11007
+ try {
11008
+ parsed = JSON.parse(body);
11009
+ } catch {
11010
+ return void 0;
11011
+ }
11012
+ return isRecord9(parsed) ? parsed : void 0;
11013
+ }
11014
+ function growthBookFeatures() {
11015
+ return {
11016
+ tengu_ccr_bridge: { defaultValue: true },
11017
+ tengu_bridge_repl_v2: { defaultValue: true },
11018
+ tengu_bridge_repl_v2_cse_shim_enabled: { defaultValue: true },
11019
+ tengu_bridge_min_version: { defaultValue: { minVersion: "0.0.0" } }
11020
+ };
11021
+ }
11022
+ var answerServerJson = (response, status, body) => {
11023
+ const text = JSON.stringify(body);
11024
+ response.writeHead(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")) });
11025
+ response.end(text);
11026
+ };
11027
+ function createRcSelfHostLocal(deps) {
11028
+ return {
11029
+ parsesHost: (host) => host === RC_SELF_HOST_OAUTH_HOST,
11030
+ serve: async (host, request4, response) => {
11031
+ const method = (request4.method ?? "").toUpperCase();
11032
+ const pathname = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
11033
+ if (host === RC_SELF_HOST_OAUTH_HOST) {
11034
+ if (method === "POST" && pathname === "/v1/oauth/token") {
11035
+ const body = parseJsonObject4(await deps.readBody(request4, CONTROL_BODY_CAP_BYTES));
11036
+ const record = deps.credentialRecord();
11037
+ if (record === void 0) {
11038
+ 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" });
11039
+ return true;
11040
+ }
11041
+ if (body?.grant_type !== void 0 && body.grant_type !== "refresh_token") {
11042
+ answerServerJson(response, HTTP_STATUS.badRequest, { error: "unsupported_grant_type" });
11043
+ return true;
11044
+ }
11045
+ answerServerJson(response, HTTP_STATUS.ok, { access_token: record.accessToken, refresh_token: record.refreshToken, expires_in: RC_SELF_HOST_OAUTH_TOKEN_TTL_SECONDS, scope: RC_SELF_HOST_SCOPE_LIST.join(" ") });
11046
+ return true;
11047
+ }
11048
+ if (method === "GET" && pathname === "/v1/oauth/hello") {
11049
+ answerServerJson(response, HTTP_STATUS.ok, {});
11050
+ return true;
11051
+ }
11052
+ return false;
11053
+ }
11054
+ if (method === "POST" && pathname.startsWith("/api/eval/")) {
11055
+ await deps.readBody(request4, CONTROL_BODY_CAP_BYTES);
11056
+ answerServerJson(response, HTTP_STATUS.ok, { features: growthBookFeatures(), dateUpdated: Math.floor(deps.now() / MS_PER_SECOND5) });
11057
+ return true;
11058
+ }
11059
+ if (method === "GET" && pathname === "/api/oauth/profile") {
11060
+ const record = deps.credentialRecord();
11061
+ if (record === void 0) {
11062
+ 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" });
11063
+ return true;
11064
+ }
11065
+ answerServerJson(response, HTTP_STATUS.ok, {
11066
+ account: { uuid: record.accountUuid, display_name: "agent-shim self-hosted", created_at: (/* @__PURE__ */ new Date(0)).toISOString() },
11067
+ organization: { uuid: record.organizationUuid, organization_type: "claude_max" }
11068
+ });
11069
+ return true;
11070
+ }
11071
+ if (method === "GET" && pathname === "/api/claude_code/policy_limits") {
11072
+ answerServerJson(response, HTTP_STATUS.ok, {});
11073
+ return true;
11074
+ }
11075
+ if (method === "POST" && pathname === "/api/oauth/validate") {
11076
+ await deps.readBody(request4, CONTROL_BODY_CAP_BYTES);
11077
+ const record = deps.credentialRecord();
11078
+ if (record === void 0) {
11079
+ 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" });
11080
+ return true;
11081
+ }
11082
+ answerServerJson(response, HTTP_STATUS.ok, { scopes: [...RC_SELF_HOST_SCOPE_LIST], expiresAt: null, subscriptionType: "max", account_uuid: record.accountUuid, organization_uuid: record.organizationUuid });
11083
+ return true;
11084
+ }
11085
+ if (method === "POST" && (pathname === "/api/event_logging/v2/batch" || pathname === "/api/claude_code/metrics" || pathname === "/api/claude_cli_feedback")) {
11086
+ await deps.readBody(request4, CONTROL_BODY_CAP_BYTES);
11087
+ answerServerJson(response, HTTP_STATUS.ok, {});
11088
+ return true;
11089
+ }
11090
+ if (method === "GET" && pathname === "/api/hello") {
11091
+ answerServerJson(response, HTTP_STATUS.ok, {});
11092
+ return true;
11093
+ }
11094
+ return false;
11095
+ }
11096
+ };
11097
+ }
11098
+
10819
11099
  // src/frontdoor/rcWebFetch.ts
10820
11100
  var dns2 = __toESM(require("node:dns"), 1);
10821
11101
  var http2 = __toESM(require("node:http"), 1);
@@ -11067,17 +11347,14 @@ var RC_SELF_HOST_ENV = "AGENT_SHIM_FRONTDOOR_RC_SELF_HOST";
11067
11347
  function rcSelfHostFromEnv(env) {
11068
11348
  return env[RC_SELF_HOST_ENV] === "1";
11069
11349
  }
11070
- var RC_SELF_HOST_SCOPE_LIST = ["user:profile", "user:inference", "user:sessions:claude_code", "user:mcp_servers", "user:file_upload"];
11071
11350
  var RC_SESSIONS_PATH_PREFIX2 = "/v1/code/sessions";
11072
11351
  var RC_COMPAT_SESSIONS_PATH = "/v1/sessions";
11073
11352
  var RC_SESSION_ID_PREFIX2 = "cse_";
11074
11353
  var RC_SELF_HOST_API_BASE_URL = "https://api.anthropic.com";
11075
- var RC_SELF_HOST_OAUTH_HOST = "platform.claude.com";
11076
11354
  var HTTP_CONFLICT = 409;
11077
- var MS_PER_SECOND5 = 1e3;
11078
- var HTTP_SERVICE_UNAVAILABLE = 503;
11355
+ var MS_PER_SECOND6 = 1e3;
11356
+ var HTTP_SERVICE_UNAVAILABLE2 = 503;
11079
11357
  var RC_SELF_HOST_WORKER_JWT_TTL_SECONDS = 46800;
11080
- var RC_SELF_HOST_OAUTH_TOKEN_TTL_SECONDS = 3600;
11081
11358
  var RC_SELF_HOST_KEEPALIVE_MS = 15e3;
11082
11359
  var RC_SELF_HOST_RETENTION_MS = 6e5;
11083
11360
  var RC_PRESENCE_REFRESH_SECONDS = 60;
@@ -11086,13 +11363,13 @@ var RC_SELF_HOST_WORKER_EPOCH = 1;
11086
11363
  var RC_STREAM_EVENT_NAME2 = "client_event";
11087
11364
  var CONFLICT_REASON_HEADER2 = "x-ccr-conflict-reason";
11088
11365
  var CONFLICT_REASON_EPOCH_STALE = "epoch_stale";
11089
- function isRecord7(value) {
11366
+ function isRecord10(value) {
11090
11367
  return typeof value === "object" && value !== null && !Array.isArray(value);
11091
11368
  }
11092
11369
  function isWebSearchRefusal(value) {
11093
11370
  return !Array.isArray(value);
11094
11371
  }
11095
- function nonEmptyString3(record, field) {
11372
+ function nonEmptyString4(record, field) {
11096
11373
  const value = record === void 0 ? void 0 : record[field];
11097
11374
  return typeof value === "string" && value !== "" ? value : void 0;
11098
11375
  }
@@ -11163,56 +11440,69 @@ async function readBody2(request4, capBytes) {
11163
11440
  });
11164
11441
  });
11165
11442
  }
11166
- function parseJsonObject3(body) {
11443
+ function parseJsonObject5(body) {
11167
11444
  let parsed;
11168
11445
  try {
11169
11446
  parsed = JSON.parse(body);
11170
11447
  } catch {
11171
11448
  return void 0;
11172
11449
  }
11173
- return isRecord7(parsed) ? parsed : void 0;
11450
+ return isRecord10(parsed) ? parsed : void 0;
11174
11451
  }
11175
11452
  function mintWorkerJwt(randomToken, issuedAt) {
11176
11453
  const encode = (value) => Buffer.from(JSON.stringify(value), "utf8").toString("base64url");
11177
11454
  const header = encode({ alg: "HS256", typ: "JWT" });
11178
- const payload = encode({ exp: Math.floor((issuedAt + RC_SELF_HOST_WORKER_JWT_TTL_SECONDS * MS_PER_SECOND5) / MS_PER_SECOND5), iat: Math.floor(issuedAt / MS_PER_SECOND5) });
11455
+ const payload = encode({ exp: Math.floor((issuedAt + RC_SELF_HOST_WORKER_JWT_TTL_SECONDS * MS_PER_SECOND6) / MS_PER_SECOND6), iat: Math.floor(issuedAt / MS_PER_SECOND6) });
11179
11456
  return `${header}.${payload}.${Buffer.from(randomToken(), "utf8").toString("base64url")}`;
11180
11457
  }
11181
- function growthBookFeatures() {
11182
- return {
11183
- tengu_ccr_bridge: { defaultValue: true },
11184
- tengu_bridge_repl_v2: { defaultValue: true },
11185
- tengu_bridge_repl_v2_cse_shim_enabled: { defaultValue: true },
11186
- tengu_bridge_min_version: { defaultValue: { minVersion: "0.0.0" } }
11187
- };
11188
- }
11189
11458
  function createRcSelfHostSurface(deps) {
11190
11459
  const sessions = /* @__PURE__ */ new Map();
11460
+ const conversations = /* @__PURE__ */ new Map();
11461
+ const retireSession = (session) => {
11462
+ for (const sink of [...session.conversation.workerStreams, ...session.conversation.clientStreams]) {
11463
+ if (sink.owner === session.id) {
11464
+ sink.retire();
11465
+ }
11466
+ }
11467
+ session.conversation.sessionIds.delete(session.id);
11468
+ sessions.delete(session.id);
11469
+ };
11191
11470
  const sweep = () => {
11192
11471
  const now = deps.now();
11193
11472
  for (const session of sessions.values()) {
11194
- while (session.events[0] !== void 0 && now - session.events[0].storedAt > RC_SELF_HOST_RETENTION_MS) {
11195
- const dropped = session.events.shift();
11196
- const uuid = dropped === void 0 ? void 0 : nonEmptyString3(dropped.payload, "uuid");
11473
+ for (const [clientId, lastSeenAt] of session.clients) {
11474
+ if (now - lastSeenAt > RC_SELF_HOST_RETENTION_MS) {
11475
+ session.clients.delete(clientId);
11476
+ }
11477
+ }
11478
+ const holdsStream = [...session.conversation.workerStreams, ...session.conversation.clientStreams].some((sink) => sink.owner === session.id);
11479
+ if (!holdsStream && now - session.lastTrafficAt > RC_SELF_HOST_RETENTION_MS) {
11480
+ retireSession(session);
11481
+ }
11482
+ }
11483
+ for (const conversation of conversations.values()) {
11484
+ while (conversation.events[0] !== void 0 && now - conversation.events[0].storedAt > RC_SELF_HOST_RETENTION_MS) {
11485
+ const dropped = conversation.events.shift();
11486
+ const uuid = dropped === void 0 ? void 0 : nonEmptyString4(dropped.payload, "uuid");
11197
11487
  if (uuid !== void 0) {
11198
- session.seenPayloadUuids.delete(uuid);
11488
+ conversation.seenPayloadUuids.delete(uuid);
11199
11489
  }
11200
11490
  }
11201
- for (const [clientId, lastSeenAt] of session.clients) {
11491
+ for (const [clientId, lastSeenAt] of conversation.clients) {
11202
11492
  if (now - lastSeenAt > RC_SELF_HOST_RETENTION_MS) {
11203
- session.clients.delete(clientId);
11493
+ conversation.clients.delete(clientId);
11204
11494
  }
11205
11495
  }
11206
- if (session.workerStreams.size === 0 && session.clientStreams.size === 0 && now - session.lastTrafficAt > RC_SELF_HOST_RETENTION_MS) {
11207
- sessions.delete(session.id);
11496
+ if (conversation.sessionIds.size === 0 && conversation.workerStreams.size === 0 && conversation.clientStreams.size === 0 && now - conversation.lastTrafficAt > RC_SELF_HOST_RETENTION_MS) {
11497
+ conversations.delete(conversation.id);
11208
11498
  }
11209
11499
  }
11210
11500
  };
11211
11501
  const timer = setInterval(sweep, RC_SELF_HOST_RETENTION_MS);
11212
11502
  timer.unref();
11213
11503
  const keepalive = setInterval(() => {
11214
- for (const session of sessions.values()) {
11215
- for (const sink of [...session.workerStreams, ...session.clientStreams]) {
11504
+ for (const conversation of conversations.values()) {
11505
+ for (const sink of [...conversation.workerStreams, ...conversation.clientStreams]) {
11216
11506
  sink.write(": keep-alive\n\n");
11217
11507
  }
11218
11508
  }
@@ -11241,7 +11531,7 @@ function createRcSelfHostSurface(deps) {
11241
11531
  const frameOf = (event) => {
11242
11532
  const envelope = {
11243
11533
  event_id: event.eventId,
11244
- event_type: nonEmptyString3(event.payload, "type") ?? "event",
11534
+ event_type: nonEmptyString4(event.payload, "type") ?? "event",
11245
11535
  sequence_num: event.sequenceNum,
11246
11536
  source: event.source,
11247
11537
  payload: event.payload,
@@ -11253,14 +11543,16 @@ data: ${JSON.stringify(envelope)}
11253
11543
 
11254
11544
  `;
11255
11545
  };
11256
- const holdStream = async (session, which, wantsReplay, request4, response) => {
11546
+ const reachesWorkerStream = (event, owner) => !(event.writer.half === "worker" && event.writer.session === owner);
11547
+ const holdStream = async (conversation, owner, which, wantsReplay, request4, response) => {
11257
11548
  const url = new URL(request4.url, "http://127.0.0.1");
11258
11549
  const cursor = numberOf2({ value: url.searchParams.get("from_sequence_num") }, "value") ?? numberOf2({ value: singleHeader2(request4.headers, "last-event-id") }, "value");
11259
- const replay = cursor === void 0 ? [] : session.events.filter((event) => wantsReplay(event) && event.sequenceNum > cursor);
11550
+ const replay = cursor === void 0 ? [] : conversation.events.filter((event) => wantsReplay(event) && event.sequenceNum > cursor);
11260
11551
  response.start(HTTP_STATUS.ok, { "content-type": "text/event-stream", "cache-control": "no-cache" });
11261
11552
  response.flush();
11262
11553
  let pending = Promise.resolve();
11263
11554
  const sink = {
11555
+ owner,
11264
11556
  write: (frame) => {
11265
11557
  pending = pending.then(async () => {
11266
11558
  await response.write(frame);
@@ -11268,11 +11560,11 @@ data: ${JSON.stringify(envelope)}
11268
11560
  });
11269
11561
  },
11270
11562
  retire: () => {
11271
- session[which].delete(sink);
11563
+ conversation[which].delete(sink);
11272
11564
  }
11273
11565
  };
11274
- session[which].add(sink);
11275
- session.lastTrafficAt = deps.now();
11566
+ conversation[which].add(sink);
11567
+ conversation.lastTrafficAt = deps.now();
11276
11568
  for (const event of replay) {
11277
11569
  sink.write(frameOf(event));
11278
11570
  }
@@ -11284,36 +11576,37 @@ data: ${JSON.stringify(envelope)}
11284
11576
  sink.retire();
11285
11577
  response.end();
11286
11578
  };
11287
- const publish = (session, source, payload, toWorker = source === RC_SOURCE_CLIENT) => {
11288
- const uuid = nonEmptyString3(payload, "uuid");
11289
- const duplicate = uuid !== void 0 && session.seenPayloadUuids.has(uuid);
11290
- session.sequenceNum += 1;
11579
+ const publish = (conversation, writer, source, payload) => {
11580
+ const uuid = nonEmptyString4(payload, "uuid");
11581
+ const duplicate = uuid !== void 0 && conversation.seenPayloadUuids.has(uuid);
11582
+ conversation.sequenceNum += 1;
11291
11583
  const event = {
11292
- sequenceNum: session.sequenceNum,
11584
+ sequenceNum: conversation.sequenceNum,
11293
11585
  eventId: uuid ?? deps.newUuid(),
11294
11586
  source,
11295
11587
  // 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.
11296
11588
  payload: source === RC_SOURCE_WORKER ? payload : { ...payload, server_received_wall_ms: deps.now() },
11297
11589
  createdAt: new Date(deps.now()).toISOString(),
11298
- toWorker,
11590
+ writer,
11299
11591
  storedAt: deps.now()
11300
11592
  };
11301
- session.events.push(event);
11593
+ conversation.events.push(event);
11302
11594
  if (uuid !== void 0) {
11303
- session.seenPayloadUuids.add(uuid);
11595
+ conversation.seenPayloadUuids.add(uuid);
11304
11596
  }
11305
- session.lastTrafficAt = event.storedAt;
11597
+ conversation.lastTrafficAt = event.storedAt;
11306
11598
  const frame = frameOf(event);
11307
- if (event.toWorker) {
11308
- for (const sink of [...session.workerStreams]) {
11599
+ for (const sink of [...conversation.workerStreams]) {
11600
+ if (reachesWorkerStream(event, sink.owner)) {
11309
11601
  sink.write(frame);
11310
11602
  }
11311
11603
  }
11312
- for (const sink of [...session.clientStreams]) {
11604
+ for (const sink of [...conversation.clientStreams]) {
11313
11605
  sink.write(frame);
11314
11606
  }
11315
11607
  return { sequenceNum: event.sequenceNum, eventId: event.eventId, duplicate };
11316
11608
  };
11609
+ const sessionRow = (session) => ({ id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), status: "active", conversation_id: session.conversation.id });
11317
11610
  const answerJson2 = async (response, status, body, extraHeaders = {}) => {
11318
11611
  const text = JSON.stringify(body);
11319
11612
  response.start(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")), ...extraHeaders });
@@ -11321,7 +11614,7 @@ data: ${JSON.stringify(envelope)}
11321
11614
  response.end();
11322
11615
  };
11323
11616
  const noCredential = async (response) => {
11324
- await answerJson2(response, HTTP_SERVICE_UNAVAILABLE, { type: "error", error: { type: "api_error", message: "the front door's self-hosted Remote Control surface is on but no credential has been minted for it; run agent-shim frontdoor rc selfhost mint first" } });
11617
+ await answerJson2(response, HTTP_SERVICE_UNAVAILABLE2, { type: "error", error: { type: "api_error", message: "the front door's self-hosted Remote Control surface is on but no credential has been minted for it; run agent-shim frontdoor rc selfhost mint first" } });
11325
11618
  };
11326
11619
  const epochStale = async (response) => {
11327
11620
  await answerJson2(response, HTTP_CONFLICT, { type: "error", error: { type: "api_error", message: "worker_epoch does not match the session's live epoch" } }, { [CONFLICT_REASON_HEADER2]: CONFLICT_REASON_EPOCH_STALE });
@@ -11349,8 +11642,8 @@ data: ${JSON.stringify(envelope)}
11349
11642
  await unauthorized(response, refusal);
11350
11643
  return;
11351
11644
  }
11352
- const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11353
- const title = nonEmptyString3(body, "title");
11645
+ const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11646
+ const title = nonEmptyString4(body, "title");
11354
11647
  if (title === void 0) {
11355
11648
  await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a session update is JSON naming a non-empty title" } });
11356
11649
  return;
@@ -11365,7 +11658,7 @@ data: ${JSON.stringify(envelope)}
11365
11658
  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" } });
11366
11659
  return;
11367
11660
  }
11368
- await answerJson2(response, HTTP_STATUS.ok, { session: { id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), status: "active" } });
11661
+ await answerJson2(response, HTTP_STATUS.ok, { session: sessionRow(session) });
11369
11662
  return;
11370
11663
  }
11371
11664
  if (tail === "bridge" && method === "POST") {
@@ -11386,10 +11679,7 @@ data: ${JSON.stringify(envelope)}
11386
11679
  await unauthorized(response, refusal);
11387
11680
  return;
11388
11681
  }
11389
- for (const sink of [...session.workerStreams, ...session.clientStreams]) {
11390
- sink.retire();
11391
- }
11392
- sessions.delete(session.id);
11682
+ retireSession(session);
11393
11683
  deps.log?.(`rc selfhost ${session.id}: archived at its client's request`);
11394
11684
  await answerJson2(response, HTTP_STATUS.ok, {});
11395
11685
  return;
@@ -11407,17 +11697,17 @@ data: ${JSON.stringify(envelope)}
11407
11697
  return;
11408
11698
  }
11409
11699
  if (workerTail === "" && method === "PUT") {
11410
- const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11700
+ const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11411
11701
  const claimedEpoch = numberOf2(body, "worker_epoch");
11412
11702
  if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
11413
11703
  await epochStale(response);
11414
11704
  return;
11415
11705
  }
11416
- const status = nonEmptyString3(body, "worker_status");
11706
+ const status = nonEmptyString4(body, "worker_status");
11417
11707
  if (status !== void 0) {
11418
11708
  session.workerStatus = status;
11419
11709
  }
11420
- if (isRecord7(body?.external_metadata)) {
11710
+ if (isRecord10(body?.external_metadata)) {
11421
11711
  session.externalMetadata = body.external_metadata;
11422
11712
  }
11423
11713
  await answerJson2(response, HTTP_STATUS.ok, {});
@@ -11434,7 +11724,7 @@ data: ${JSON.stringify(envelope)}
11434
11724
  return;
11435
11725
  }
11436
11726
  if (workerTail === "events" && method === "POST") {
11437
- const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11727
+ const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11438
11728
  const claimedEpoch = numberOf2(body, "worker_epoch");
11439
11729
  if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
11440
11730
  await epochStale(response);
@@ -11447,11 +11737,11 @@ data: ${JSON.stringify(envelope)}
11447
11737
  }
11448
11738
  const results = [];
11449
11739
  for (const event of events) {
11450
- if (!isRecord7(event) || !isRecord7(event.payload)) {
11740
+ if (!isRecord10(event) || !isRecord10(event.payload)) {
11451
11741
  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" } });
11452
11742
  return;
11453
11743
  }
11454
- const published = publish(session, RC_SOURCE_WORKER, event.payload);
11744
+ const published = publish(session.conversation, { session: session.id, half: "worker" }, RC_SOURCE_WORKER, event.payload);
11455
11745
  results.push({ event_id: published.eventId, sequence_num: String(published.sequenceNum) });
11456
11746
  }
11457
11747
  sweep();
@@ -11459,7 +11749,7 @@ data: ${JSON.stringify(envelope)}
11459
11749
  return;
11460
11750
  }
11461
11751
  if (workerTail === "events/stream" && method === "GET") {
11462
- await holdStream(session, "workerStreams", (event) => event.toWorker, request4, response);
11752
+ await holdStream(session.conversation, session.id, "workerStreams", (event) => reachesWorkerStream(event, session.id), request4, response);
11463
11753
  return;
11464
11754
  }
11465
11755
  if (workerTail === "events/delivery" && method === "POST") {
@@ -11468,7 +11758,7 @@ data: ${JSON.stringify(envelope)}
11468
11758
  return;
11469
11759
  }
11470
11760
  if (workerTail === "internal-events" && method === "POST") {
11471
- const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11761
+ const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11472
11762
  const claimedEpoch = numberOf2(body, "worker_epoch");
11473
11763
  if (claimedEpoch !== void 0 && claimedEpoch !== RC_SELF_HOST_WORKER_EPOCH) {
11474
11764
  await epochStale(response);
@@ -11480,7 +11770,7 @@ data: ${JSON.stringify(envelope)}
11480
11770
  return;
11481
11771
  }
11482
11772
  for (const event of events) {
11483
- if (isRecord7(event)) {
11773
+ if (isRecord10(event)) {
11484
11774
  session.internalEvents.push({ eventId: deps.newUuid(), body: event });
11485
11775
  }
11486
11776
  }
@@ -11508,8 +11798,8 @@ data: ${JSON.stringify(envelope)}
11508
11798
  return;
11509
11799
  }
11510
11800
  if (workerTail === "web-fetch" && method === "POST") {
11511
- const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11512
- const target = nonEmptyString3(body, "url");
11801
+ const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11802
+ const target = nonEmptyString4(body, "url");
11513
11803
  if (target === void 0) {
11514
11804
  await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-fetch carries one url" } });
11515
11805
  return;
@@ -11531,8 +11821,8 @@ data: ${JSON.stringify(envelope)}
11531
11821
  return;
11532
11822
  }
11533
11823
  if (workerTail === "web-search" && method === "POST") {
11534
- const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11535
- const query = nonEmptyString3(body, "query");
11824
+ const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11825
+ const query = nonEmptyString4(body, "query");
11536
11826
  if (query === void 0) {
11537
11827
  await answerJson2(response, HTTP_STATUS.badRequest, { type: "error", error: { type: "invalid_request_error", message: "a worker web-search carries one query" } });
11538
11828
  return;
@@ -11543,7 +11833,7 @@ data: ${JSON.stringify(envelope)}
11543
11833
  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" } });
11544
11834
  return;
11545
11835
  }
11546
- const searchProfile = nonEmptyString3(body, "search_profile");
11836
+ const searchProfile = nonEmptyString4(body, "search_profile");
11547
11837
  const backend = deps.webSearch;
11548
11838
  if (backend === void 0) {
11549
11839
  deps.log?.(`rc selfhost ${session.id}: worker web-search of "${query}" refused: no search backend serves this door`);
@@ -11568,7 +11858,7 @@ data: ${JSON.stringify(envelope)}
11568
11858
  return;
11569
11859
  }
11570
11860
  if (tail === "events" && method === "POST") {
11571
- const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11861
+ const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11572
11862
  const events = body?.events;
11573
11863
  if (!Array.isArray(events) || events.length === 0 || events.length > RC_SELF_HOST_MAX_BATCH_EVENTS) {
11574
11864
  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` } });
@@ -11576,11 +11866,11 @@ data: ${JSON.stringify(envelope)}
11576
11866
  }
11577
11867
  const results = [];
11578
11868
  for (const event of events) {
11579
- if (!isRecord7(event) || !isRecord7(event.payload)) {
11869
+ if (!isRecord10(event) || !isRecord10(event.payload)) {
11580
11870
  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" } });
11581
11871
  return;
11582
11872
  }
11583
- const published = publish(session, RC_SOURCE_CLIENT, event.payload);
11873
+ const published = publish(session.conversation, { session: session.id, half: "client" }, RC_SOURCE_CLIENT, event.payload);
11584
11874
  results.push({ sequence_num: String(published.sequenceNum), duplicate: published.duplicate });
11585
11875
  }
11586
11876
  sweep();
@@ -11588,14 +11878,14 @@ data: ${JSON.stringify(envelope)}
11588
11878
  return;
11589
11879
  }
11590
11880
  if (tail === "events" && method === "GET") {
11591
- 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 })) });
11881
+ await answerJson2(response, HTTP_STATUS.ok, { data: session.conversation.events.map((event) => ({ event_id: event.eventId, event_type: nonEmptyString4(event.payload, "type") ?? "event", sequence_num: event.sequenceNum, source: event.source, payload: event.payload, created_at: event.createdAt })) });
11592
11882
  return;
11593
11883
  }
11594
11884
  if (tail === "events/stream" && method === "GET") {
11595
- await holdStream(session, "clientStreams", () => true, request4, response);
11885
+ await holdStream(session.conversation, session.id, "clientStreams", () => true, request4, response);
11596
11886
  return;
11597
11887
  }
11598
- 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)) {
11888
+ if (await serveRcClientChannels({ request: request4, response, host: { id: session.id, clients: session.clients, conversation: session.conversation, writer: { session: session.id, half: "client" } }, 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)) {
11599
11889
  return;
11600
11890
  }
11601
11891
  await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `the self-hosted Remote Control surface serves no path "${tail}"` } });
@@ -11613,7 +11903,11 @@ data: ${JSON.stringify(envelope)}
11613
11903
  return;
11614
11904
  }
11615
11905
  if (pathname === RC_COMPAT_SESSIONS_PATH || pathname.startsWith(`${RC_COMPAT_SESSIONS_PATH}/`)) {
11616
- 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);
11906
+ await serveRcCompatSessions({ request: request4, response, sessions, retireSession, answerJson: answerJson2, readBody: readBody2, credentialRefusal, unauthorized, now: deps.now, ...deps.log === void 0 ? {} : { log: deps.log } }, pathname, RC_COMPAT_SESSIONS_PATH);
11907
+ return;
11908
+ }
11909
+ if (pathname === RC_CONVERSATIONS_PATH_PREFIX || pathname.startsWith(`${RC_CONVERSATIONS_PATH_PREFIX}/`)) {
11910
+ await serveRcConversations({ request: request4, response, conversations, answerJson: answerJson2, readBody: readBody2, credentialRefusal, unauthorized, publish, holdStream, sweep, now: deps.now, presenceRefreshSeconds: RC_PRESENCE_REFRESH_SECONDS, maxBatchEvents: RC_SELF_HOST_MAX_BATCH_EVENTS, ...deps.log === void 0 ? {} : { log: deps.log } }, pathname, method);
11617
11911
  return;
11618
11912
  }
11619
11913
  if (pathname !== RC_SESSIONS_PATH_PREFIX2 && !pathname.startsWith(`${RC_SESSIONS_PATH_PREFIX2}/`)) {
@@ -11627,26 +11921,36 @@ data: ${JSON.stringify(envelope)}
11627
11921
  await unauthorized(response, createRefusal);
11628
11922
  return;
11629
11923
  }
11630
- const body = parseJsonObject3(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11924
+ const body = parseJsonObject5(await readBody2(request4.body, CONTROL_BODY_CAP_BYTES));
11925
+ const namedConversation = nonEmptyString4(body, "conversation");
11926
+ let conversation;
11927
+ if (namedConversation === void 0) {
11928
+ conversation = newRcSelfHostConversation(deps.newUuid, deps.now);
11929
+ conversations.set(conversation.id, conversation);
11930
+ } else {
11931
+ conversation = conversations.get(namedConversation);
11932
+ if (conversation === void 0) {
11933
+ await answerJson2(response, HTTP_STATUS.notFound, { type: "error", error: { type: "api_error", message: `no such Remote Control conversation on this door: ${namedConversation}` } });
11934
+ return;
11935
+ }
11936
+ conversation.lastTrafficAt = deps.now();
11937
+ }
11631
11938
  const session = {
11632
11939
  id: `${RC_SESSION_ID_PREFIX2}${deps.newUuid()}`,
11633
11940
  createdAt: deps.now(),
11634
- title: nonEmptyString3(body, "title"),
11941
+ title: nonEmptyString4(body, "title"),
11942
+ conversation,
11635
11943
  workerJwts: /* @__PURE__ */ new Set(),
11636
- sequenceNum: 0,
11637
- events: [],
11638
- seenPayloadUuids: /* @__PURE__ */ new Set(),
11639
- workerStreams: /* @__PURE__ */ new Set(),
11640
- clientStreams: /* @__PURE__ */ new Set(),
11641
11944
  internalEvents: [],
11642
11945
  clients: /* @__PURE__ */ new Map(),
11643
11946
  workerStatus: void 0,
11644
11947
  externalMetadata: void 0,
11645
11948
  lastTrafficAt: deps.now()
11646
11949
  };
11950
+ conversation.sessionIds.add(session.id);
11647
11951
  sessions.set(session.id, session);
11648
- deps.log?.(`rc selfhost ${session.id}: session created on the door's own surface`);
11649
- await answerJson2(response, HTTP_STATUS.ok, { session: { id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), status: "active" } });
11952
+ deps.log?.(`rc selfhost ${session.id}: session created on the door's own surface, attached to conversation ${conversation.id}${namedConversation === void 0 ? "" : " at its client's request"}`);
11953
+ await answerJson2(response, HTTP_STATUS.ok, { session: sessionRow(session) });
11650
11954
  return;
11651
11955
  }
11652
11956
  if (method === "GET") {
@@ -11655,7 +11959,7 @@ data: ${JSON.stringify(envelope)}
11655
11959
  await unauthorized(response, readRefusal);
11656
11960
  return;
11657
11961
  }
11658
- await answerJson2(response, HTTP_STATUS.ok, { data: [...sessions.values()].map((session) => ({ id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), updated_at: new Date(session.lastTrafficAt).toISOString(), session_status: "active" })) });
11962
+ await answerJson2(response, HTTP_STATUS.ok, { data: [...sessions.values()].map((session) => ({ id: session.id, title: session.title, created_at: new Date(session.createdAt).toISOString(), updated_at: new Date(session.lastTrafficAt).toISOString(), session_status: "active", conversation_id: session.conversation.id })) });
11659
11963
  return;
11660
11964
  }
11661
11965
  await answerJson2(response, HTTP_STATUS.methodNotAllowed, { type: "error", error: { type: "api_error", message: "the session collection is a POST (create) and a GET (list)" } });
@@ -11672,92 +11976,20 @@ data: ${JSON.stringify(envelope)}
11672
11976
  await serveSessionPath(sessions.get(id), tail, request4, response);
11673
11977
  }
11674
11978
  };
11675
- const answerServerJson = (response, status, body) => {
11676
- const text = JSON.stringify(body);
11677
- response.writeHead(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")) });
11678
- response.end(text);
11679
- };
11680
- const local = {
11681
- parsesHost: (host) => host === RC_SELF_HOST_OAUTH_HOST,
11682
- serve: async (host, request4, response) => {
11683
- const method = (request4.method ?? "").toUpperCase();
11684
- const pathname = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
11685
- if (host === RC_SELF_HOST_OAUTH_HOST) {
11686
- if (method === "POST" && pathname === "/v1/oauth/token") {
11687
- const body = parseJsonObject3(await readBody2(request4, CONTROL_BODY_CAP_BYTES));
11688
- const record = deps.credentialRecord();
11689
- if (record === void 0) {
11690
- 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" });
11691
- return true;
11692
- }
11693
- if (body?.grant_type !== void 0 && body.grant_type !== "refresh_token") {
11694
- answerServerJson(response, HTTP_STATUS.badRequest, { error: "unsupported_grant_type" });
11695
- return true;
11696
- }
11697
- answerServerJson(response, HTTP_STATUS.ok, { access_token: record.accessToken, refresh_token: record.refreshToken, expires_in: RC_SELF_HOST_OAUTH_TOKEN_TTL_SECONDS, scope: RC_SELF_HOST_SCOPE_LIST.join(" ") });
11698
- return true;
11699
- }
11700
- if (method === "GET" && pathname === "/v1/oauth/hello") {
11701
- answerServerJson(response, HTTP_STATUS.ok, {});
11702
- return true;
11703
- }
11704
- return false;
11705
- }
11706
- if (method === "POST" && pathname.startsWith("/api/eval/")) {
11707
- await readBody2(request4, CONTROL_BODY_CAP_BYTES);
11708
- answerServerJson(response, HTTP_STATUS.ok, { features: growthBookFeatures(), dateUpdated: Math.floor(deps.now() / MS_PER_SECOND5) });
11709
- return true;
11710
- }
11711
- if (method === "GET" && pathname === "/api/oauth/profile") {
11712
- const record = deps.credentialRecord();
11713
- if (record === void 0) {
11714
- 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" });
11715
- return true;
11716
- }
11717
- answerServerJson(response, HTTP_STATUS.ok, {
11718
- account: { uuid: record.accountUuid, display_name: "agent-shim self-hosted", created_at: (/* @__PURE__ */ new Date(0)).toISOString() },
11719
- organization: { uuid: record.organizationUuid, organization_type: "claude_max" }
11720
- });
11721
- return true;
11722
- }
11723
- if (method === "GET" && pathname === "/api/claude_code/policy_limits") {
11724
- answerServerJson(response, HTTP_STATUS.ok, {});
11725
- return true;
11726
- }
11727
- if (method === "POST" && pathname === "/api/oauth/validate") {
11728
- await readBody2(request4, CONTROL_BODY_CAP_BYTES);
11729
- const record = deps.credentialRecord();
11730
- if (record === void 0) {
11731
- 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" });
11732
- return true;
11733
- }
11734
- answerServerJson(response, HTTP_STATUS.ok, { scopes: [...RC_SELF_HOST_SCOPE_LIST], expiresAt: null, subscriptionType: "max", account_uuid: record.accountUuid, organization_uuid: record.organizationUuid });
11735
- return true;
11736
- }
11737
- if (method === "POST" && (pathname === "/api/event_logging/v2/batch" || pathname === "/api/claude_code/metrics" || pathname === "/api/claude_cli_feedback")) {
11738
- await readBody2(request4, CONTROL_BODY_CAP_BYTES);
11739
- answerServerJson(response, HTTP_STATUS.ok, {});
11740
- return true;
11741
- }
11742
- if (method === "GET" && pathname === "/api/hello") {
11743
- answerServerJson(response, HTTP_STATUS.ok, {});
11744
- return true;
11745
- }
11746
- return false;
11747
- }
11748
- };
11979
+ const local = createRcSelfHostLocal({ now: deps.now, credentialRecord: deps.credentialRecord, readBody: readBody2 });
11749
11980
  return {
11750
11981
  route,
11751
11982
  local,
11752
11983
  close: () => {
11753
11984
  clearInterval(timer);
11754
11985
  clearInterval(keepalive);
11755
- for (const session of sessions.values()) {
11756
- for (const sink of [...session.workerStreams, ...session.clientStreams]) {
11986
+ for (const conversation of conversations.values()) {
11987
+ for (const sink of [...conversation.workerStreams, ...conversation.clientStreams]) {
11757
11988
  sink.retire();
11758
11989
  }
11759
11990
  }
11760
11991
  sessions.clear();
11992
+ conversations.clear();
11761
11993
  }
11762
11994
  };
11763
11995
  }
@@ -11770,7 +12002,7 @@ var RC_SELF_HOST_RECORD_DIR = "rc-selfhost";
11770
12002
  var RC_SELF_HOST_RECORD_FILE = "credential.json";
11771
12003
  var SELF_HOST_EMAIL = "selfhost@agent-shim.invalid";
11772
12004
  var SELF_HOST_SUBSCRIPTION_TYPE = "max";
11773
- function isRecord8(value) {
12005
+ function isRecord11(value) {
11774
12006
  return typeof value === "object" && value !== null && !Array.isArray(value);
11775
12007
  }
11776
12008
  function readJsonObject(fs12, file) {
@@ -11784,7 +12016,7 @@ function readJsonObject(fs12, file) {
11784
12016
  } catch {
11785
12017
  return void 0;
11786
12018
  }
11787
- return isRecord8(parsed) ? parsed : void 0;
12019
+ return isRecord11(parsed) ? parsed : void 0;
11788
12020
  }
11789
12021
  function readRcSelfHostRecord(fs12, frontdoorDir) {
11790
12022
  const parsed = readJsonObject(fs12, import_node_path28.default.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
@@ -11808,7 +12040,7 @@ function mintRcSelfHostCredential(deps) {
11808
12040
  const recordFile = import_node_path28.default.join(recordDir, RC_SELF_HOST_RECORD_FILE);
11809
12041
  const previousRecord = readRcSelfHostRecord(deps.fs, deps.frontdoorDir);
11810
12042
  const existingCredentials = readJsonObject(deps.fs, credentialsFile);
11811
- const existingOauth = isRecord8(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
12043
+ const existingOauth = isRecord11(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
11812
12044
  const existingToken = typeof existingOauth?.accessToken === "string" && existingOauth.accessToken !== "" ? existingOauth.accessToken : void 0;
11813
12045
  const existingIsOwn = existingToken !== void 0 && previousRecord?.accessToken === existingToken;
11814
12046
  if (existingToken !== void 0 && !existingIsOwn && !deps.force) {
@@ -11825,7 +12057,7 @@ function mintRcSelfHostCredential(deps) {
11825
12057
  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)}
11826
12058
  `);
11827
12059
  const claudeJson = readJsonObject(deps.fs, claudeJsonFile) ?? {};
11828
- const existingBlock = (value) => isRecord8(value) ? value : {};
12060
+ const existingBlock = (value) => isRecord11(value) ? value : {};
11829
12061
  deps.fs.writeFilePrivate(claudeJsonFile, `${JSON.stringify({
11830
12062
  ...claudeJson,
11831
12063
  oauthAccount: {
@@ -12024,7 +12256,7 @@ var import_node_path30 = __toESM(require("node:path"), 1);
12024
12256
  // package.json
12025
12257
  var package_default = {
12026
12258
  name: "agent-shim",
12027
- version: "8.25.0",
12259
+ version: "8.27.0",
12028
12260
  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.",
12029
12261
  license: "MIT",
12030
12262
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -13288,11 +13520,15 @@ function prepareClaudeLaunch(options) {
13288
13520
  RC_WEB_FETCH_MAX_BYTES,
13289
13521
  RC_WEB_FETCH_MAX_REDIRECTS,
13290
13522
  ROUTED_PATH_PREFIX,
13523
+ RcLiveRateLimitSchema,
13291
13524
  RcPermissionModeSchema,
13525
+ RcRateLimitInfoSchema,
13526
+ RcRateLimitWindowSchema,
13292
13527
  RcStreamEnvelopeSchema,
13293
13528
  RcStreamEventSchema,
13294
13529
  UsageError,
13295
13530
  UsageListOutputSchema,
13531
+ UsageLiveOutputSchema,
13296
13532
  UsageSnapshotError,
13297
13533
  UsageSnapshotSchema,
13298
13534
  UsageWindowsInputSchema,
@@ -13344,6 +13580,7 @@ function prepareClaudeLaunch(options) {
13344
13580
  createRcControlHandler,
13345
13581
  createRcCredentialStore,
13346
13582
  createRcEventFanout,
13583
+ createRcLiveUsage,
13347
13584
  createRcSelfHostSurface,
13348
13585
  createRcSessionTracker,
13349
13586
  createRcStreamHub,
@@ -13405,6 +13642,7 @@ function prepareClaudeLaunch(options) {
13405
13642
  rcEventWriteResultFromAnswer,
13406
13643
  rcFanoutOnDoorHub,
13407
13644
  rcObserverAsPassthrough,
13645
+ rcRateLimitInfoOf,
13408
13646
  rcSelfHostFromEnv,
13409
13647
  rcWebFetchAllowsPrivateFromEnv,
13410
13648
  readActiveIdentity,