agent-shim 8.8.0 → 8.9.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
@@ -3066,1326 +3066,1916 @@ function realConnectCertStore(paths) {
3066
3066
  // src/frontdoor/connectEffects.ts
3067
3067
  import dns from "node:dns";
3068
3068
  import * as http from "node:http";
3069
- import * as https from "node:https";
3069
+ import * as https2 from "node:https";
3070
3070
  import * as net2 from "node:net";
3071
3071
  import * as tls from "node:tls";
3072
- var UPSTREAM_LOCAL_PORT_START = 47900;
3073
- var UPSTREAM_LOCAL_PORT_END = 47919;
3074
- var UPSTREAM_LOCAL_PORT_COUNT = UPSTREAM_LOCAL_PORT_END - UPSTREAM_LOCAL_PORT_START + 1;
3075
- var nextUpstreamLocalPort = UPSTREAM_LOCAL_PORT_START;
3076
- function upstreamLocalPort() {
3077
- const port = nextUpstreamLocalPort;
3078
- nextUpstreamLocalPort = nextUpstreamLocalPort === UPSTREAM_LOCAL_PORT_END ? UPSTREAM_LOCAL_PORT_START : nextUpstreamLocalPort + 1;
3079
- return port;
3072
+
3073
+ // src/codex/http.ts
3074
+ var HTTP_STATUS = {
3075
+ ok: 200,
3076
+ badRequest: 400,
3077
+ unauthorized: 401,
3078
+ forbidden: 403,
3079
+ notFound: 404,
3080
+ methodNotAllowed: 405,
3081
+ payloadTooLarge: 413,
3082
+ tooManyRequests: 429,
3083
+ /** Not sent to anyone: the client is gone. Recorded for a response abandoned because the client disconnected, following the convention nginx uses for the same case. */
3084
+ clientClosedRequest: 499,
3085
+ internalServerError: 500,
3086
+ badGateway: 502
3087
+ };
3088
+
3089
+ // src/frontdoor/rcControl.ts
3090
+ import * as https from "node:https";
3091
+
3092
+ // src/frontdoor/rcSessions.ts
3093
+ var RC_SESSIONS_PATH_PREFIX = "/v1/code/sessions";
3094
+ var RC_SESSION_ID_PREFIX = "cse_";
3095
+ var RC_IDLE_EXPIRY_MS = 45e3;
3096
+ var RC_CREATE_RESPONSE_CAP_BYTES = 8192;
3097
+ var CONFLICT_REASON_HEADER = "x-ccr-conflict-reason";
3098
+ var RC_STREAM_WORKER_SOURCE = "worker";
3099
+ var RC_PENDING_DEADLINE_MS = 14e3;
3100
+ var RC_REQUEST_PARSE_CAP_BYTES = 262144;
3101
+ var RC_PENDING_SUMMARY_EXCERPT_CHARS = 300;
3102
+ var SUCCESS_STATUS_MAX_EXCLUSIVE = 300;
3103
+ function isSuccessful(status) {
3104
+ return status >= HTTP_STATUS.ok && status < SUCCESS_STATUS_MAX_EXCLUSIVE;
3080
3105
  }
3081
- var realAddressLookup = (host, _options, callback) => {
3082
- if (net2.isIP(host) !== 0) {
3083
- callback(null, [{ address: host, family: net2.isIP(host) }]);
3084
- return;
3106
+ function singleHeader(headers, name) {
3107
+ const value = headers[name];
3108
+ return typeof value === "string" ? value : void 0;
3109
+ }
3110
+ var OAUTH_AUTHORIZATION_PREFIX = "Bearer sk-ant-oat";
3111
+ function oauthAuthorizationOf(headers) {
3112
+ const value = singleHeader(headers, "authorization");
3113
+ return value?.startsWith(OAUTH_AUTHORIZATION_PREFIX) === true ? value : void 0;
3114
+ }
3115
+ function sessionCreateId(body) {
3116
+ let parsed;
3117
+ try {
3118
+ parsed = JSON.parse(body);
3119
+ } catch {
3120
+ return void 0;
3085
3121
  }
3086
- dns.resolve4(host, (error, addresses) => {
3087
- if (error !== null) {
3088
- callback(error, [{ address: "", family: 4 }]);
3089
- return;
3090
- }
3091
- const first = addresses[0];
3092
- if (first === void 0) {
3093
- callback(new Error(`no A record for ${host}`), [{ address: "", family: 4 }]);
3094
- return;
3095
- }
3096
- callback(null, [{ address: first, family: 4 }]);
3097
- });
3098
- };
3099
- var HTTP_PORT = 80;
3100
- var HELD_PORT_ERRNOS = /* @__PURE__ */ new Set(["EADDRINUSE", "EADDRNOTAVAIL"]);
3101
- async function dialOnce(connectOptions) {
3102
- return await new Promise((resolve) => {
3103
- const socket = net2.connect(connectOptions);
3104
- socket.once("connect", () => {
3105
- resolve({ error: null, socket });
3106
- });
3107
- socket.once("error", (error) => {
3108
- resolve({ error, socket });
3109
- });
3110
- });
3122
+ if (typeof parsed !== "object" || parsed === null || !("session" in parsed)) {
3123
+ return void 0;
3124
+ }
3125
+ const session = parsed.session;
3126
+ if (typeof session !== "object" || session === null || !("id" in session)) {
3127
+ return void 0;
3128
+ }
3129
+ const id = session.id;
3130
+ return typeof id === "string" && id.startsWith(RC_SESSION_ID_PREFIX) ? id : void 0;
3111
3131
  }
3112
- function dialTarget(options, defaultPort) {
3113
- return { host: typeof options.host === "string" ? options.host : "", port: typeof options.port === "number" ? options.port : defaultPort };
3132
+ function isRecord2(value) {
3133
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3114
3134
  }
3115
- function literalDial(target) {
3116
- return net2.connect({ host: target.host, port: target.port });
3135
+ function parseJsonObject(body) {
3136
+ let parsed;
3137
+ try {
3138
+ parsed = JSON.parse(body);
3139
+ } catch {
3140
+ return void 0;
3141
+ }
3142
+ return isRecord2(parsed) ? parsed : void 0;
3117
3143
  }
3118
- async function reservedDial(target) {
3119
- for (let attempt = 0; ; attempt += 1) {
3120
- const outcome = await dialOnce({ host: target.host, port: target.port, localPort: upstreamLocalPort(), lookup: realAddressLookup });
3121
- if (outcome.error === null) {
3122
- return outcome;
3144
+ function nonEmptyString(record, field) {
3145
+ const value = record === void 0 ? void 0 : record[field];
3146
+ return typeof value === "string" && value !== "" ? value : void 0;
3147
+ }
3148
+ function finiteNumber(record, field) {
3149
+ const value = record === void 0 ? void 0 : record[field];
3150
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3151
+ }
3152
+ function excerpt(text) {
3153
+ return text.length <= RC_PENDING_SUMMARY_EXCERPT_CHARS ? text : `${text.slice(0, RC_PENDING_SUMMARY_EXCERPT_CHARS)}...`;
3154
+ }
3155
+ function payloadEventsOf(body) {
3156
+ const parsed = parseJsonObject(body);
3157
+ const events = parsed === void 0 ? void 0 : parsed.events;
3158
+ if (!Array.isArray(events)) {
3159
+ return [];
3160
+ }
3161
+ const payloads = [];
3162
+ for (const event of events) {
3163
+ if (!isRecord2(event)) {
3164
+ continue;
3123
3165
  }
3124
- const held = outcome.error.code !== void 0 && HELD_PORT_ERRNOS.has(outcome.error.code);
3125
- if (!held || attempt === UPSTREAM_LOCAL_PORT_COUNT - 1) {
3126
- return outcome;
3166
+ const payload = event.payload;
3167
+ if (!isRecord2(payload)) {
3168
+ continue;
3127
3169
  }
3170
+ payloads.push(payload);
3128
3171
  }
3172
+ return payloads;
3129
3173
  }
3130
- function requestServername(options, fallback) {
3131
- return "servername" in options && typeof options.servername === "string" && options.servername !== "" ? options.servername : fallback;
3132
- }
3133
- var ExemptHttpAgent = class extends http.Agent {
3134
- // A reserved-port dial that has to retry cannot produce its socket synchronously, so it settles first and arrives through the callback (node's documented asynchronous createConnection form); a literal dial has nothing to retry and keeps node's own synchronous shape.
3135
- createConnection(options, oncreate) {
3136
- const target = dialTarget(options, HTTP_PORT);
3137
- if (net2.isIP(target.host) !== 0) {
3138
- return literalDial(target);
3139
- }
3140
- void reservedDial(target).then((outcome) => {
3141
- oncreate?.(outcome.error, outcome.socket);
3142
- });
3174
+ function pendingOfControlRequestPayload(payload) {
3175
+ if (payload.type !== "control_request") {
3143
3176
  return void 0;
3144
3177
  }
3145
- };
3146
- var ExemptTlsAgent = class extends https.Agent {
3147
- // The same split as the plain sibling: a literal dial wraps TLS around the socket node's own agent would have returned, a reserved dial settles (retries included) first and arrives through the callback, which is exactly where node's own agent wraps its own dials.
3148
- createConnection(options, oncreate) {
3149
- const target = dialTarget(options, HTTPS_PORT);
3150
- if (net2.isIP(target.host) !== 0) {
3151
- return tls.connect({ socket: literalDial(target), servername: requestServername(options, target.host), ...tlsFacts(options) });
3152
- }
3153
- void reservedDial(target).then((outcome) => {
3154
- if (outcome.error !== null) {
3155
- oncreate?.(outcome.error, outcome.socket);
3156
- return;
3157
- }
3158
- oncreate?.(null, tls.connect({ socket: outcome.socket, servername: requestServername(options, target.host), ...tlsFacts(options) }));
3159
- });
3178
+ const requestId = nonEmptyString(payload, "request_id") ?? nonEmptyString(payload, "uuid");
3179
+ if (requestId === void 0) {
3160
3180
  return void 0;
3161
3181
  }
3162
- };
3163
- function tlsFacts(options) {
3164
- const facts = {};
3165
- if ("ca" in options && Array.isArray(options.ca)) {
3166
- facts.ca = options.ca;
3182
+ const request4 = isRecord2(payload.request) ? payload.request : void 0;
3183
+ const subtype = nonEmptyString(request4, "subtype");
3184
+ const type = subtype ?? "control_request";
3185
+ let summary = "";
3186
+ if (subtype === "can_use_tool") {
3187
+ const toolName = nonEmptyString(request4, "tool_name") ?? "unknown tool";
3188
+ const input = request4 === void 0 ? void 0 : request4.input;
3189
+ summary = input === void 0 ? toolName : `${toolName} ${excerpt(JSON.stringify(input))}`;
3190
+ } else if (request4 !== void 0) {
3191
+ summary = excerpt(JSON.stringify(request4));
3167
3192
  }
3168
- if ("rejectUnauthorized" in options && typeof options.rejectUnauthorized === "boolean") {
3169
- facts.rejectUnauthorized = options.rejectUnauthorized;
3193
+ return { requestId, type, summary };
3194
+ }
3195
+ function answeredRequestIdsOfControlResponsePayload(payload) {
3196
+ if (payload.type !== "control_response") {
3197
+ return [];
3170
3198
  }
3171
- return facts;
3199
+ const response = isRecord2(payload.response) ? payload.response : void 0;
3200
+ const id = nonEmptyString(response, "request_id") ?? nonEmptyString(payload, "request_id") ?? nonEmptyString(payload, "uuid");
3201
+ return id === void 0 ? [] : [id];
3172
3202
  }
3173
- function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
3174
- const agent = new ExemptTlsAgent();
3175
- return {
3176
- writeEvents: async (sessionId, headers, body) => await new Promise((resolve, reject) => {
3177
- const options = {
3178
- host: target.host,
3179
- port: target.port,
3180
- method: "POST",
3181
- path: `/v1/code/sessions/${encodeURIComponent(sessionId)}/events`,
3182
- // 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.
3183
- headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
3184
- agent
3185
- };
3186
- options.servername = target.servername ?? target.host;
3187
- if (target.ca !== void 0) {
3188
- options.ca = [...target.ca];
3203
+ function parseRcRequestBody(kind, body) {
3204
+ if (kind === "heartbeat") {
3205
+ return { controlRequests: [], answeredRequestIds: [], idleSeconds: finiteNumber(parseJsonObject(body), "idle_seconds"), workerStatus: void 0 };
3206
+ }
3207
+ if (kind === "worker-registration") {
3208
+ return { controlRequests: [], answeredRequestIds: [], idleSeconds: void 0, workerStatus: nonEmptyString(parseJsonObject(body), "status") };
3209
+ }
3210
+ const controlRequests = [];
3211
+ const answeredRequestIds = [];
3212
+ for (const payload of payloadEventsOf(body)) {
3213
+ const pending = pendingOfControlRequestPayload(payload);
3214
+ if (pending !== void 0) {
3215
+ controlRequests.push({ requestId: pending.requestId, type: pending.type, summary: pending.summary });
3216
+ continue;
3217
+ }
3218
+ answeredRequestIds.push(...answeredRequestIdsOfControlResponsePayload(payload));
3219
+ }
3220
+ return { controlRequests, answeredRequestIds, idleSeconds: void 0, workerStatus: void 0 };
3221
+ }
3222
+ function createRcSessionTracker(deps) {
3223
+ const entries = /* @__PURE__ */ new Map();
3224
+ const expire = (id) => {
3225
+ entries.delete(id);
3226
+ };
3227
+ const expirePendings = (entry, now) => {
3228
+ for (const [requestId, pending] of entry.pending) {
3229
+ if (now - pending.observedAt >= RC_PENDING_DEADLINE_MS) {
3230
+ entry.pending.delete(requestId);
3189
3231
  }
3190
- if (target.rejectUnauthorized !== void 0) {
3191
- options.rejectUnauthorized = target.rejectUnauthorized;
3232
+ }
3233
+ };
3234
+ const sweep = () => {
3235
+ for (const [id, entry] of entries) {
3236
+ if (deps.now() - entry.lastSeenAt >= deps.idleMs) {
3237
+ expire(id);
3238
+ continue;
3192
3239
  }
3193
- const request4 = https.request(options, (response) => {
3194
- const chunks = [];
3195
- response.on("data", (chunk) => {
3196
- chunks.push(chunk);
3197
- });
3198
- response.on("end", () => {
3199
- resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
3200
- });
3201
- });
3202
- request4.once("error", reject);
3203
- request4.end(body);
3204
- })
3240
+ expirePendings(entry, deps.now());
3241
+ }
3205
3242
  };
3206
- }
3207
- function realConnectEffects() {
3208
- const plainAgent = new ExemptHttpAgent();
3209
- const tlsAgent = new ExemptTlsAgent();
3210
- const listenOnce = async (port, onSocket) => await new Promise((resolve, reject) => {
3211
- const accepted = /* @__PURE__ */ new Set();
3212
- const server = net2.createServer((socket) => {
3213
- accepted.add(socket);
3214
- socket.on("close", () => {
3215
- accepted.delete(socket);
3216
- });
3217
- onSocket(socket);
3218
- });
3219
- server.once("error", reject);
3220
- server.listen(port, "127.0.0.1", () => {
3221
- const address = server.address();
3222
- const bound = typeof address === "object" && address !== null ? address.port : 0;
3223
- if (bound === 0) {
3224
- reject(new Error("could not bind a loopback port"));
3225
- return;
3226
- }
3227
- resolve({
3228
- port: bound,
3229
- close: async () => {
3230
- await new Promise((closeResolve) => {
3231
- server.close(() => {
3232
- closeResolve(void 0);
3233
- });
3234
- for (const socket of accepted) {
3235
- socket.destroy();
3236
- }
3237
- });
3238
- }
3239
- });
3240
- });
3241
- });
3242
- return {
3243
- listenLoopback: async (preferredPort, onSocket) => {
3244
- if (preferredPort !== void 0) {
3245
- try {
3246
- return await listenOnce(preferredPort, onSocket);
3247
- } catch {
3248
- }
3243
+ const timer = setInterval(sweep, deps.idleMs);
3244
+ timer.unref();
3245
+ const birth = (id, facts) => {
3246
+ const record = { id, createdAt: deps.now(), lastSeenAt: deps.now(), ...facts, pending: /* @__PURE__ */ new Map(), workerState: void 0, workerIdleSeconds: void 0, sequenceNum: void 0 };
3247
+ entries.set(id, record);
3248
+ return record;
3249
+ };
3250
+ const observeExchange = (request4) => {
3251
+ const method = (request4.method ?? "").toUpperCase();
3252
+ const path29 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
3253
+ if (!path29.startsWith(RC_SESSIONS_PATH_PREFIX)) {
3254
+ return void 0;
3255
+ }
3256
+ const tail = path29.slice(RC_SESSIONS_PATH_PREFIX.length);
3257
+ const facts = {
3258
+ oauthAuthorization: oauthAuthorizationOf(request4.headers),
3259
+ anthropicVersion: singleHeader(request4.headers, "anthropic-version"),
3260
+ anthropicClientPlatform: singleHeader(request4.headers, "anthropic-client-platform")
3261
+ };
3262
+ if (tail === "") {
3263
+ if (method !== "POST") {
3264
+ return void 0;
3249
3265
  }
3250
- return await listenOnce(0, onSocket);
3251
- },
3252
- createTlsAcceptor: (leaf, onSecure, alpnProtocols) => {
3253
- let pendingContext = "";
3254
- const server = tls.createServer(
3255
- { key: leaf.keyPem, cert: leaf.certPem, ...alpnProtocols === void 0 ? {} : { ALPNProtocols: [...alpnProtocols] } },
3256
- (secure) => {
3257
- onSecure(secure, pendingContext);
3258
- }
3259
- );
3260
- server.on("clientError", (_error, socket) => {
3261
- socket.destroy();
3262
- });
3266
+ let accepted2 = false;
3267
+ let buffered2 = 0;
3268
+ let body = "";
3263
3269
  return {
3264
- // tls.Server is a net.Server whose connection listener wraps the raw socket in the TLS handshake, and emitting the event by hand is what runs that listener on a socket this process already owns (the CONNECT half of the connection) rather than one the server accepted itself.
3265
- accept: (socket, context) => {
3266
- pendingContext = context;
3267
- server.emit("connection", socket);
3270
+ onResponse: (status) => {
3271
+ accepted2 = isSuccessful(status);
3268
3272
  },
3269
- close: () => {
3270
- server.close();
3271
- }
3272
- };
3273
- },
3274
- createSniTlsAcceptor: (hosts, leafFor, onSecure) => {
3275
- const contexts = new Map(
3276
- hosts.map((host) => {
3277
- const leaf = leafFor(host);
3278
- return [host, tls.createSecureContext({ key: leaf.keyPem, cert: leaf.certPem })];
3279
- })
3280
- );
3281
- const server = tls.createServer(
3282
- {
3283
- // No default key or cert exists, so the SNI selection is the only source of credentials: an unserved name fails its handshake here, and a client that sent no SNI at all never reaches the selection and fails for want of any certificate, which is exactly the fail-closed shape this surface wants.
3284
- SNICallback: (servername, callback) => {
3285
- const context = contexts.get(servername);
3286
- callback(context === void 0 ? new Error(`the transparent surface serves no host named ${servername}`) : null, context);
3273
+ onBodyChunk: (chunk) => {
3274
+ if (!accepted2 || buffered2 >= RC_CREATE_RESPONSE_CAP_BYTES) {
3275
+ return;
3287
3276
  }
3277
+ const text = Buffer.from(chunk).toString("utf8");
3278
+ body = body + text.slice(0, RC_CREATE_RESPONSE_CAP_BYTES - buffered2);
3279
+ buffered2 = body.length;
3288
3280
  },
3289
- (secure) => {
3290
- onSecure(secure, typeof secure.servername === "string" ? secure.servername : "");
3291
- }
3292
- );
3293
- server.on("clientError", (_error, socket) => {
3294
- socket.destroy();
3295
- });
3296
- return {
3297
- // The same hand-off the single-leaf acceptor uses: emitting the connection event runs the TLS server's own connection listener on a socket this process already owns (the redirect half of the connection) rather than one the server accepted itself.
3298
- accept: (socket) => {
3299
- server.emit("connection", socket);
3300
- },
3301
- close: () => {
3302
- server.close();
3281
+ onEnd: () => {
3282
+ if (!accepted2) {
3283
+ return;
3284
+ }
3285
+ const id2 = sessionCreateId(body);
3286
+ if (id2 === void 0) {
3287
+ return;
3288
+ }
3289
+ birth(id2, facts);
3303
3290
  }
3304
3291
  };
3305
- },
3306
- createHttpSession: (handler, onUpgrade) => {
3307
- const server = http.createServer(handler);
3308
- server.on("clientError", (_error, socket) => {
3309
- socket.destroy();
3310
- });
3311
- if (onUpgrade !== void 0) {
3312
- server.on("upgrade", (request4, socket, head) => {
3313
- onUpgrade(request4, socket, head);
3314
- });
3292
+ }
3293
+ const rest = tail.slice(1);
3294
+ const [id] = rest.split("/");
3295
+ if (id?.startsWith(RC_SESSION_ID_PREFIX) !== true) {
3296
+ return void 0;
3297
+ }
3298
+ const entry = entries.get(id) ?? birth(id, facts);
3299
+ entry.lastSeenAt = deps.now();
3300
+ entry.oauthAuthorization = facts.oauthAuthorization ?? entry.oauthAuthorization;
3301
+ entry.anthropicVersion = facts.anthropicVersion ?? entry.anthropicVersion;
3302
+ entry.anthropicClientPlatform = facts.anthropicClientPlatform ?? entry.anthropicClientPlatform;
3303
+ const bodyKind = method === "POST" && rest === `${id}/worker/events` ? "worker-events" : method === "POST" && rest === `${id}/events` ? "client-events" : method === "POST" && rest === `${id}/worker/heartbeat` ? "heartbeat" : method === "PUT" && rest === `${id}/worker` ? "worker-registration" : void 0;
3304
+ let chunks = [];
3305
+ let buffered = 0;
3306
+ let oversized = false;
3307
+ let requestEnded = false;
3308
+ let accepted;
3309
+ let filed = false;
3310
+ const file = () => {
3311
+ if (filed || accepted !== true || !requestEnded || bodyKind === void 0) {
3312
+ return;
3315
3313
  }
3316
- return {
3317
- serve: (socket) => {
3318
- server.emit("connection", socket);
3314
+ filed = true;
3315
+ const factsOfBody = oversized ? void 0 : parseRcRequestBody(bodyKind, Buffer.concat(chunks).toString("utf8"));
3316
+ chunks = [];
3317
+ const entryNow = entries.get(id);
3318
+ if (entryNow === void 0 || factsOfBody === void 0) {
3319
+ return;
3320
+ }
3321
+ const now = deps.now();
3322
+ for (const controlRequest of factsOfBody.controlRequests) {
3323
+ entryNow.pending.set(controlRequest.requestId, { sessionId: id, ...controlRequest, observedAt: now });
3324
+ }
3325
+ for (const requestId of factsOfBody.answeredRequestIds) {
3326
+ entryNow.pending.delete(requestId);
3327
+ }
3328
+ if (factsOfBody.idleSeconds !== void 0) {
3329
+ entryNow.workerIdleSeconds = { value: factsOfBody.idleSeconds, observedAt: now };
3330
+ }
3331
+ if (factsOfBody.workerStatus !== void 0) {
3332
+ entryNow.workerState = { value: factsOfBody.workerStatus, observedAt: now };
3333
+ }
3334
+ };
3335
+ const archiving = method === "POST" && rest === `${id}/archive`;
3336
+ return {
3337
+ ...bodyKind === void 0 ? {} : {
3338
+ onRequestChunk: (chunk) => {
3339
+ if (oversized) {
3340
+ return;
3341
+ }
3342
+ chunks.push(Buffer.from(chunk));
3343
+ buffered += chunk.length;
3344
+ if (buffered > RC_REQUEST_PARSE_CAP_BYTES) {
3345
+ oversized = true;
3346
+ chunks = [];
3347
+ }
3319
3348
  },
3320
- close: () => {
3321
- server.close();
3322
- server.closeAllConnections();
3349
+ onRequestEnd: () => {
3350
+ requestEnded = true;
3351
+ file();
3323
3352
  }
3324
- };
3325
- },
3326
- connectTlsUpstream: async (host, port, clientAlpn) => {
3327
- const dialled = await reservedDial({ host, port });
3328
- if (dialled.error !== null) {
3329
- throw dialled.error;
3353
+ },
3354
+ onResponse: (status, headers) => {
3355
+ if (singleHeader(headers, CONFLICT_REASON_HEADER) !== void 0 || archiving && isSuccessful(status)) {
3356
+ expire(id);
3357
+ return;
3358
+ }
3359
+ accepted = isSuccessful(status);
3360
+ file();
3361
+ },
3362
+ onBodyChunk: () => {
3363
+ },
3364
+ onEnd: () => {
3330
3365
  }
3331
- return await new Promise((resolve, reject) => {
3332
- const secure = tls.connect({ socket: dialled.socket, servername: host, ...clientAlpn === void 0 ? {} : { ALPNProtocols: [clientAlpn] } });
3333
- secure.once("secureConnect", () => {
3334
- resolve(secure);
3335
- });
3336
- secure.once("error", reject);
3337
- });
3366
+ };
3367
+ };
3368
+ const summaryOf = ({ id, createdAt, lastSeenAt }) => ({ id, createdAt, lastSeenAt });
3369
+ const pendingSummaryOf = ({ sessionId, requestId, type, summary, observedAt }) => ({ sessionId, requestId, type, summary, observedAt });
3370
+ const sortedEntries = () => [...entries.values()].sort((left, right) => left.createdAt - right.createdAt || (left.id < right.id ? -1 : 1));
3371
+ const pendingSorted = (entry) => [...entry.pending.values()].sort((left, right) => left.observedAt - right.observedAt || (left.requestId < right.requestId ? -1 : 1)).map(pendingSummaryOf);
3372
+ return {
3373
+ observeExchange,
3374
+ list: () => {
3375
+ sweep();
3376
+ return sortedEntries().map(summaryOf);
3338
3377
  },
3339
- connectTcp: async (host, port) => await new Promise((resolve, reject) => {
3340
- const socket = net2.connect({ host, port });
3341
- socket.once("connect", () => {
3342
- resolve(socket);
3343
- });
3344
- socket.once("error", reject);
3345
- }),
3346
- forwardHttp: (target, request4, response, observer) => {
3347
- const headers = forwardableHeaders(request4.headers);
3348
- const options = {
3349
- host: target.host,
3350
- port: target.port,
3351
- lookup: realAddressLookup,
3352
- method: request4.method,
3353
- path: request4.url,
3354
- headers,
3355
- agent: target.tls ? tlsAgent : plainAgent
3356
- };
3357
- if (target.tls) {
3358
- options.servername = target.servername ?? target.host;
3359
- if (target.ca !== void 0) {
3360
- options.ca = [...target.ca];
3361
- }
3362
- if (target.rejectUnauthorized !== void 0) {
3363
- options.rejectUnauthorized = target.rejectUnauthorized;
3364
- }
3378
+ statusOf: (sessionId) => {
3379
+ sweep();
3380
+ return sortedEntries().filter((entry) => sessionId === void 0 || entry.id === sessionId).map((entry) => ({ ...summaryOf(entry), workerState: entry.workerState, workerIdleSeconds: entry.workerIdleSeconds, pending: pendingSorted(entry) }));
3381
+ },
3382
+ pendingOf: (sessionId) => {
3383
+ sweep();
3384
+ return sortedEntries().filter((entry) => sessionId === void 0 || entry.id === sessionId).flatMap((entry) => pendingSorted(entry));
3385
+ },
3386
+ credentialOf: (sessionId) => {
3387
+ const entry = entries.get(sessionId);
3388
+ return entry === void 0 ? void 0 : { authorization: entry.oauthAuthorization, anthropicVersion: entry.anthropicVersion, anthropicClientPlatform: entry.anthropicClientPlatform };
3389
+ },
3390
+ noteSequenceNums: (sessionId, sequenceNums) => {
3391
+ const entry = entries.get(sessionId);
3392
+ if (entry === void 0) {
3393
+ return;
3365
3394
  }
3366
- const onUpstreamResponse = (upstreamResponse) => {
3367
- observer?.onResponse(upstreamResponse.statusCode ?? HTTP_BAD_GATEWAY, upstreamResponse.headers);
3368
- response.writeHead(upstreamResponse.statusCode ?? HTTP_BAD_GATEWAY, forwardableHeaders(upstreamResponse.headers));
3369
- response.flushHeaders();
3370
- if (observer !== void 0) {
3371
- upstreamResponse.on("data", (chunk) => {
3372
- observer.onResponseChunk(chunk);
3373
- });
3374
- }
3375
- upstreamResponse.pipe(response);
3376
- };
3377
- const upstream = target.tls ? https.request(options, onUpstreamResponse) : http.request(options, onUpstreamResponse);
3378
- upstream.on("error", (error) => {
3379
- if (response.headersSent) {
3380
- response.destroy(error);
3381
- return;
3395
+ for (const sequenceNum of sequenceNums) {
3396
+ entry.sequenceNum = entry.sequenceNum === void 0 || sequenceNum > entry.sequenceNum ? sequenceNum : entry.sequenceNum;
3397
+ }
3398
+ },
3399
+ sequenceNumOf: (sessionId) => entries.get(sessionId)?.sequenceNum,
3400
+ fileStreamEvent: (sessionId, event) => {
3401
+ const entry = entries.get(sessionId);
3402
+ if (entry === void 0) {
3403
+ return;
3404
+ }
3405
+ entry.sequenceNum = entry.sequenceNum === void 0 || event.sequence_num > entry.sequenceNum ? event.sequence_num : entry.sequenceNum;
3406
+ if (!isRecord2(event.payload)) {
3407
+ return;
3408
+ }
3409
+ const pending = pendingOfControlRequestPayload(event.payload);
3410
+ if (pending !== void 0) {
3411
+ if (event.source === RC_STREAM_WORKER_SOURCE) {
3412
+ entry.pending.set(pending.requestId, { sessionId, ...pending, observedAt: deps.now() });
3382
3413
  }
3383
- const body = `agent-shim headroom: upstream unreachable (${error.message})`;
3384
- response.writeHead(HTTP_BAD_GATEWAY, { "content-type": "text/plain", "content-length": String(body.length) });
3385
- response.end(body);
3386
- });
3387
- if (observer !== void 0) {
3388
- request4.on("data", (chunk) => {
3389
- observer.onRequestChunk(chunk);
3414
+ return;
3415
+ }
3416
+ for (const requestId of answeredRequestIdsOfControlResponsePayload(event.payload)) {
3417
+ entry.pending.delete(requestId);
3418
+ }
3419
+ },
3420
+ completePending: (sessionId, requestId) => {
3421
+ entries.get(sessionId)?.pending.delete(requestId);
3422
+ },
3423
+ close: () => {
3424
+ clearInterval(timer);
3425
+ }
3426
+ };
3427
+ }
3428
+ function observingRoutedRoute(route, tracker, hooks) {
3429
+ return {
3430
+ ...route,
3431
+ serve: async (request4, response) => {
3432
+ const observed = tracker.observeExchange({ method: request4.method, url: request4.url, headers: request4.headers });
3433
+ if (observed?.onRequestChunk !== void 0) {
3434
+ request4.body.on("data", (chunk) => {
3435
+ observed.onRequestChunk?.(chunk);
3390
3436
  });
3391
- if (observer.onRequestEnd !== void 0) {
3392
- request4.on("end", () => {
3393
- observer.onRequestEnd?.();
3394
- });
3395
- }
3396
- response.on("close", () => {
3397
- observer.onEnd();
3437
+ request4.body.on("end", () => {
3438
+ observed.onRequestEnd?.();
3398
3439
  });
3399
3440
  }
3400
- request4.pipe(upstream);
3401
- request4.socket.setNoDelay(true);
3402
- upstream.on("socket", (socket) => {
3403
- socket.setNoDelay(true);
3404
- });
3441
+ await route.serve(request4, observed === void 0 ? response : teeRoutedResponse(response, observed, hooks?.onExchangeSettled));
3405
3442
  }
3406
3443
  };
3407
3444
  }
3408
-
3409
- // src/codex/http.ts
3410
- var HTTP_STATUS = {
3411
- ok: 200,
3412
- badRequest: 400,
3413
- unauthorized: 401,
3414
- forbidden: 403,
3415
- notFound: 404,
3416
- methodNotAllowed: 405,
3417
- payloadTooLarge: 413,
3418
- tooManyRequests: 429,
3419
- /** Not sent to anyone: the client is gone. Recorded for a response abandoned because the client disconnected, following the convention nginx uses for the same case. */
3420
- clientClosedRequest: 499,
3421
- internalServerError: 500,
3422
- badGateway: 502
3423
- };
3424
-
3425
- // src/frontdoor/rcSessions.ts
3426
- var RC_SESSIONS_PATH_PREFIX = "/v1/code/sessions";
3427
- var RC_SESSION_ID_PREFIX = "cse_";
3428
- var RC_IDLE_EXPIRY_MS = 45e3;
3429
- var RC_CREATE_RESPONSE_CAP_BYTES = 8192;
3430
- var CONFLICT_REASON_HEADER = "x-ccr-conflict-reason";
3431
- var RC_PENDING_DEADLINE_MS = 14e3;
3432
- var RC_REQUEST_PARSE_CAP_BYTES = 262144;
3433
- var RC_PENDING_SUMMARY_EXCERPT_CHARS = 300;
3434
- var SUCCESS_STATUS_MAX_EXCLUSIVE = 300;
3435
- function isSuccessful(status) {
3436
- return status >= HTTP_STATUS.ok && status < SUCCESS_STATUS_MAX_EXCLUSIVE;
3445
+ function teeRoutedResponse(response, observer, onSettled) {
3446
+ return {
3447
+ get headersSent() {
3448
+ return response.headersSent;
3449
+ },
3450
+ start: (status, headers) => {
3451
+ observer.onResponse(status, headers);
3452
+ response.start(status, headers);
3453
+ },
3454
+ flush: () => {
3455
+ response.flush();
3456
+ },
3457
+ write: async (chunk) => {
3458
+ observer.onBodyChunk(typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk);
3459
+ await response.write(chunk);
3460
+ },
3461
+ end: () => {
3462
+ response.end();
3463
+ observer.onEnd();
3464
+ onSettled?.();
3465
+ },
3466
+ destroy: () => {
3467
+ response.destroy();
3468
+ observer.onEnd();
3469
+ onSettled?.();
3470
+ }
3471
+ };
3437
3472
  }
3438
- function singleHeader(headers, name) {
3439
- const value = headers[name];
3440
- return typeof value === "string" ? value : void 0;
3473
+ function rcObserverAsPassthrough(observer) {
3474
+ return {
3475
+ onRequestChunk: (chunk) => {
3476
+ observer.onRequestChunk?.(chunk);
3477
+ },
3478
+ ...observer.onRequestEnd === void 0 ? {} : {
3479
+ onRequestEnd: () => {
3480
+ observer.onRequestEnd?.();
3481
+ }
3482
+ },
3483
+ onResponse: (status, headers) => {
3484
+ observer.onResponse(status, headers);
3485
+ },
3486
+ onResponseChunk: (chunk) => {
3487
+ observer.onBodyChunk(chunk);
3488
+ },
3489
+ onEnd: () => {
3490
+ observer.onEnd();
3491
+ }
3492
+ };
3441
3493
  }
3442
- var OAUTH_AUTHORIZATION_PREFIX = "Bearer sk-ant-oat";
3443
- function oauthAuthorizationOf(headers) {
3444
- const value = singleHeader(headers, "authorization");
3445
- return value?.startsWith(OAUTH_AUTHORIZATION_PREFIX) === true ? value : void 0;
3494
+ function buildRcUserMessagePayload(uuid, sessionId, text) {
3495
+ return {
3496
+ type: "user",
3497
+ uuid,
3498
+ session_id: sessionId,
3499
+ parent_tool_use_id: null,
3500
+ message: { role: "user", content: text }
3501
+ };
3446
3502
  }
3447
- function sessionCreateId(body) {
3503
+ function buildRcEventWriteBody(payload) {
3504
+ return { events: [{ payload }] };
3505
+ }
3506
+ function sequenceNumsOf(body) {
3448
3507
  let parsed;
3449
3508
  try {
3450
3509
  parsed = JSON.parse(body);
3451
3510
  } catch {
3452
3511
  return void 0;
3453
3512
  }
3454
- if (typeof parsed !== "object" || parsed === null || !("session" in parsed)) {
3513
+ if (typeof parsed !== "object" || parsed === null || !("results" in parsed) || !Array.isArray(parsed.results)) {
3455
3514
  return void 0;
3456
3515
  }
3457
- const session = parsed.session;
3458
- if (typeof session !== "object" || session === null || !("id" in session)) {
3459
- return void 0;
3516
+ const sequenceNums = [];
3517
+ const results = parsed.results;
3518
+ for (const result of results) {
3519
+ if (typeof result !== "object" || result === null || !("sequence_num" in result)) {
3520
+ return void 0;
3521
+ }
3522
+ const sequenceNum = result.sequence_num;
3523
+ if (typeof sequenceNum === "number") {
3524
+ sequenceNums.push(sequenceNum);
3525
+ } else if (typeof sequenceNum === "string" && /^[0-9]+$/.test(sequenceNum)) {
3526
+ sequenceNums.push(Number(sequenceNum));
3527
+ } else {
3528
+ return void 0;
3529
+ }
3460
3530
  }
3461
- const id = session.id;
3462
- return typeof id === "string" && id.startsWith(RC_SESSION_ID_PREFIX) ? id : void 0;
3463
- }
3464
- function isRecord2(value) {
3465
- return typeof value === "object" && value !== null && !Array.isArray(value);
3531
+ return sequenceNums.length === 0 ? void 0 : sequenceNums;
3466
3532
  }
3467
- function parseJsonObject(body) {
3468
- let parsed;
3469
- try {
3470
- parsed = JSON.parse(body);
3471
- } catch {
3472
- return void 0;
3533
+ function rcEventWriteResultFromAnswer(answer) {
3534
+ if (!isSuccessful(answer.status)) {
3535
+ if (answer.status === HTTP_STATUS.unauthorized) {
3536
+ return { ok: false, message: `the API refused the observed Authorization bearer for this session (HTTP 401): the bearer went stale after the door last saw the session's traffic. The session's own client refreshes it on its next call, which the door will observe, so retry once Remote Control has been active again. API answer: ${answer.body}` };
3537
+ }
3538
+ return { ok: false, message: `the API answered HTTP ${String(answer.status)} to the injected event: ${answer.body}` };
3473
3539
  }
3474
- return isRecord2(parsed) ? parsed : void 0;
3475
- }
3476
- function nonEmptyString(record, field) {
3477
- const value = record === void 0 ? void 0 : record[field];
3478
- return typeof value === "string" && value !== "" ? value : void 0;
3540
+ const sequenceNums = sequenceNumsOf(answer.body);
3541
+ if (sequenceNums === void 0) {
3542
+ return { ok: false, message: `the API accepted the injected event but its answer named no results[].sequence_num, so delivery cannot be confirmed: ${answer.body}` };
3543
+ }
3544
+ return { ok: true, sequenceNums };
3479
3545
  }
3480
- function finiteNumber(record, field) {
3481
- const value = record === void 0 ? void 0 : record[field];
3482
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3546
+ function noOAuthCredentialResult(sessionId) {
3547
+ return { ok: false, message: `no claude.ai OAuth bearer has been observed for session ${sessionId}: only worker calls crossed this door, and the worker JWT they carry is answered 401 on the client half. Reconnect Remote Control through this door and the create's own bearer will be observed` };
3483
3548
  }
3484
- function excerpt(text) {
3485
- return text.length <= RC_PENDING_SUMMARY_EXCERPT_CHARS ? text : `${text.slice(0, RC_PENDING_SUMMARY_EXCERPT_CHARS)}...`;
3549
+ function rcSessionNotObservedMessage(sessionId) {
3550
+ return `the front door has not observed Remote Control session ${sessionId}: it may never have passed through this door, or it ended or expired (an entry lives only a bounded idle period past its last observed traffic)`;
3486
3551
  }
3487
- function payloadEventsOf(body) {
3488
- const parsed = parseJsonObject(body);
3489
- const events = parsed === void 0 ? void 0 : parsed.events;
3490
- if (!Array.isArray(events)) {
3491
- return [];
3492
- }
3493
- const payloads = [];
3494
- for (const event of events) {
3495
- if (!isRecord2(event)) {
3496
- continue;
3497
- }
3498
- const payload = event.payload;
3499
- if (!isRecord2(payload)) {
3500
- continue;
3501
- }
3502
- payloads.push(payload);
3552
+ function rcWriteHeaders(credential) {
3553
+ const headers = { "content-type": "application/json" };
3554
+ if (credential.authorization !== void 0) {
3555
+ headers.authorization = credential.authorization;
3503
3556
  }
3504
- return payloads;
3557
+ if (credential.anthropicVersion !== void 0) {
3558
+ headers["anthropic-version"] = credential.anthropicVersion;
3559
+ }
3560
+ if (credential.anthropicClientPlatform !== void 0) {
3561
+ headers["anthropic-client-platform"] = credential.anthropicClientPlatform;
3562
+ }
3563
+ return headers;
3505
3564
  }
3506
- function pendingOfControlRequestPayload(payload) {
3507
- if (payload.type !== "control_request") {
3508
- return void 0;
3565
+ async function injectRcUserMessage(deps, sessionId, text) {
3566
+ const credential = deps.credentialOf(sessionId);
3567
+ if (credential === void 0) {
3568
+ return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
3509
3569
  }
3510
- const requestId = nonEmptyString(payload, "request_id") ?? nonEmptyString(payload, "uuid");
3511
- if (requestId === void 0) {
3512
- return void 0;
3570
+ if (credential.authorization === void 0) {
3571
+ return noOAuthCredentialResult(sessionId);
3513
3572
  }
3514
- const request4 = isRecord2(payload.request) ? payload.request : void 0;
3515
- const subtype = nonEmptyString(request4, "subtype");
3516
- const type = subtype ?? "control_request";
3517
- let summary = "";
3518
- if (subtype === "can_use_tool") {
3519
- const toolName = nonEmptyString(request4, "tool_name") ?? "unknown tool";
3520
- const input = request4 === void 0 ? void 0 : request4.input;
3521
- summary = input === void 0 ? toolName : `${toolName} ${excerpt(JSON.stringify(input))}`;
3522
- } else if (request4 !== void 0) {
3523
- summary = excerpt(JSON.stringify(request4));
3573
+ const body = JSON.stringify(buildRcEventWriteBody(buildRcUserMessagePayload(deps.newUuid(), sessionId, text)));
3574
+ let answer;
3575
+ try {
3576
+ answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3577
+ } catch (error) {
3578
+ return { ok: false, message: `the door could not reach the API host to inject into session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
3524
3579
  }
3525
- return { requestId, type, summary };
3580
+ const result = rcEventWriteResultFromAnswer(answer);
3581
+ if (result.ok) {
3582
+ deps.noteSequenceNums(sessionId, result.sequenceNums);
3583
+ }
3584
+ return result;
3526
3585
  }
3527
- function answeredRequestIdsOfControlResponsePayload(payload) {
3528
- if (payload.type !== "control_response") {
3529
- return [];
3586
+ function buildRcControlResponsePayload(requestId, decision) {
3587
+ const result = decision.approve ? { behavior: "allow" } : { behavior: "deny", message: decision.message ?? "" };
3588
+ return { type: "control_response", response: { subtype: "success", request_id: requestId, response: result } };
3589
+ }
3590
+ var RC_PERMISSION_MODES = ["default", "acceptEdits", "bypassPermissions", "plan", "dontAsk", "auto"];
3591
+ function isRcPermissionMode(value) {
3592
+ return RC_PERMISSION_MODES.some((mode) => mode === value);
3593
+ }
3594
+ function buildRcInterruptPayload(requestId) {
3595
+ return { type: "control_request", request_id: requestId, request: { subtype: "interrupt" } };
3596
+ }
3597
+ function buildRcSetModelPayload(requestId, model) {
3598
+ return { type: "control_request", request_id: requestId, request: { subtype: "set_model", model } };
3599
+ }
3600
+ function buildRcSetPermissionModePayload(requestId, mode) {
3601
+ return { type: "control_request", request_id: requestId, request: { subtype: "set_permission_mode", mode } };
3602
+ }
3603
+ async function answerRcControlRequest(deps, sessionId, requestId, decision) {
3604
+ const credential = deps.credentialOf(sessionId);
3605
+ if (credential === void 0) {
3606
+ return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
3530
3607
  }
3531
- const response = isRecord2(payload.response) ? payload.response : void 0;
3532
- const id = nonEmptyString(response, "request_id") ?? nonEmptyString(payload, "request_id") ?? nonEmptyString(payload, "uuid");
3533
- return id === void 0 ? [] : [id];
3608
+ if (credential.authorization === void 0) {
3609
+ return noOAuthCredentialResult(sessionId);
3610
+ }
3611
+ if (!deps.pendingOf(sessionId).some((pending) => pending.requestId === requestId)) {
3612
+ return { ok: false, message: `the front door has not observed control request ${requestId} pending on session ${sessionId}: it was answered already (by this door or by an attached client), it passed its answer deadline (the protocol's permission round-trip bound), or it never passed through this door` };
3613
+ }
3614
+ const body = JSON.stringify(buildRcEventWriteBody(buildRcControlResponsePayload(requestId, decision)));
3615
+ let answer;
3616
+ try {
3617
+ answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3618
+ } catch (error) {
3619
+ return { ok: false, message: `the door could not reach the API host to answer on session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
3620
+ }
3621
+ const result = rcEventWriteResultFromAnswer(answer);
3622
+ if (result.ok) {
3623
+ deps.completePending(sessionId, requestId);
3624
+ deps.noteSequenceNums(sessionId, result.sequenceNums);
3625
+ }
3626
+ return result;
3534
3627
  }
3535
- function parseRcRequestBody(kind, body) {
3536
- if (kind === "heartbeat") {
3537
- return { controlRequests: [], answeredRequestIds: [], idleSeconds: finiteNumber(parseJsonObject(body), "idle_seconds"), workerStatus: void 0 };
3628
+ async function interruptRcSession(deps, sessionId) {
3629
+ const credential = deps.credentialOf(sessionId);
3630
+ if (credential === void 0) {
3631
+ return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
3538
3632
  }
3539
- if (kind === "worker-registration") {
3540
- return { controlRequests: [], answeredRequestIds: [], idleSeconds: void 0, workerStatus: nonEmptyString(parseJsonObject(body), "status") };
3633
+ if (credential.authorization === void 0) {
3634
+ return noOAuthCredentialResult(sessionId);
3541
3635
  }
3542
- const controlRequests = [];
3543
- const answeredRequestIds = [];
3544
- for (const payload of payloadEventsOf(body)) {
3545
- const pending = pendingOfControlRequestPayload(payload);
3546
- if (pending !== void 0) {
3547
- controlRequests.push({ requestId: pending.requestId, type: pending.type, summary: pending.summary });
3548
- continue;
3549
- }
3550
- answeredRequestIds.push(...answeredRequestIdsOfControlResponsePayload(payload));
3636
+ const body = JSON.stringify(buildRcEventWriteBody(buildRcInterruptPayload(deps.newUuid())));
3637
+ let answer;
3638
+ try {
3639
+ answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3640
+ } catch (error) {
3641
+ return { ok: false, message: `the door could not reach the API host to interrupt session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
3551
3642
  }
3552
- return { controlRequests, answeredRequestIds, idleSeconds: void 0, workerStatus: void 0 };
3643
+ const result = rcEventWriteResultFromAnswer(answer);
3644
+ if (result.ok) {
3645
+ deps.noteSequenceNums(sessionId, result.sequenceNums);
3646
+ }
3647
+ return result;
3553
3648
  }
3554
- function createRcSessionTracker(deps) {
3555
- const entries = /* @__PURE__ */ new Map();
3556
- const expire = (id) => {
3557
- entries.delete(id);
3558
- };
3559
- const expirePendings = (entry, now) => {
3560
- for (const [requestId, pending] of entry.pending) {
3561
- if (now - pending.observedAt >= RC_PENDING_DEADLINE_MS) {
3562
- entry.pending.delete(requestId);
3649
+ async function setRcSessionModel(deps, sessionId, model) {
3650
+ if (model === "") {
3651
+ return { ok: false, message: "a set-model write needs a non-empty model id: the SDK's own field treats an omitted or null value as a reset to the session default, which this operation never sends, and an empty string is neither" };
3652
+ }
3653
+ const credential = deps.credentialOf(sessionId);
3654
+ if (credential === void 0) {
3655
+ return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
3656
+ }
3657
+ if (credential.authorization === void 0) {
3658
+ return noOAuthCredentialResult(sessionId);
3659
+ }
3660
+ const body = JSON.stringify(buildRcEventWriteBody(buildRcSetModelPayload(deps.newUuid(), model)));
3661
+ let answer;
3662
+ try {
3663
+ answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3664
+ } catch (error) {
3665
+ return { ok: false, message: `the door could not reach the API host to set the model on session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
3666
+ }
3667
+ const result = rcEventWriteResultFromAnswer(answer);
3668
+ if (result.ok) {
3669
+ deps.noteSequenceNums(sessionId, result.sequenceNums);
3670
+ }
3671
+ return result;
3672
+ }
3673
+ async function setRcSessionPermissionMode(deps, sessionId, mode) {
3674
+ const credential = deps.credentialOf(sessionId);
3675
+ if (credential === void 0) {
3676
+ return { ok: false, message: rcSessionNotObservedMessage(sessionId) };
3677
+ }
3678
+ if (credential.authorization === void 0) {
3679
+ return noOAuthCredentialResult(sessionId);
3680
+ }
3681
+ const body = JSON.stringify(buildRcEventWriteBody(buildRcSetPermissionModePayload(deps.newUuid(), mode)));
3682
+ let answer;
3683
+ try {
3684
+ answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3685
+ } catch (error) {
3686
+ return { ok: false, message: `the door could not reach the API host to set the permission mode on session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
3687
+ }
3688
+ const result = rcEventWriteResultFromAnswer(answer);
3689
+ if (result.ok) {
3690
+ deps.noteSequenceNums(sessionId, result.sequenceNums);
3691
+ }
3692
+ return result;
3693
+ }
3694
+
3695
+ // src/frontdoor/rcControl.ts
3696
+ var RC_PERMISSION_MODE_LIST = RC_PERMISSION_MODES.join(", ");
3697
+ var CONTROL_PATH_PREFIX = "/__agent-shim/rc";
3698
+ var CONTROL_BODY_CAP_BYTES = 10485760;
3699
+ function answerJson(response, status, body) {
3700
+ const text = JSON.stringify(body);
3701
+ response.writeHead(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")) });
3702
+ response.end(text);
3703
+ }
3704
+ async function readBody(request4) {
3705
+ return await new Promise((resolve, reject) => {
3706
+ const chunks = [];
3707
+ let total = 0;
3708
+ request4.on("data", (chunk) => {
3709
+ total += chunk.length;
3710
+ if (total > CONTROL_BODY_CAP_BYTES) {
3711
+ request4.destroy();
3712
+ reject(new Error("too large"));
3713
+ return;
3563
3714
  }
3715
+ chunks.push(chunk);
3716
+ });
3717
+ request4.on("end", () => {
3718
+ resolve(Buffer.concat(chunks).toString("utf8"));
3719
+ });
3720
+ request4.on("error", () => {
3721
+ reject(new Error("the request body ended early"));
3722
+ });
3723
+ });
3724
+ }
3725
+ function createRcControlHandler(deps) {
3726
+ const sessionsPath = `${CONTROL_PATH_PREFIX}/sessions`;
3727
+ const statusPath = `${CONTROL_PATH_PREFIX}/status`;
3728
+ const pendingPath = `${CONTROL_PATH_PREFIX}/pending`;
3729
+ const injectPath = `${CONTROL_PATH_PREFIX}/inject`;
3730
+ const answerPath = `${CONTROL_PATH_PREFIX}/answer`;
3731
+ const interruptPath = `${CONTROL_PATH_PREFIX}/interrupt`;
3732
+ const setModelPath = `${CONTROL_PATH_PREFIX}/set-model`;
3733
+ const setPermissionModePath = `${CONTROL_PATH_PREFIX}/set-permission-mode`;
3734
+ return (request4, response) => {
3735
+ const presented = request4.headers.authorization;
3736
+ const bearerPrefix = "bearer ".length;
3737
+ const token = typeof presented === "string" && presented.slice(0, bearerPrefix).toLowerCase() === "bearer " ? presented.slice(bearerPrefix) : void 0;
3738
+ if (token === void 0 || !isLiveCapability(token, [deps.expectedToken])) {
3739
+ answerJson(response, HTTP_STATUS.unauthorized, { error: "the front door's control routes demand this generation's control token as a Bearer credential" });
3740
+ return;
3564
3741
  }
3565
- };
3566
- const sweep = () => {
3567
- for (const [id, entry] of entries) {
3568
- if (deps.now() - entry.lastSeenAt >= deps.idleMs) {
3569
- expire(id);
3570
- continue;
3742
+ const url = new URL(request4.url ?? "/", "http://127.0.0.1");
3743
+ const handle = async () => {
3744
+ if (url.pathname === sessionsPath) {
3745
+ if (request4.method !== "GET") {
3746
+ answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the session list is a GET" });
3747
+ return;
3748
+ }
3749
+ answerJson(response, HTTP_STATUS.ok, { sessions: deps.list() });
3750
+ return;
3571
3751
  }
3572
- expirePendings(entry, deps.now());
3573
- }
3574
- };
3575
- const timer = setInterval(sweep, deps.idleMs);
3576
- timer.unref();
3577
- const birth = (id, facts) => {
3578
- const record = { id, createdAt: deps.now(), lastSeenAt: deps.now(), ...facts, pending: /* @__PURE__ */ new Map(), workerState: void 0, workerIdleSeconds: void 0 };
3579
- entries.set(id, record);
3580
- return record;
3581
- };
3582
- const observeExchange = (request4) => {
3583
- const method = (request4.method ?? "").toUpperCase();
3584
- const path29 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
3585
- if (!path29.startsWith(RC_SESSIONS_PATH_PREFIX)) {
3586
- return void 0;
3587
- }
3588
- const tail = path29.slice(RC_SESSIONS_PATH_PREFIX.length);
3589
- const facts = {
3590
- oauthAuthorization: oauthAuthorizationOf(request4.headers),
3591
- anthropicVersion: singleHeader(request4.headers, "anthropic-version"),
3592
- anthropicClientPlatform: singleHeader(request4.headers, "anthropic-client-platform")
3593
- };
3594
- if (tail === "") {
3595
- if (method !== "POST") {
3596
- return void 0;
3597
- }
3598
- let accepted2 = false;
3599
- let buffered2 = 0;
3600
- let body = "";
3601
- return {
3602
- onResponse: (status) => {
3603
- accepted2 = isSuccessful(status);
3604
- },
3605
- onBodyChunk: (chunk) => {
3606
- if (!accepted2 || buffered2 >= RC_CREATE_RESPONSE_CAP_BYTES) {
3607
- return;
3608
- }
3609
- const text = Buffer.from(chunk).toString("utf8");
3610
- body = body + text.slice(0, RC_CREATE_RESPONSE_CAP_BYTES - buffered2);
3611
- buffered2 = body.length;
3612
- },
3613
- onEnd: () => {
3614
- if (!accepted2) {
3615
- return;
3616
- }
3617
- const id2 = sessionCreateId(body);
3618
- if (id2 === void 0) {
3619
- return;
3620
- }
3621
- birth(id2, facts);
3752
+ if (url.pathname === statusPath || url.pathname === pendingPath) {
3753
+ if (request4.method !== "GET") {
3754
+ answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the status and pending reads are GETs" });
3755
+ return;
3622
3756
  }
3623
- };
3624
- }
3625
- const rest = tail.slice(1);
3626
- const [id] = rest.split("/");
3627
- if (id?.startsWith(RC_SESSION_ID_PREFIX) !== true) {
3628
- return void 0;
3629
- }
3630
- const entry = entries.get(id) ?? birth(id, facts);
3631
- entry.lastSeenAt = deps.now();
3632
- entry.oauthAuthorization = facts.oauthAuthorization ?? entry.oauthAuthorization;
3633
- entry.anthropicVersion = facts.anthropicVersion ?? entry.anthropicVersion;
3634
- entry.anthropicClientPlatform = facts.anthropicClientPlatform ?? entry.anthropicClientPlatform;
3635
- const bodyKind = method === "POST" && rest === `${id}/worker/events` ? "worker-events" : method === "POST" && rest === `${id}/events` ? "client-events" : method === "POST" && rest === `${id}/worker/heartbeat` ? "heartbeat" : method === "PUT" && rest === `${id}/worker` ? "worker-registration" : void 0;
3636
- let chunks = [];
3637
- let buffered = 0;
3638
- let oversized = false;
3639
- let requestEnded = false;
3640
- let accepted;
3641
- let filed = false;
3642
- const file = () => {
3643
- if (filed || accepted !== true || !requestEnded || bodyKind === void 0) {
3757
+ const session = url.searchParams.get("session") ?? void 0;
3758
+ if (session !== void 0 && !deps.statusOf(session).some((entry) => entry.id === session)) {
3759
+ answerJson(response, HTTP_STATUS.notFound, { error: rcSessionNotObservedMessage(session) });
3760
+ return;
3761
+ }
3762
+ answerJson(response, HTTP_STATUS.ok, url.pathname === statusPath ? { statuses: deps.statusOf(session) } : { pending: deps.pendingOf(session) });
3644
3763
  return;
3645
3764
  }
3646
- filed = true;
3647
- const factsOfBody = oversized ? void 0 : parseRcRequestBody(bodyKind, Buffer.concat(chunks).toString("utf8"));
3648
- chunks = [];
3649
- const entryNow = entries.get(id);
3650
- if (entryNow === void 0 || factsOfBody === void 0) {
3765
+ if (url.pathname === injectPath) {
3766
+ if (request4.method !== "POST") {
3767
+ answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the inject operation is a POST" });
3768
+ return;
3769
+ }
3770
+ let parsed;
3771
+ try {
3772
+ parsed = JSON.parse(await readBody(request4));
3773
+ } catch {
3774
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the inject body is not readable JSON within the control body cap" });
3775
+ return;
3776
+ }
3777
+ if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("text" in parsed) || typeof parsed.session !== "string" || typeof parsed.text !== "string" || parsed.session === "" || parsed.text === "") {
3778
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the inject body must be JSON naming a non-empty session id and text" });
3779
+ return;
3780
+ }
3781
+ const result = await deps.inject(parsed.session, parsed.text);
3782
+ if (result.ok) {
3783
+ answerJson(response, HTTP_STATUS.ok, { session: parsed.session, sequenceNums: result.sequenceNums });
3784
+ return;
3785
+ }
3786
+ answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
3651
3787
  return;
3652
3788
  }
3653
- const now = deps.now();
3654
- for (const controlRequest of factsOfBody.controlRequests) {
3655
- entryNow.pending.set(controlRequest.requestId, { sessionId: id, ...controlRequest, observedAt: now });
3656
- }
3657
- for (const requestId of factsOfBody.answeredRequestIds) {
3658
- entryNow.pending.delete(requestId);
3789
+ if (url.pathname === answerPath) {
3790
+ if (request4.method !== "POST") {
3791
+ answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the answer operation is a POST" });
3792
+ return;
3793
+ }
3794
+ let parsed;
3795
+ try {
3796
+ parsed = JSON.parse(await readBody(request4));
3797
+ } catch {
3798
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the answer body is not readable JSON within the control body cap" });
3799
+ return;
3800
+ }
3801
+ if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("request" in parsed) || !("approve" in parsed) || typeof parsed.session !== "string" || typeof parsed.request !== "string" || typeof parsed.approve !== "boolean" || parsed.session === "" || parsed.request === "") {
3802
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the answer body must be JSON naming a non-empty session id, request id, and an approve boolean" });
3803
+ return;
3804
+ }
3805
+ const text = "text" in parsed && typeof parsed.text === "string" ? parsed.text : void 0;
3806
+ if (parsed.approve && text !== void 0 && text !== "") {
3807
+ answerJson(response, HTTP_STATUS.badRequest, { error: "an approval carries no text (the protocol's allow result has no message field); text is the denial message" });
3808
+ return;
3809
+ }
3810
+ const result = await deps.answer(parsed.session, parsed.request, { approve: parsed.approve, message: parsed.approve ? void 0 : text });
3811
+ if (result.ok) {
3812
+ answerJson(response, HTTP_STATUS.ok, { session: parsed.session, request: parsed.request, sequenceNums: result.sequenceNums });
3813
+ return;
3814
+ }
3815
+ answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
3816
+ return;
3659
3817
  }
3660
- if (factsOfBody.idleSeconds !== void 0) {
3661
- entryNow.workerIdleSeconds = { value: factsOfBody.idleSeconds, observedAt: now };
3818
+ if (url.pathname === interruptPath) {
3819
+ if (request4.method !== "POST") {
3820
+ answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the interrupt operation is a POST" });
3821
+ return;
3822
+ }
3823
+ let parsed;
3824
+ try {
3825
+ parsed = JSON.parse(await readBody(request4));
3826
+ } catch {
3827
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body is not readable JSON within the control body cap" });
3828
+ return;
3829
+ }
3830
+ if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || typeof parsed.session !== "string" || parsed.session === "") {
3831
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body must be JSON naming a non-empty session id" });
3832
+ return;
3833
+ }
3834
+ const result = await deps.interrupt(parsed.session);
3835
+ if (result.ok) {
3836
+ answerJson(response, HTTP_STATUS.ok, { session: parsed.session, sequenceNums: result.sequenceNums });
3837
+ return;
3838
+ }
3839
+ answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
3840
+ return;
3662
3841
  }
3663
- if (factsOfBody.workerStatus !== void 0) {
3664
- entryNow.workerState = { value: factsOfBody.workerStatus, observedAt: now };
3842
+ if (url.pathname === setModelPath) {
3843
+ if (request4.method !== "POST") {
3844
+ answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-model operation is a POST" });
3845
+ return;
3846
+ }
3847
+ let parsed;
3848
+ try {
3849
+ parsed = JSON.parse(await readBody(request4));
3850
+ } catch {
3851
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body is not readable JSON within the control body cap" });
3852
+ return;
3853
+ }
3854
+ if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("model" in parsed) || typeof parsed.session !== "string" || typeof parsed.model !== "string" || parsed.session === "" || parsed.model === "") {
3855
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body must be JSON naming a non-empty session id and model id" });
3856
+ return;
3857
+ }
3858
+ const result = await deps.setModel(parsed.session, parsed.model);
3859
+ if (result.ok) {
3860
+ answerJson(response, HTTP_STATUS.ok, { session: parsed.session, model: parsed.model, sequenceNums: result.sequenceNums });
3861
+ return;
3862
+ }
3863
+ answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
3864
+ return;
3665
3865
  }
3666
- };
3667
- const archiving = method === "POST" && rest === `${id}/archive`;
3668
- return {
3669
- ...bodyKind === void 0 ? {} : {
3670
- onRequestChunk: (chunk) => {
3671
- if (oversized) {
3672
- return;
3673
- }
3674
- chunks.push(Buffer.from(chunk));
3675
- buffered += chunk.length;
3676
- if (buffered > RC_REQUEST_PARSE_CAP_BYTES) {
3677
- oversized = true;
3678
- chunks = [];
3679
- }
3680
- },
3681
- onRequestEnd: () => {
3682
- requestEnded = true;
3683
- file();
3866
+ if (url.pathname === setPermissionModePath) {
3867
+ if (request4.method !== "POST") {
3868
+ answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-permission-mode operation is a POST" });
3869
+ return;
3684
3870
  }
3685
- },
3686
- onResponse: (status, headers) => {
3687
- if (singleHeader(headers, CONFLICT_REASON_HEADER) !== void 0 || archiving && isSuccessful(status)) {
3688
- expire(id);
3871
+ let parsed;
3872
+ try {
3873
+ parsed = JSON.parse(await readBody(request4));
3874
+ } catch {
3875
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the set-permission-mode body is not readable JSON within the control body cap" });
3689
3876
  return;
3690
3877
  }
3691
- accepted = isSuccessful(status);
3692
- file();
3693
- },
3694
- onBodyChunk: () => {
3695
- },
3696
- onEnd: () => {
3878
+ if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("mode" in parsed) || typeof parsed.session !== "string" || parsed.session === "" || !isRcPermissionMode(parsed.mode)) {
3879
+ answerJson(response, HTTP_STATUS.badRequest, { error: `the set-permission-mode body must be JSON naming a non-empty session id and a mode the SDK's own type permits (${RC_PERMISSION_MODE_LIST})` });
3880
+ return;
3881
+ }
3882
+ const result = await deps.setPermissionMode(parsed.session, parsed.mode);
3883
+ if (result.ok) {
3884
+ answerJson(response, HTTP_STATUS.ok, { session: parsed.session, mode: parsed.mode, sequenceNums: result.sequenceNums });
3885
+ return;
3886
+ }
3887
+ answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
3888
+ return;
3697
3889
  }
3890
+ answerJson(response, HTTP_STATUS.notFound, { error: `no such control route: ${url.pathname}` });
3698
3891
  };
3892
+ handle().catch((error) => {
3893
+ answerJson(response, HTTP_STATUS.internalServerError, { error: `the control route failed: ${error instanceof Error ? error.message : String(error)}` });
3894
+ });
3699
3895
  };
3700
- const summaryOf = ({ id, createdAt, lastSeenAt }) => ({ id, createdAt, lastSeenAt });
3701
- const pendingSummaryOf = ({ sessionId, requestId, type, summary, observedAt }) => ({ sessionId, requestId, type, summary, observedAt });
3702
- const sortedEntries = () => [...entries.values()].sort((left, right) => left.createdAt - right.createdAt || (left.id < right.id ? -1 : 1));
3703
- const pendingSorted = (entry) => [...entry.pending.values()].sort((left, right) => left.observedAt - right.observedAt || (left.requestId < right.requestId ? -1 : 1)).map(pendingSummaryOf);
3896
+ }
3897
+ function realRcControlTransport(port, ca) {
3704
3898
  return {
3705
- observeExchange,
3706
- list: () => {
3707
- sweep();
3708
- return sortedEntries().map(summaryOf);
3709
- },
3710
- statusOf: (sessionId) => {
3711
- sweep();
3712
- return sortedEntries().filter((entry) => sessionId === void 0 || entry.id === sessionId).map((entry) => ({ ...summaryOf(entry), workerState: entry.workerState, workerIdleSeconds: entry.workerIdleSeconds, pending: pendingSorted(entry) }));
3713
- },
3714
- pendingOf: (sessionId) => {
3715
- sweep();
3716
- return sortedEntries().filter((entry) => sessionId === void 0 || entry.id === sessionId).flatMap((entry) => pendingSorted(entry));
3717
- },
3718
- credentialOf: (sessionId) => {
3719
- const entry = entries.get(sessionId);
3720
- return entry === void 0 ? void 0 : { authorization: entry.oauthAuthorization, anthropicVersion: entry.anthropicVersion, anthropicClientPlatform: entry.anthropicClientPlatform };
3721
- },
3722
- completePending: (sessionId, requestId) => {
3723
- entries.get(sessionId)?.pending.delete(requestId);
3724
- },
3725
- close: () => {
3726
- clearInterval(timer);
3727
- }
3899
+ request: async (options) => await new Promise((resolve, reject) => {
3900
+ const request4 = https.request(
3901
+ {
3902
+ host: "127.0.0.1",
3903
+ port,
3904
+ method: options.method,
3905
+ path: options.path,
3906
+ headers: options.body === void 0 ? options.headers : { ...options.headers, "content-length": String(Buffer.byteLength(options.body, "utf8")) },
3907
+ agent: false,
3908
+ ca: [ca]
3909
+ },
3910
+ (response) => {
3911
+ const chunks = [];
3912
+ response.on("data", (chunk) => {
3913
+ chunks.push(chunk);
3914
+ });
3915
+ response.on("end", () => {
3916
+ resolve({ status: response.statusCode ?? 0, body: Buffer.concat(chunks).toString("utf8") });
3917
+ });
3918
+ }
3919
+ );
3920
+ request4.once("error", reject);
3921
+ if (options.body === void 0) {
3922
+ request4.end();
3923
+ } else {
3924
+ request4.end(options.body);
3925
+ }
3926
+ })
3728
3927
  };
3729
3928
  }
3730
- function observingRoutedRoute(route, tracker) {
3731
- return {
3732
- ...route,
3733
- serve: async (request4, response) => {
3734
- const observed = tracker.observeExchange({ method: request4.method, url: request4.url, headers: request4.headers });
3735
- if (observed?.onRequestChunk !== void 0) {
3736
- request4.body.on("data", (chunk) => {
3737
- observed.onRequestChunk?.(chunk);
3738
- });
3739
- request4.body.on("end", () => {
3740
- observed.onRequestEnd?.();
3741
- });
3929
+ function controlErrorOf(body) {
3930
+ try {
3931
+ const parsed = JSON.parse(body);
3932
+ if (typeof parsed === "object" && parsed !== null && "error" in parsed && typeof parsed.error === "string") {
3933
+ return parsed.error;
3934
+ }
3935
+ } catch {
3936
+ return body;
3937
+ }
3938
+ return void 0;
3939
+ }
3940
+ function isRcSessionSummary(value) {
3941
+ if (typeof value !== "object" || value === null) {
3942
+ return false;
3943
+ }
3944
+ if (!("id" in value) || !("createdAt" in value) || !("lastSeenAt" in value)) {
3945
+ return false;
3946
+ }
3947
+ return typeof value.id === "string" && typeof value.createdAt === "number" && typeof value.lastSeenAt === "number";
3948
+ }
3949
+ function isRcWorkerFact(value, valueIs) {
3950
+ if (typeof value !== "object" || value === null) {
3951
+ return false;
3952
+ }
3953
+ if (!("value" in value) || !("observedAt" in value)) {
3954
+ return false;
3955
+ }
3956
+ return valueIs(value.value) && typeof value.observedAt === "number";
3957
+ }
3958
+ function isRcSessionStatus(value) {
3959
+ if (!isRcSessionSummary(value)) {
3960
+ return false;
3961
+ }
3962
+ if (!("pending" in value) || !Array.isArray(value.pending)) {
3963
+ return false;
3964
+ }
3965
+ if ("workerState" in value && value.workerState !== void 0 && !isRcWorkerFact(value.workerState, (candidate) => typeof candidate === "string")) {
3966
+ return false;
3967
+ }
3968
+ if ("workerIdleSeconds" in value && value.workerIdleSeconds !== void 0 && !isRcWorkerFact(value.workerIdleSeconds, (candidate) => typeof candidate === "number")) {
3969
+ return false;
3970
+ }
3971
+ return value.pending.every((candidate) => isRcPendingRequestSummary(candidate));
3972
+ }
3973
+ function isRcPendingRequestSummary(value) {
3974
+ if (typeof value !== "object" || value === null) {
3975
+ return false;
3976
+ }
3977
+ if (!("sessionId" in value) || !("requestId" in value) || !("type" in value) || !("summary" in value) || !("observedAt" in value)) {
3978
+ return false;
3979
+ }
3980
+ return typeof value.sessionId === "string" && typeof value.requestId === "string" && typeof value.type === "string" && typeof value.summary === "string" && typeof value.observedAt === "number";
3981
+ }
3982
+ function sequenceNumsOfBody(body) {
3983
+ let parsed;
3984
+ try {
3985
+ parsed = JSON.parse(body);
3986
+ } catch {
3987
+ return void 0;
3988
+ }
3989
+ if (typeof parsed !== "object" || parsed === null || !("sequenceNums" in parsed) || !Array.isArray(parsed.sequenceNums)) {
3990
+ return void 0;
3991
+ }
3992
+ const sequenceNums = [];
3993
+ const values = parsed.sequenceNums;
3994
+ for (const value of values) {
3995
+ if (typeof value !== "number") {
3996
+ return void 0;
3997
+ }
3998
+ sequenceNums.push(value);
3999
+ }
4000
+ return sequenceNums;
4001
+ }
4002
+ function frontDoorRcControl(transport, token) {
4003
+ const headers = { authorization: `Bearer ${token}` };
4004
+ const listAnswerOf = async (path29, what) => {
4005
+ try {
4006
+ return await transport.request({ method: "GET", path: path29, headers });
4007
+ } catch (error) {
4008
+ throw new Error(`${what}: could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
4009
+ }
4010
+ };
4011
+ const sessionsOf = (answer, what) => {
4012
+ if (answer.status !== HTTP_STATUS.ok) {
4013
+ throw new Error(`${what} failed (HTTP ${String(answer.status)}): ${controlErrorOf(answer.body) ?? answer.body}`);
4014
+ }
4015
+ let parsed;
4016
+ try {
4017
+ parsed = JSON.parse(answer.body);
4018
+ } catch {
4019
+ throw new Error(`${what}: the door's answer was not JSON: ${answer.body}`);
4020
+ }
4021
+ if (typeof parsed !== "object" || parsed === null || !("sessions" in parsed) || !Array.isArray(parsed.sessions)) {
4022
+ throw new Error(`${what}: the door's answer named no sessions list: ${answer.body}`);
4023
+ }
4024
+ const candidates = parsed.sessions;
4025
+ const sessions = [];
4026
+ for (const value of candidates) {
4027
+ if (!isRcSessionSummary(value)) {
4028
+ throw new Error(`${what}: the door's answer named no sessions list: ${answer.body}`);
3742
4029
  }
3743
- await route.serve(request4, observed === void 0 ? response : teeRoutedResponse(response, observed));
4030
+ sessions.push(value);
3744
4031
  }
4032
+ return sessions;
3745
4033
  };
3746
- }
3747
- function teeRoutedResponse(response, observer) {
3748
- return {
3749
- get headersSent() {
3750
- return response.headersSent;
3751
- },
3752
- start: (status, headers) => {
3753
- observer.onResponse(status, headers);
3754
- response.start(status, headers);
3755
- },
3756
- flush: () => {
3757
- response.flush();
3758
- },
3759
- write: async (chunk) => {
3760
- observer.onBodyChunk(typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk);
3761
- await response.write(chunk);
3762
- },
3763
- end: () => {
3764
- response.end();
3765
- observer.onEnd();
3766
- },
3767
- destroy: () => {
3768
- response.destroy();
3769
- observer.onEnd();
4034
+ const statusesOf = (answer, what) => {
4035
+ if (answer.status !== HTTP_STATUS.ok) {
4036
+ throw new Error(`${what} failed (HTTP ${String(answer.status)}): ${controlErrorOf(answer.body) ?? answer.body}`);
4037
+ }
4038
+ let parsed;
4039
+ try {
4040
+ parsed = JSON.parse(answer.body);
4041
+ } catch {
4042
+ throw new Error(`${what}: the door's answer was not JSON: ${answer.body}`);
4043
+ }
4044
+ if (typeof parsed !== "object" || parsed === null || !("statuses" in parsed) || !Array.isArray(parsed.statuses)) {
4045
+ throw new Error(`${what}: the door's answer named no status list: ${answer.body}`);
4046
+ }
4047
+ const statuses = [];
4048
+ for (const value of parsed.statuses) {
4049
+ if (!isRcSessionStatus(value)) {
4050
+ throw new Error(`${what}: the door's answer named no status list: ${answer.body}`);
4051
+ }
4052
+ statuses.push(value);
3770
4053
  }
4054
+ return statuses;
3771
4055
  };
3772
- }
3773
- function rcObserverAsPassthrough(observer) {
3774
- return {
3775
- onRequestChunk: (chunk) => {
3776
- observer.onRequestChunk?.(chunk);
3777
- },
3778
- ...observer.onRequestEnd === void 0 ? {} : {
3779
- onRequestEnd: () => {
3780
- observer.onRequestEnd?.();
4056
+ const pendingOfAnswer = (answer, what) => {
4057
+ if (answer.status !== HTTP_STATUS.ok) {
4058
+ throw new Error(`${what} failed (HTTP ${String(answer.status)}): ${controlErrorOf(answer.body) ?? answer.body}`);
4059
+ }
4060
+ let parsed;
4061
+ try {
4062
+ parsed = JSON.parse(answer.body);
4063
+ } catch {
4064
+ throw new Error(`${what}: the door's answer was not JSON: ${answer.body}`);
4065
+ }
4066
+ if (typeof parsed !== "object" || parsed === null || !("pending" in parsed) || !Array.isArray(parsed.pending)) {
4067
+ throw new Error(`${what}: the door's answer named no pending list: ${answer.body}`);
4068
+ }
4069
+ const pending = [];
4070
+ for (const value of parsed.pending) {
4071
+ if (!isRcPendingRequestSummary(value)) {
4072
+ throw new Error(`${what}: the door's answer named no pending list: ${answer.body}`);
3781
4073
  }
3782
- },
3783
- onResponse: (status, headers) => {
3784
- observer.onResponse(status, headers);
3785
- },
3786
- onResponseChunk: (chunk) => {
3787
- observer.onBodyChunk(chunk);
3788
- },
3789
- onEnd: () => {
3790
- observer.onEnd();
4074
+ pending.push(value);
3791
4075
  }
4076
+ return pending;
3792
4077
  };
3793
- }
3794
- function buildRcUserMessagePayload(uuid, sessionId, text) {
4078
+ const writeOf = async (path29, body, describeFailure) => {
4079
+ let answer;
4080
+ try {
4081
+ answer = await transport.request({ method: "POST", path: path29, headers: { ...headers, "content-type": "application/json" }, body });
4082
+ } catch (error) {
4083
+ return { ok: false, message: `could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}` };
4084
+ }
4085
+ if (answer.status === HTTP_STATUS.ok) {
4086
+ const sequenceNums = sequenceNumsOfBody(answer.body);
4087
+ if (sequenceNums === void 0) {
4088
+ return { ok: false, message: `the door accepted the event but its answer named no sequence numbers: ${answer.body}` };
4089
+ }
4090
+ return { ok: true, sequenceNums };
4091
+ }
4092
+ return { ok: false, message: describeFailure(answer.status, controlErrorOf(answer.body) ?? answer.body) };
4093
+ };
4094
+ const sessionQuery = (sessionId) => sessionId === void 0 ? "" : `?session=${encodeURIComponent(sessionId)}`;
3795
4095
  return {
3796
- type: "user",
3797
- uuid,
3798
- session_id: sessionId,
3799
- parent_tool_use_id: null,
3800
- message: { role: "user", content: text }
4096
+ listSessions: async () => sessionsOf(await listAnswerOf(`${CONTROL_PATH_PREFIX}/sessions`, "listing the door's Remote Control sessions"), "listing the door's Remote Control sessions"),
4097
+ statusOf: async (sessionId) => statusesOf(await listAnswerOf(`${CONTROL_PATH_PREFIX}/status${sessionQuery(sessionId)}`, "reading the door's Remote Control session status"), "reading the door's Remote Control session status"),
4098
+ pendingOf: async (sessionId) => pendingOfAnswer(await listAnswerOf(`${CONTROL_PATH_PREFIX}/pending${sessionQuery(sessionId)}`, "listing the door's pending Remote Control control requests"), "listing the door's pending Remote Control control requests"),
4099
+ sendPrompt: async (sessionId, text) => await writeOf(`${CONTROL_PATH_PREFIX}/inject`, JSON.stringify({ session: sessionId, text }), (status, message) => `injecting into ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4100
+ answerRequest: async (sessionId, requestId, decision) => await writeOf(
4101
+ `${CONTROL_PATH_PREFIX}/answer`,
4102
+ JSON.stringify({ session: sessionId, request: requestId, approve: decision.approve, ...decision.approve || decision.message === void 0 ? {} : { text: decision.message } }),
4103
+ (status, message) => `answering control request ${requestId} on ${sessionId} failed (HTTP ${String(status)}): ${message}`
4104
+ ),
4105
+ interruptSession: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/interrupt`, JSON.stringify({ session: sessionId }), (status, message) => `interrupting ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4106
+ setModel: async (sessionId, model) => await writeOf(`${CONTROL_PATH_PREFIX}/set-model`, JSON.stringify({ session: sessionId, model }), (status, message) => `setting the model on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4107
+ setPermissionMode: async (sessionId, mode) => await writeOf(`${CONTROL_PATH_PREFIX}/set-permission-mode`, JSON.stringify({ session: sessionId, mode }), (status, message) => `setting the permission mode on ${sessionId} failed (HTTP ${String(status)}): ${message}`)
3801
4108
  };
3802
4109
  }
3803
- function buildRcEventWriteBody(payload) {
3804
- return { events: [{ payload }] };
3805
- }
3806
- function sequenceNumsOf(body) {
3807
- let parsed;
3808
- try {
3809
- parsed = JSON.parse(body);
3810
- } catch {
3811
- return void 0;
3812
- }
3813
- if (typeof parsed !== "object" || parsed === null || !("results" in parsed) || !Array.isArray(parsed.results)) {
3814
- return void 0;
3815
- }
3816
- const sequenceNums = [];
3817
- const results = parsed.results;
3818
- for (const result of results) {
3819
- if (typeof result !== "object" || result === null || !("sequence_num" in result)) {
3820
- return void 0;
3821
- }
3822
- const sequenceNum = result.sequence_num;
3823
- if (typeof sequenceNum === "number") {
3824
- sequenceNums.push(sequenceNum);
3825
- } else if (typeof sequenceNum === "string" && /^[0-9]+$/.test(sequenceNum)) {
3826
- sequenceNums.push(Number(sequenceNum));
3827
- } else {
3828
- return void 0;
4110
+
4111
+ // src/frontdoor/server.ts
4112
+ async function* upstreamChunks(stream) {
4113
+ for await (const chunk of stream) {
4114
+ if (typeof chunk === "string") {
4115
+ yield Buffer.from(chunk, "utf8");
4116
+ continue;
3829
4117
  }
3830
- }
3831
- return sequenceNums.length === 0 ? void 0 : sequenceNums;
3832
- }
3833
- function rcEventWriteResultFromAnswer(answer) {
3834
- if (!isSuccessful(answer.status)) {
3835
- if (answer.status === HTTP_STATUS.unauthorized) {
3836
- return { ok: false, message: `the API refused the observed Authorization bearer for this session (HTTP 401): the bearer went stale after the door last saw the session's traffic. The session's own client refreshes it on its next call, which the door will observe, so retry once Remote Control has been active again. API answer: ${answer.body}` };
4118
+ if (chunk instanceof Uint8Array) {
4119
+ yield chunk;
3837
4120
  }
3838
- return { ok: false, message: `the API answered HTTP ${String(answer.status)} to the injected event: ${answer.body}` };
3839
- }
3840
- const sequenceNums = sequenceNumsOf(answer.body);
3841
- if (sequenceNums === void 0) {
3842
- return { ok: false, message: `the API accepted the injected event but its answer named no results[].sequence_num, so delivery cannot be confirmed: ${answer.body}` };
3843
- }
3844
- return { ok: true, sequenceNums };
3845
- }
3846
- function noOAuthCredentialResult(sessionId) {
3847
- return { ok: false, message: `no claude.ai OAuth bearer has been observed for session ${sessionId}: only worker calls crossed this door, and the worker JWT they carry is answered 401 on the client half. Reconnect Remote Control through this door and the create's own bearer will be observed` };
3848
- }
3849
- function rcWriteHeaders(credential) {
3850
- const headers = { "content-type": "application/json" };
3851
- if (credential.authorization !== void 0) {
3852
- headers.authorization = credential.authorization;
3853
- }
3854
- if (credential.anthropicVersion !== void 0) {
3855
- headers["anthropic-version"] = credential.anthropicVersion;
3856
- }
3857
- if (credential.anthropicClientPlatform !== void 0) {
3858
- headers["anthropic-client-platform"] = credential.anthropicClientPlatform;
3859
- }
3860
- return headers;
3861
- }
3862
- async function injectRcUserMessage(deps, sessionId, text) {
3863
- const credential = deps.credentialOf(sessionId);
3864
- if (credential === void 0) {
3865
- return { ok: false, message: `the front door has not observed Remote Control session ${sessionId}: it may never have passed through this door, or it ended or expired (an entry lives only a bounded idle period past its last observed traffic)` };
3866
- }
3867
- if (credential.authorization === void 0) {
3868
- return noOAuthCredentialResult(sessionId);
3869
- }
3870
- const body = JSON.stringify(buildRcEventWriteBody(buildRcUserMessagePayload(deps.newUuid(), sessionId, text)));
3871
- let answer;
3872
- try {
3873
- answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3874
- } catch (error) {
3875
- return { ok: false, message: `the door could not reach the API host to inject into session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
3876
4121
  }
3877
- return rcEventWriteResultFromAnswer(answer);
3878
- }
3879
- function buildRcControlResponsePayload(requestId, decision) {
3880
- const result = decision.approve ? { behavior: "allow" } : { behavior: "deny", message: decision.message ?? "" };
3881
- return { type: "control_response", response: { subtype: "success", request_id: requestId, response: result } };
3882
- }
3883
- var RC_PERMISSION_MODES = ["default", "acceptEdits", "bypassPermissions", "plan", "dontAsk", "auto"];
3884
- function isRcPermissionMode(value) {
3885
- return RC_PERMISSION_MODES.some((mode) => mode === value);
3886
- }
3887
- function buildRcInterruptPayload(requestId) {
3888
- return { type: "control_request", request_id: requestId, request: { subtype: "interrupt" } };
3889
4122
  }
3890
- function buildRcSetModelPayload(requestId, model) {
3891
- return { type: "control_request", request_id: requestId, request: { subtype: "set_model", model } };
4123
+
4124
+ // src/frontdoor/connectEffects.ts
4125
+ var SUCCESS_STATUS_MAX_EXCLUSIVE2 = 300;
4126
+ function isSuccessful2(status) {
4127
+ return status >= HTTP_STATUS.ok && status < SUCCESS_STATUS_MAX_EXCLUSIVE2;
3892
4128
  }
3893
- function buildRcSetPermissionModePayload(requestId, mode) {
3894
- return { type: "control_request", request_id: requestId, request: { subtype: "set_permission_mode", mode } };
4129
+ var UPSTREAM_LOCAL_PORT_START = 47900;
4130
+ var UPSTREAM_LOCAL_PORT_END = 47919;
4131
+ var UPSTREAM_LOCAL_PORT_COUNT = UPSTREAM_LOCAL_PORT_END - UPSTREAM_LOCAL_PORT_START + 1;
4132
+ var nextUpstreamLocalPort = UPSTREAM_LOCAL_PORT_START;
4133
+ function upstreamLocalPort() {
4134
+ const port = nextUpstreamLocalPort;
4135
+ nextUpstreamLocalPort = nextUpstreamLocalPort === UPSTREAM_LOCAL_PORT_END ? UPSTREAM_LOCAL_PORT_START : nextUpstreamLocalPort + 1;
4136
+ return port;
3895
4137
  }
3896
- async function answerRcControlRequest(deps, sessionId, requestId, decision) {
3897
- const credential = deps.credentialOf(sessionId);
3898
- if (credential === void 0) {
3899
- return { ok: false, message: `the front door has not observed Remote Control session ${sessionId}: it may never have passed through this door, or it ended or expired (an entry lives only a bounded idle period past its last observed traffic)` };
3900
- }
3901
- if (credential.authorization === void 0) {
3902
- return noOAuthCredentialResult(sessionId);
3903
- }
3904
- if (!deps.pendingOf(sessionId).some((pending) => pending.requestId === requestId)) {
3905
- return { ok: false, message: `the front door has not observed control request ${requestId} pending on session ${sessionId}: it was answered already (by this door or by an attached client), it passed its answer deadline (the protocol's permission round-trip bound), or it never passed through this door` };
3906
- }
3907
- const body = JSON.stringify(buildRcEventWriteBody(buildRcControlResponsePayload(requestId, decision)));
3908
- let answer;
3909
- try {
3910
- answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3911
- } catch (error) {
3912
- return { ok: false, message: `the door could not reach the API host to answer on session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
3913
- }
3914
- const result = rcEventWriteResultFromAnswer(answer);
3915
- if (result.ok) {
3916
- deps.completePending(sessionId, requestId);
4138
+ var realAddressLookup = (host, _options, callback) => {
4139
+ if (net2.isIP(host) !== 0) {
4140
+ callback(null, [{ address: host, family: net2.isIP(host) }]);
4141
+ return;
3917
4142
  }
3918
- return result;
4143
+ dns.resolve4(host, (error, addresses) => {
4144
+ if (error !== null) {
4145
+ callback(error, [{ address: "", family: 4 }]);
4146
+ return;
4147
+ }
4148
+ const first = addresses[0];
4149
+ if (first === void 0) {
4150
+ callback(new Error(`no A record for ${host}`), [{ address: "", family: 4 }]);
4151
+ return;
4152
+ }
4153
+ callback(null, [{ address: first, family: 4 }]);
4154
+ });
4155
+ };
4156
+ var HTTP_PORT = 80;
4157
+ var HELD_PORT_ERRNOS = /* @__PURE__ */ new Set(["EADDRINUSE", "EADDRNOTAVAIL"]);
4158
+ async function dialOnce(connectOptions) {
4159
+ return await new Promise((resolve) => {
4160
+ const socket = net2.connect(connectOptions);
4161
+ socket.once("connect", () => {
4162
+ resolve({ error: null, socket });
4163
+ });
4164
+ socket.once("error", (error) => {
4165
+ resolve({ error, socket });
4166
+ });
4167
+ });
3919
4168
  }
3920
- async function interruptRcSession(deps, sessionId) {
3921
- const credential = deps.credentialOf(sessionId);
3922
- if (credential === void 0) {
3923
- return { ok: false, message: `the front door has not observed Remote Control session ${sessionId}: it may never have passed through this door, or it ended or expired (an entry lives only a bounded idle period past its last observed traffic)` };
3924
- }
3925
- if (credential.authorization === void 0) {
3926
- return noOAuthCredentialResult(sessionId);
3927
- }
3928
- const body = JSON.stringify(buildRcEventWriteBody(buildRcInterruptPayload(deps.newUuid())));
3929
- let answer;
3930
- try {
3931
- answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3932
- } catch (error) {
3933
- return { ok: false, message: `the door could not reach the API host to interrupt session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
3934
- }
3935
- return rcEventWriteResultFromAnswer(answer);
4169
+ function dialTarget(options, defaultPort) {
4170
+ return { host: typeof options.host === "string" ? options.host : "", port: typeof options.port === "number" ? options.port : defaultPort };
3936
4171
  }
3937
- async function setRcSessionModel(deps, sessionId, model) {
3938
- if (model === "") {
3939
- return { ok: false, message: "a set-model write needs a non-empty model id: the SDK's own field treats an omitted or null value as a reset to the session default, which this operation never sends, and an empty string is neither" };
3940
- }
3941
- const credential = deps.credentialOf(sessionId);
3942
- if (credential === void 0) {
3943
- return { ok: false, message: `the front door has not observed Remote Control session ${sessionId}: it may never have passed through this door, or it ended or expired (an entry lives only a bounded idle period past its last observed traffic)` };
3944
- }
3945
- if (credential.authorization === void 0) {
3946
- return noOAuthCredentialResult(sessionId);
3947
- }
3948
- const body = JSON.stringify(buildRcEventWriteBody(buildRcSetModelPayload(deps.newUuid(), model)));
3949
- let answer;
3950
- try {
3951
- answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3952
- } catch (error) {
3953
- return { ok: false, message: `the door could not reach the API host to set the model on session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
3954
- }
3955
- return rcEventWriteResultFromAnswer(answer);
4172
+ function literalDial(target) {
4173
+ return net2.connect({ host: target.host, port: target.port });
3956
4174
  }
3957
- async function setRcSessionPermissionMode(deps, sessionId, mode) {
3958
- const credential = deps.credentialOf(sessionId);
3959
- if (credential === void 0) {
3960
- return { ok: false, message: `the front door has not observed Remote Control session ${sessionId}: it may never have passed through this door, or it ended or expired (an entry lives only a bounded idle period past its last observed traffic)` };
3961
- }
3962
- if (credential.authorization === void 0) {
3963
- return noOAuthCredentialResult(sessionId);
3964
- }
3965
- const body = JSON.stringify(buildRcEventWriteBody(buildRcSetPermissionModePayload(deps.newUuid(), mode)));
3966
- let answer;
3967
- try {
3968
- answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3969
- } catch (error) {
3970
- return { ok: false, message: `the door could not reach the API host to set the permission mode on session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
4175
+ async function reservedDial(target) {
4176
+ for (let attempt = 0; ; attempt += 1) {
4177
+ const outcome = await dialOnce({ host: target.host, port: target.port, localPort: upstreamLocalPort(), lookup: realAddressLookup });
4178
+ if (outcome.error === null) {
4179
+ return outcome;
4180
+ }
4181
+ const held = outcome.error.code !== void 0 && HELD_PORT_ERRNOS.has(outcome.error.code);
4182
+ if (!held || attempt === UPSTREAM_LOCAL_PORT_COUNT - 1) {
4183
+ return outcome;
4184
+ }
3971
4185
  }
3972
- return rcEventWriteResultFromAnswer(answer);
3973
4186
  }
3974
-
3975
- // src/frontdoor/rcControl.ts
3976
- import * as https2 from "node:https";
3977
- var RC_PERMISSION_MODE_LIST = RC_PERMISSION_MODES.join(", ");
3978
- var CONTROL_PATH_PREFIX = "/__agent-shim/rc";
3979
- var CONTROL_BODY_CAP_BYTES = 10485760;
3980
- function answerJson(response, status, body) {
3981
- const text = JSON.stringify(body);
3982
- response.writeHead(status, { "content-type": "application/json", "content-length": String(Buffer.byteLength(text, "utf8")) });
3983
- response.end(text);
4187
+ function requestServername(options, fallback) {
4188
+ return "servername" in options && typeof options.servername === "string" && options.servername !== "" ? options.servername : fallback;
3984
4189
  }
3985
- async function readBody(request4) {
3986
- return await new Promise((resolve, reject) => {
3987
- const chunks = [];
3988
- let total = 0;
3989
- request4.on("data", (chunk) => {
3990
- total += chunk.length;
3991
- if (total > CONTROL_BODY_CAP_BYTES) {
3992
- request4.destroy();
3993
- reject(new Error("too large"));
4190
+ var ExemptHttpAgent = class extends http.Agent {
4191
+ // A reserved-port dial that has to retry cannot produce its socket synchronously, so it settles first and arrives through the callback (node's documented asynchronous createConnection form); a literal dial has nothing to retry and keeps node's own synchronous shape.
4192
+ createConnection(options, oncreate) {
4193
+ const target = dialTarget(options, HTTP_PORT);
4194
+ if (net2.isIP(target.host) !== 0) {
4195
+ return literalDial(target);
4196
+ }
4197
+ void reservedDial(target).then((outcome) => {
4198
+ oncreate?.(outcome.error, outcome.socket);
4199
+ });
4200
+ return void 0;
4201
+ }
4202
+ };
4203
+ var ExemptTlsAgent = class extends https2.Agent {
4204
+ // The same split as the plain sibling: a literal dial wraps TLS around the socket node's own agent would have returned, a reserved dial settles (retries included) first and arrives through the callback, which is exactly where node's own agent wraps its own dials.
4205
+ createConnection(options, oncreate) {
4206
+ const target = dialTarget(options, HTTPS_PORT);
4207
+ if (net2.isIP(target.host) !== 0) {
4208
+ return tls.connect({ socket: literalDial(target), servername: requestServername(options, target.host), ...tlsFacts(options) });
4209
+ }
4210
+ void reservedDial(target).then((outcome) => {
4211
+ if (outcome.error !== null) {
4212
+ oncreate?.(outcome.error, outcome.socket);
3994
4213
  return;
3995
4214
  }
3996
- chunks.push(chunk);
3997
- });
3998
- request4.on("end", () => {
3999
- resolve(Buffer.concat(chunks).toString("utf8"));
4000
- });
4001
- request4.on("error", () => {
4002
- reject(new Error("the request body ended early"));
4215
+ oncreate?.(null, tls.connect({ socket: outcome.socket, servername: requestServername(options, target.host), ...tlsFacts(options) }));
4003
4216
  });
4004
- });
4217
+ return void 0;
4218
+ }
4219
+ };
4220
+ function tlsFacts(options) {
4221
+ const facts = {};
4222
+ if ("ca" in options && Array.isArray(options.ca)) {
4223
+ facts.ca = options.ca;
4224
+ }
4225
+ if ("rejectUnauthorized" in options && typeof options.rejectUnauthorized === "boolean") {
4226
+ facts.rejectUnauthorized = options.rejectUnauthorized;
4227
+ }
4228
+ return facts;
4005
4229
  }
4006
- function createRcControlHandler(deps) {
4007
- const sessionsPath = `${CONTROL_PATH_PREFIX}/sessions`;
4008
- const statusPath = `${CONTROL_PATH_PREFIX}/status`;
4009
- const pendingPath = `${CONTROL_PATH_PREFIX}/pending`;
4010
- const injectPath = `${CONTROL_PATH_PREFIX}/inject`;
4011
- const answerPath = `${CONTROL_PATH_PREFIX}/answer`;
4012
- const interruptPath = `${CONTROL_PATH_PREFIX}/interrupt`;
4013
- const setModelPath = `${CONTROL_PATH_PREFIX}/set-model`;
4014
- const setPermissionModePath = `${CONTROL_PATH_PREFIX}/set-permission-mode`;
4015
- return (request4, response) => {
4016
- const presented = request4.headers.authorization;
4017
- const bearerPrefix = "bearer ".length;
4018
- const token = typeof presented === "string" && presented.slice(0, bearerPrefix).toLowerCase() === "bearer " ? presented.slice(bearerPrefix) : void 0;
4019
- if (token === void 0 || !isLiveCapability(token, [deps.expectedToken])) {
4020
- answerJson(response, HTTP_STATUS.unauthorized, { error: "the front door's control routes demand this generation's control token as a Bearer credential" });
4021
- return;
4022
- }
4023
- const url = new URL(request4.url ?? "/", "http://127.0.0.1");
4024
- const handle = async () => {
4025
- if (url.pathname === sessionsPath) {
4026
- if (request4.method !== "GET") {
4027
- answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the session list is a GET" });
4028
- return;
4029
- }
4030
- answerJson(response, HTTP_STATUS.ok, { sessions: deps.list() });
4031
- return;
4230
+ function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
4231
+ const agent = new ExemptTlsAgent();
4232
+ return {
4233
+ writeEvents: async (sessionId, headers, body) => await new Promise((resolve, reject) => {
4234
+ const options = {
4235
+ host: target.host,
4236
+ port: target.port,
4237
+ method: "POST",
4238
+ path: `/v1/code/sessions/${encodeURIComponent(sessionId)}/events`,
4239
+ // 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.
4240
+ headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
4241
+ agent
4242
+ };
4243
+ options.servername = target.servername ?? target.host;
4244
+ if (target.ca !== void 0) {
4245
+ options.ca = [...target.ca];
4032
4246
  }
4033
- if (url.pathname === statusPath || url.pathname === pendingPath) {
4034
- if (request4.method !== "GET") {
4035
- answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the status and pending reads are GETs" });
4036
- return;
4037
- }
4038
- const session = url.searchParams.get("session") ?? void 0;
4039
- if (session !== void 0 && !deps.statusOf(session).some((entry) => entry.id === session)) {
4040
- answerJson(response, HTTP_STATUS.notFound, { error: `the front door has not observed Remote Control session ${session}: it may never have passed through this door, or it ended or expired (an entry lives only a bounded idle period past its last observed traffic)` });
4041
- return;
4042
- }
4043
- answerJson(response, HTTP_STATUS.ok, url.pathname === statusPath ? { statuses: deps.statusOf(session) } : { pending: deps.pendingOf(session) });
4044
- return;
4247
+ if (target.rejectUnauthorized !== void 0) {
4248
+ options.rejectUnauthorized = target.rejectUnauthorized;
4045
4249
  }
4046
- if (url.pathname === injectPath) {
4047
- if (request4.method !== "POST") {
4048
- answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the inject operation is a POST" });
4049
- return;
4050
- }
4051
- let parsed;
4052
- try {
4053
- parsed = JSON.parse(await readBody(request4));
4054
- } catch {
4055
- answerJson(response, HTTP_STATUS.badRequest, { error: "the inject body is not readable JSON within the control body cap" });
4056
- return;
4057
- }
4058
- if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("text" in parsed) || typeof parsed.session !== "string" || typeof parsed.text !== "string" || parsed.session === "" || parsed.text === "") {
4059
- answerJson(response, HTTP_STATUS.badRequest, { error: "the inject body must be JSON naming a non-empty session id and text" });
4060
- return;
4061
- }
4062
- const result = await deps.inject(parsed.session, parsed.text);
4063
- if (result.ok) {
4064
- answerJson(response, HTTP_STATUS.ok, { session: parsed.session, sequenceNums: result.sequenceNums });
4065
- return;
4066
- }
4067
- answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
4068
- return;
4250
+ const request4 = https2.request(options, (response) => {
4251
+ const chunks = [];
4252
+ response.on("data", (chunk) => {
4253
+ chunks.push(chunk);
4254
+ });
4255
+ response.on("end", () => {
4256
+ resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, body: Buffer.concat(chunks).toString("utf8") });
4257
+ });
4258
+ });
4259
+ request4.once("error", reject);
4260
+ request4.end(body);
4261
+ })
4262
+ };
4263
+ }
4264
+ var NO_CHUNKS = {
4265
+ [Symbol.asyncIterator]: () => ({
4266
+ next: async () => await Promise.resolve({ done: true, value: void 0 })
4267
+ })
4268
+ };
4269
+ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
4270
+ const agent = new ExemptTlsAgent();
4271
+ const request4 = async (method, path29, headers, body, signal) => await new Promise((resolve, reject) => {
4272
+ const options = {
4273
+ host: target.host,
4274
+ port: target.port,
4275
+ method,
4276
+ path: path29,
4277
+ // 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.
4278
+ headers: { ...headers, host: target.servername ?? target.host, ...body === void 0 ? {} : { "content-length": String(Buffer.byteLength(body, "utf8")) } },
4279
+ agent,
4280
+ signal
4281
+ };
4282
+ options.servername = target.servername ?? target.host;
4283
+ if (target.ca !== void 0) {
4284
+ options.ca = [...target.ca];
4285
+ }
4286
+ if (target.rejectUnauthorized !== void 0) {
4287
+ options.rejectUnauthorized = target.rejectUnauthorized;
4288
+ }
4289
+ const sent = https2.request(options, (response) => {
4290
+ resolve({ status: response.statusCode ?? HTTP_BAD_GATEWAY, response });
4291
+ });
4292
+ sent.once("error", reject);
4293
+ if (body === void 0) {
4294
+ sent.end();
4295
+ } else {
4296
+ sent.end(body);
4297
+ }
4298
+ });
4299
+ const readAll = async (response) => {
4300
+ const chunks = [];
4301
+ for await (const chunk of upstreamChunks(response)) {
4302
+ chunks.push(Buffer.from(chunk));
4303
+ }
4304
+ return Buffer.concat(chunks).toString("utf8");
4305
+ };
4306
+ return {
4307
+ announcePresence: async (sessionId, headers, clientId) => {
4308
+ const body = JSON.stringify({ client_id: clientId, clear: false });
4309
+ const { status, response } = await request4("POST", `/v1/code/sessions/${encodeURIComponent(sessionId)}/client/presence`, { ...headers, "content-type": "application/json" }, body, new AbortController().signal);
4310
+ return { status, body: await readAll(response) };
4311
+ },
4312
+ openStream: async (sessionId, headers, resume, signal) => {
4313
+ const path29 = resume === void 0 ? `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream` : `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream?from_sequence_num=${String(resume.fromSequenceNum)}`;
4314
+ const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
4315
+ const { status, response } = await request4("GET", path29, streamHeaders, void 0, signal);
4316
+ if (!isSuccessful2(status)) {
4317
+ await readAll(response);
4318
+ return { status, chunks: NO_CHUNKS };
4069
4319
  }
4070
- if (url.pathname === answerPath) {
4071
- if (request4.method !== "POST") {
4072
- answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the answer operation is a POST" });
4073
- return;
4074
- }
4075
- let parsed;
4076
- try {
4077
- parsed = JSON.parse(await readBody(request4));
4078
- } catch {
4079
- answerJson(response, HTTP_STATUS.badRequest, { error: "the answer body is not readable JSON within the control body cap" });
4080
- return;
4081
- }
4082
- if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("request" in parsed) || !("approve" in parsed) || typeof parsed.session !== "string" || typeof parsed.request !== "string" || typeof parsed.approve !== "boolean" || parsed.session === "" || parsed.request === "") {
4083
- answerJson(response, HTTP_STATUS.badRequest, { error: "the answer body must be JSON naming a non-empty session id, request id, and an approve boolean" });
4084
- return;
4085
- }
4086
- const text = "text" in parsed && typeof parsed.text === "string" ? parsed.text : void 0;
4087
- if (parsed.approve && text !== void 0 && text !== "") {
4088
- answerJson(response, HTTP_STATUS.badRequest, { error: "an approval carries no text (the protocol's allow result has no message field); text is the denial message" });
4089
- return;
4090
- }
4091
- const result = await deps.answer(parsed.session, parsed.request, { approve: parsed.approve, message: parsed.approve ? void 0 : text });
4092
- if (result.ok) {
4093
- answerJson(response, HTTP_STATUS.ok, { session: parsed.session, request: parsed.request, sequenceNums: result.sequenceNums });
4094
- return;
4095
- }
4096
- answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
4320
+ return { status, chunks: response };
4321
+ }
4322
+ };
4323
+ }
4324
+ function realConnectEffects() {
4325
+ const plainAgent = new ExemptHttpAgent();
4326
+ const tlsAgent = new ExemptTlsAgent();
4327
+ const listenOnce = async (port, onSocket) => await new Promise((resolve, reject) => {
4328
+ const accepted = /* @__PURE__ */ new Set();
4329
+ const server = net2.createServer((socket) => {
4330
+ accepted.add(socket);
4331
+ socket.on("close", () => {
4332
+ accepted.delete(socket);
4333
+ });
4334
+ onSocket(socket);
4335
+ });
4336
+ server.once("error", reject);
4337
+ server.listen(port, "127.0.0.1", () => {
4338
+ const address = server.address();
4339
+ const bound = typeof address === "object" && address !== null ? address.port : 0;
4340
+ if (bound === 0) {
4341
+ reject(new Error("could not bind a loopback port"));
4097
4342
  return;
4098
4343
  }
4099
- if (url.pathname === interruptPath) {
4100
- if (request4.method !== "POST") {
4101
- answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the interrupt operation is a POST" });
4102
- return;
4344
+ resolve({
4345
+ port: bound,
4346
+ close: async () => {
4347
+ await new Promise((closeResolve) => {
4348
+ server.close(() => {
4349
+ closeResolve(void 0);
4350
+ });
4351
+ for (const socket of accepted) {
4352
+ socket.destroy();
4353
+ }
4354
+ });
4103
4355
  }
4104
- let parsed;
4356
+ });
4357
+ });
4358
+ });
4359
+ return {
4360
+ listenLoopback: async (preferredPort, onSocket) => {
4361
+ if (preferredPort !== void 0) {
4105
4362
  try {
4106
- parsed = JSON.parse(await readBody(request4));
4363
+ return await listenOnce(preferredPort, onSocket);
4107
4364
  } catch {
4108
- answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body is not readable JSON within the control body cap" });
4109
- return;
4110
- }
4111
- if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || typeof parsed.session !== "string" || parsed.session === "") {
4112
- answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body must be JSON naming a non-empty session id" });
4113
- return;
4114
- }
4115
- const result = await deps.interrupt(parsed.session);
4116
- if (result.ok) {
4117
- answerJson(response, HTTP_STATUS.ok, { session: parsed.session, sequenceNums: result.sequenceNums });
4118
- return;
4119
4365
  }
4120
- answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
4121
- return;
4122
4366
  }
4123
- if (url.pathname === setModelPath) {
4124
- if (request4.method !== "POST") {
4125
- answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-model operation is a POST" });
4126
- return;
4367
+ return await listenOnce(0, onSocket);
4368
+ },
4369
+ createTlsAcceptor: (leaf, onSecure, alpnProtocols) => {
4370
+ let pendingContext = "";
4371
+ const server = tls.createServer(
4372
+ { key: leaf.keyPem, cert: leaf.certPem, ...alpnProtocols === void 0 ? {} : { ALPNProtocols: [...alpnProtocols] } },
4373
+ (secure) => {
4374
+ onSecure(secure, pendingContext);
4127
4375
  }
4128
- let parsed;
4129
- try {
4130
- parsed = JSON.parse(await readBody(request4));
4131
- } catch {
4132
- answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body is not readable JSON within the control body cap" });
4133
- return;
4376
+ );
4377
+ server.on("clientError", (_error, socket) => {
4378
+ socket.destroy();
4379
+ });
4380
+ return {
4381
+ // tls.Server is a net.Server whose connection listener wraps the raw socket in the TLS handshake, and emitting the event by hand is what runs that listener on a socket this process already owns (the CONNECT half of the connection) rather than one the server accepted itself.
4382
+ accept: (socket, context) => {
4383
+ pendingContext = context;
4384
+ server.emit("connection", socket);
4385
+ },
4386
+ close: () => {
4387
+ server.close();
4134
4388
  }
4135
- if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("model" in parsed) || typeof parsed.session !== "string" || typeof parsed.model !== "string" || parsed.session === "" || parsed.model === "") {
4136
- answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body must be JSON naming a non-empty session id and model id" });
4137
- return;
4389
+ };
4390
+ },
4391
+ createSniTlsAcceptor: (hosts, leafFor, onSecure) => {
4392
+ const contexts = new Map(
4393
+ hosts.map((host) => {
4394
+ const leaf = leafFor(host);
4395
+ return [host, tls.createSecureContext({ key: leaf.keyPem, cert: leaf.certPem })];
4396
+ })
4397
+ );
4398
+ const server = tls.createServer(
4399
+ {
4400
+ // No default key or cert exists, so the SNI selection is the only source of credentials: an unserved name fails its handshake here, and a client that sent no SNI at all never reaches the selection and fails for want of any certificate, which is exactly the fail-closed shape this surface wants.
4401
+ SNICallback: (servername, callback) => {
4402
+ const context = contexts.get(servername);
4403
+ callback(context === void 0 ? new Error(`the transparent surface serves no host named ${servername}`) : null, context);
4404
+ }
4405
+ },
4406
+ (secure) => {
4407
+ onSecure(secure, typeof secure.servername === "string" ? secure.servername : "");
4138
4408
  }
4139
- const result = await deps.setModel(parsed.session, parsed.model);
4140
- if (result.ok) {
4141
- answerJson(response, HTTP_STATUS.ok, { session: parsed.session, model: parsed.model, sequenceNums: result.sequenceNums });
4142
- return;
4409
+ );
4410
+ server.on("clientError", (_error, socket) => {
4411
+ socket.destroy();
4412
+ });
4413
+ return {
4414
+ // The same hand-off the single-leaf acceptor uses: emitting the connection event runs the TLS server's own connection listener on a socket this process already owns (the redirect half of the connection) rather than one the server accepted itself.
4415
+ accept: (socket) => {
4416
+ server.emit("connection", socket);
4417
+ },
4418
+ close: () => {
4419
+ server.close();
4143
4420
  }
4144
- answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
4145
- return;
4421
+ };
4422
+ },
4423
+ createHttpSession: (handler, onUpgrade) => {
4424
+ const server = http.createServer(handler);
4425
+ server.on("clientError", (_error, socket) => {
4426
+ socket.destroy();
4427
+ });
4428
+ if (onUpgrade !== void 0) {
4429
+ server.on("upgrade", (request4, socket, head) => {
4430
+ onUpgrade(request4, socket, head);
4431
+ });
4146
4432
  }
4147
- if (url.pathname === setPermissionModePath) {
4148
- if (request4.method !== "POST") {
4149
- answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-permission-mode operation is a POST" });
4150
- return;
4433
+ return {
4434
+ serve: (socket) => {
4435
+ server.emit("connection", socket);
4436
+ },
4437
+ close: () => {
4438
+ server.close();
4439
+ server.closeAllConnections();
4151
4440
  }
4152
- let parsed;
4153
- try {
4154
- parsed = JSON.parse(await readBody(request4));
4155
- } catch {
4156
- answerJson(response, HTTP_STATUS.badRequest, { error: "the set-permission-mode body is not readable JSON within the control body cap" });
4157
- return;
4441
+ };
4442
+ },
4443
+ connectTlsUpstream: async (host, port, clientAlpn) => {
4444
+ const dialled = await reservedDial({ host, port });
4445
+ if (dialled.error !== null) {
4446
+ throw dialled.error;
4447
+ }
4448
+ return await new Promise((resolve, reject) => {
4449
+ const secure = tls.connect({ socket: dialled.socket, servername: host, ...clientAlpn === void 0 ? {} : { ALPNProtocols: [clientAlpn] } });
4450
+ secure.once("secureConnect", () => {
4451
+ resolve(secure);
4452
+ });
4453
+ secure.once("error", reject);
4454
+ });
4455
+ },
4456
+ connectTcp: async (host, port) => await new Promise((resolve, reject) => {
4457
+ const socket = net2.connect({ host, port });
4458
+ socket.once("connect", () => {
4459
+ resolve(socket);
4460
+ });
4461
+ socket.once("error", reject);
4462
+ }),
4463
+ forwardHttp: (target, request4, response, observer) => {
4464
+ const headers = forwardableHeaders(request4.headers);
4465
+ const options = {
4466
+ host: target.host,
4467
+ port: target.port,
4468
+ lookup: realAddressLookup,
4469
+ method: request4.method,
4470
+ path: request4.url,
4471
+ headers,
4472
+ agent: target.tls ? tlsAgent : plainAgent
4473
+ };
4474
+ if (target.tls) {
4475
+ options.servername = target.servername ?? target.host;
4476
+ if (target.ca !== void 0) {
4477
+ options.ca = [...target.ca];
4158
4478
  }
4159
- if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("mode" in parsed) || typeof parsed.session !== "string" || parsed.session === "" || !isRcPermissionMode(parsed.mode)) {
4160
- answerJson(response, HTTP_STATUS.badRequest, { error: `the set-permission-mode body must be JSON naming a non-empty session id and a mode the SDK's own type permits (${RC_PERMISSION_MODE_LIST})` });
4161
- return;
4479
+ if (target.rejectUnauthorized !== void 0) {
4480
+ options.rejectUnauthorized = target.rejectUnauthorized;
4162
4481
  }
4163
- const result = await deps.setPermissionMode(parsed.session, parsed.mode);
4164
- if (result.ok) {
4165
- answerJson(response, HTTP_STATUS.ok, { session: parsed.session, mode: parsed.mode, sequenceNums: result.sequenceNums });
4482
+ }
4483
+ const onUpstreamResponse = (upstreamResponse) => {
4484
+ observer?.onResponse(upstreamResponse.statusCode ?? HTTP_BAD_GATEWAY, upstreamResponse.headers);
4485
+ response.writeHead(upstreamResponse.statusCode ?? HTTP_BAD_GATEWAY, forwardableHeaders(upstreamResponse.headers));
4486
+ response.flushHeaders();
4487
+ if (observer !== void 0) {
4488
+ upstreamResponse.on("data", (chunk) => {
4489
+ observer.onResponseChunk(chunk);
4490
+ });
4491
+ }
4492
+ upstreamResponse.pipe(response);
4493
+ };
4494
+ const upstream = target.tls ? https2.request(options, onUpstreamResponse) : http.request(options, onUpstreamResponse);
4495
+ upstream.on("error", (error) => {
4496
+ if (response.headersSent) {
4497
+ response.destroy(error);
4166
4498
  return;
4167
4499
  }
4168
- answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
4169
- return;
4500
+ const body = `agent-shim headroom: upstream unreachable (${error.message})`;
4501
+ response.writeHead(HTTP_BAD_GATEWAY, { "content-type": "text/plain", "content-length": String(body.length) });
4502
+ response.end(body);
4503
+ });
4504
+ if (observer !== void 0) {
4505
+ request4.on("data", (chunk) => {
4506
+ observer.onRequestChunk(chunk);
4507
+ });
4508
+ if (observer.onRequestEnd !== void 0) {
4509
+ request4.on("end", () => {
4510
+ observer.onRequestEnd?.();
4511
+ });
4512
+ }
4513
+ response.on("close", () => {
4514
+ observer.onEnd();
4515
+ });
4170
4516
  }
4171
- answerJson(response, HTTP_STATUS.notFound, { error: `no such control route: ${url.pathname}` });
4172
- };
4173
- handle().catch((error) => {
4174
- answerJson(response, HTTP_STATUS.internalServerError, { error: `the control route failed: ${error instanceof Error ? error.message : String(error)}` });
4175
- });
4517
+ request4.pipe(upstream);
4518
+ request4.socket.setNoDelay(true);
4519
+ upstream.on("socket", (socket) => {
4520
+ socket.setNoDelay(true);
4521
+ });
4522
+ }
4176
4523
  };
4177
4524
  }
4178
- function realRcControlTransport(port, ca) {
4525
+
4526
+ // src/frontdoor/rcApi.ts
4527
+ import { createORPCClient, ORPCError } from "@orpc/client";
4528
+ import { RPCLink } from "@orpc/client/fetch";
4529
+ import { eventIterator, os, withEventMeta } from "@orpc/server";
4530
+ import { BodyLimitPlugin, RPCHandler } from "@orpc/server/node";
4531
+ import { Agent as Agent3, fetch as undiciFetch } from "undici";
4532
+
4533
+ // src/frontdoor/rcSchemas.ts
4534
+ import { z as z4 } from "zod";
4535
+ var RcPermissionModeSchema = z4.enum(RC_PERMISSION_MODES);
4536
+ var RcSessionSummarySchema = z4.strictObject({
4537
+ id: z4.string(),
4538
+ createdAt: z4.number(),
4539
+ lastSeenAt: z4.number()
4540
+ });
4541
+ function workerFactSchema(value) {
4542
+ return z4.strictObject({ value, observedAt: z4.number() });
4543
+ }
4544
+ var RcPendingRequestSummarySchema = z4.strictObject({
4545
+ sessionId: z4.string(),
4546
+ requestId: z4.string(),
4547
+ type: z4.string(),
4548
+ summary: z4.string(),
4549
+ observedAt: z4.number()
4550
+ });
4551
+ var RcSessionStatusSchema = z4.strictObject({
4552
+ ...RcSessionSummarySchema.shape,
4553
+ workerState: workerFactSchema(z4.string()).optional(),
4554
+ workerIdleSeconds: workerFactSchema(z4.number()).optional(),
4555
+ pending: z4.readonly(z4.array(RcPendingRequestSummarySchema))
4556
+ });
4557
+ var RcStreamEnvelopeSchema = z4.looseObject({
4558
+ /** The envelope's own event id, when the host sent one. */
4559
+ event_id: z4.string().optional(),
4560
+ /** The envelope's event type, the discriminator a consumer switches on (`control_request`, `user`, and kin). */
4561
+ event_type: z4.string(),
4562
+ /** The sequence number the host assigned, the value a resume continues after. */
4563
+ sequence_num: z4.int(),
4564
+ /** Who emitted the event (`worker` for the CLI's half, a client id for a client's). */
4565
+ source: z4.string(),
4566
+ /** The event's payload, carried verbatim: its shape is the payload family's, narrowed by whichever consumer needs it. Optional in the schema's own type because the door forwards the host's envelope as it arrived, asserting only the fields it relies on itself. */
4567
+ payload: z4.unknown().optional(),
4568
+ /** When the host created the event; the protocol's own type for this field is not established here, so it is forwarded rather than asserted. */
4569
+ created_at: z4.unknown().optional()
4570
+ });
4571
+ var RcStreamEventSchema = z4.strictObject({
4572
+ session: z4.string(),
4573
+ envelope: RcStreamEnvelopeSchema
4574
+ });
4575
+ var RcSessionQuerySchema = z4.strictObject({
4576
+ session: z4.string().min(1).optional()
4577
+ });
4578
+ var RcSendInputSchema = z4.strictObject({
4579
+ session: z4.string().min(1),
4580
+ text: z4.string().min(1)
4581
+ });
4582
+ var RcAnswerInputSchema = z4.strictObject({
4583
+ session: z4.string().min(1),
4584
+ request: z4.string().min(1),
4585
+ approve: z4.boolean(),
4586
+ text: z4.string().optional()
4587
+ }).refine((input) => !input.approve || input.text === void 0 || input.text === "", { message: "an approval carries no text (the protocol's allow result has no message field); text is the denial message" });
4588
+ var RcInterruptInputSchema = z4.strictObject({
4589
+ session: z4.string().min(1)
4590
+ });
4591
+ var RcSetModelInputSchema = z4.strictObject({
4592
+ session: z4.string().min(1),
4593
+ model: z4.string().min(1)
4594
+ });
4595
+ var RcSetPermissionModeInputSchema = z4.strictObject({
4596
+ session: z4.string().min(1),
4597
+ mode: RcPermissionModeSchema
4598
+ });
4599
+ var RcListOutputSchema = z4.strictObject({ sessions: z4.readonly(z4.array(RcSessionSummarySchema)) });
4600
+ var RcStatusOutputSchema = z4.strictObject({ statuses: z4.readonly(z4.array(RcSessionStatusSchema)) });
4601
+ var RcPendingOutputSchema = z4.strictObject({ pending: z4.readonly(z4.array(RcPendingRequestSummarySchema)) });
4602
+ var RcWriteOutputSchema = z4.strictObject({
4603
+ session: z4.string(),
4604
+ sequenceNums: z4.readonly(z4.array(z4.number()))
4605
+ });
4606
+ var RcAnswerOutputSchema = z4.strictObject({
4607
+ session: z4.string(),
4608
+ request: z4.string(),
4609
+ sequenceNums: z4.readonly(z4.array(z4.number()))
4610
+ });
4611
+
4612
+ // src/frontdoor/rcApi.ts
4613
+ var RC_ORPC_PATH_PREFIX = "/__agent-shim/orpc";
4614
+ var RC_SUBSCRIBER_BUFFER_EVENTS = 100;
4615
+ function createRcApiRouter(deps) {
4616
+ const authed = os.$context().use(({ context, next }) => {
4617
+ const presented = context.headers.authorization;
4618
+ const bearerPrefix = "bearer ".length;
4619
+ const token = typeof presented === "string" && presented.slice(0, bearerPrefix).toLowerCase() === "bearer " ? presented.slice(bearerPrefix) : void 0;
4620
+ if (token === void 0 || !isLiveCapability(token, [deps.expectedToken])) {
4621
+ throw new ORPCError("UNAUTHORIZED", { message: "the front door's typed API demands this generation's control token as a Bearer credential" });
4622
+ }
4623
+ return next();
4624
+ });
4625
+ const delivered = (sessionId, result) => {
4626
+ if (!result.ok) {
4627
+ throw new ORPCError("BAD_GATEWAY", { message: result.message });
4628
+ }
4629
+ return result.sequenceNums;
4630
+ };
4179
4631
  return {
4180
- request: async (options) => await new Promise((resolve, reject) => {
4181
- const request4 = https2.request(
4182
- {
4183
- host: "127.0.0.1",
4184
- port,
4185
- method: options.method,
4186
- path: options.path,
4187
- headers: options.body === void 0 ? options.headers : { ...options.headers, "content-length": String(Buffer.byteLength(options.body, "utf8")) },
4188
- agent: false,
4189
- ca: [ca]
4190
- },
4191
- (response) => {
4192
- const chunks = [];
4193
- response.on("data", (chunk) => {
4194
- chunks.push(chunk);
4195
- });
4196
- response.on("end", () => {
4197
- resolve({ status: response.statusCode ?? 0, body: Buffer.concat(chunks).toString("utf8") });
4198
- });
4632
+ rc: {
4633
+ list: authed.output(RcListOutputSchema).handler(() => ({ sessions: deps.list() })),
4634
+ status: authed.input(RcSessionQuerySchema).output(RcStatusOutputSchema).handler(({ input }) => {
4635
+ const statuses = deps.statusOf(input.session);
4636
+ if (input.session !== void 0 && !statuses.some((entry) => entry.id === input.session)) {
4637
+ throw new ORPCError("NOT_FOUND", { message: rcSessionNotObservedMessage(input.session) });
4638
+ }
4639
+ return { statuses };
4640
+ }),
4641
+ pending: authed.input(RcSessionQuerySchema).output(RcPendingOutputSchema).handler(({ input }) => {
4642
+ const pending = deps.pendingOf(input.session);
4643
+ if (input.session !== void 0 && deps.statusOf(input.session).length === 0) {
4644
+ throw new ORPCError("NOT_FOUND", { message: rcSessionNotObservedMessage(input.session) });
4645
+ }
4646
+ return { pending };
4647
+ }),
4648
+ send: authed.input(RcSendInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.inject(input.session, input.text)) })),
4649
+ answer: authed.input(RcAnswerInputSchema).output(RcAnswerOutputSchema).handler(async ({ input }) => {
4650
+ const decision = { approve: input.approve, message: input.approve || input.text === void 0 ? void 0 : input.text };
4651
+ return { session: input.session, request: input.request, sequenceNums: delivered(input.session, await deps.answer(input.session, input.request, decision)) };
4652
+ }),
4653
+ interrupt: authed.input(RcInterruptInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.interrupt(input.session)) })),
4654
+ setModel: authed.input(RcSetModelInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.setModel(input.session, input.model)) })),
4655
+ setPermissionMode: authed.input(RcSetPermissionModeInputSchema).output(RcWriteOutputSchema).handler(async ({ input }) => ({ session: input.session, sequenceNums: delivered(input.session, await deps.setPermissionMode(input.session, input.mode)) })),
4656
+ subscribe: authed.input(RcSessionQuerySchema).output(eventIterator(RcStreamEventSchema)).handler(async function* ({ input, signal }) {
4657
+ const queue = [];
4658
+ let wake;
4659
+ const detach = deps.fanout.subscribe(input.session, (event) => {
4660
+ if (queue.length >= RC_SUBSCRIBER_BUFFER_EVENTS) {
4661
+ queue.shift();
4662
+ }
4663
+ queue.push(event);
4664
+ wake?.();
4665
+ });
4666
+ const ended = new Promise((resolve) => {
4667
+ signal?.addEventListener("abort", () => {
4668
+ resolve();
4669
+ }, { once: true });
4670
+ });
4671
+ try {
4672
+ for (; ; ) {
4673
+ signal?.throwIfAborted();
4674
+ const event = queue.shift();
4675
+ if (event !== void 0) {
4676
+ yield withEventMeta(event, { id: String(event.envelope.sequence_num) });
4677
+ continue;
4678
+ }
4679
+ await Promise.race([
4680
+ new Promise((resolve) => {
4681
+ wake = resolve;
4682
+ }),
4683
+ ended
4684
+ ]);
4685
+ }
4686
+ } finally {
4687
+ detach();
4199
4688
  }
4200
- );
4201
- request4.once("error", reject);
4202
- if (options.body === void 0) {
4203
- request4.end();
4204
- } else {
4205
- request4.end(options.body);
4206
- }
4207
- })
4689
+ })
4690
+ }
4208
4691
  };
4209
4692
  }
4210
- function controlErrorOf(body) {
4211
- try {
4212
- const parsed = JSON.parse(body);
4213
- if (typeof parsed === "object" && parsed !== null && "error" in parsed && typeof parsed.error === "string") {
4214
- return parsed.error;
4693
+ function createRcApiNodeHandler(deps) {
4694
+ const handler = new RPCHandler(createRcApiRouter(deps), { plugins: [new BodyLimitPlugin({ maxBodySize: CONTROL_BODY_CAP_BYTES })] });
4695
+ return {
4696
+ pathPrefix: RC_ORPC_PATH_PREFIX,
4697
+ handle: async (request4, response) => await handler.handle(request4, response, { context: { headers: request4.headers }, prefix: RC_ORPC_PATH_PREFIX })
4698
+ };
4699
+ }
4700
+ function frontDoorRcApiClient(port, ca, token) {
4701
+ const dispatcher = new Agent3({ connect: { ca } });
4702
+ const link = new RPCLink({
4703
+ url: `https://127.0.0.1:${String(port)}${RC_ORPC_PATH_PREFIX}`,
4704
+ headers: { authorization: `Bearer ${token}` },
4705
+ // undici's own Request and Response types and the global ones are distinct declarations of the same standard shapes (the package's bundled types sit beside @types/node's own), and its fetch refuses the global Request instance outright, so the call is rebuilt from the request's parts and the answer re-wrapped through the global constructors, never cast between the two. The request body is one bounded JSON object (the same protocol cap the handler enforces), so reading it whole costs nothing; it is the answer that streams.
4706
+ fetch: async (request4, _init, options) => {
4707
+ const body = request4.body === null ? void 0 : Buffer.from(await request4.arrayBuffer());
4708
+ const answered = await undiciFetch(request4.url, {
4709
+ method: request4.method,
4710
+ headers: [...request4.headers],
4711
+ ...body === void 0 ? {} : { body },
4712
+ dispatcher,
4713
+ ...options.signal === void 0 ? {} : { signal: options.signal }
4714
+ });
4715
+ return new Response(answered.body, { status: answered.status, statusText: answered.statusText, headers: [...answered.headers] });
4215
4716
  }
4216
- } catch {
4217
- return body;
4218
- }
4219
- return void 0;
4717
+ });
4718
+ return createORPCClient(link);
4220
4719
  }
4221
- function isRcSessionSummary(value) {
4222
- if (typeof value !== "object" || value === null) {
4223
- return false;
4224
- }
4225
- if (!("id" in value) || !("createdAt" in value) || !("lastSeenAt" in value)) {
4226
- return false;
4227
- }
4228
- return typeof value.id === "string" && typeof value.createdAt === "number" && typeof value.lastSeenAt === "number";
4720
+
4721
+ // src/frontdoor/rcStream.ts
4722
+ var RC_STREAM_EVENT_NAME = "client_event";
4723
+ var SSE_BYTE_ORDER_MARK = 65279;
4724
+ var RC_STREAM_BACKOFF_MS = 45e3;
4725
+ var SUCCESS_STATUS_MAX_EXCLUSIVE3 = 300;
4726
+ function isSuccessful3(status) {
4727
+ return status >= HTTP_STATUS.ok && status < SUCCESS_STATUS_MAX_EXCLUSIVE3;
4728
+ }
4729
+ function createSseParser() {
4730
+ let buffer = "";
4731
+ let eventName;
4732
+ let eventId;
4733
+ let dataLines = [];
4734
+ let seenAnyText = false;
4735
+ return {
4736
+ write: (text) => {
4737
+ let incoming = text;
4738
+ if (!seenAnyText) {
4739
+ seenAnyText = true;
4740
+ incoming = incoming.charCodeAt(0) === SSE_BYTE_ORDER_MARK ? incoming.slice(1) : incoming;
4741
+ }
4742
+ buffer += incoming;
4743
+ const completed = [];
4744
+ for (; ; ) {
4745
+ const carriage = buffer.indexOf("\r");
4746
+ const lineFeed = buffer.indexOf("\n");
4747
+ const end = carriage === -1 ? lineFeed : lineFeed === -1 ? carriage : carriage < lineFeed ? carriage : lineFeed;
4748
+ if (end === -1) {
4749
+ break;
4750
+ }
4751
+ if (buffer[end] === "\r" && end === buffer.length - 1) {
4752
+ break;
4753
+ }
4754
+ const after = buffer[end] === "\r" && buffer[end + 1] === "\n" ? end + 2 : end + 1;
4755
+ const line = buffer.slice(0, end);
4756
+ buffer = buffer.slice(after);
4757
+ if (line === "") {
4758
+ if (dataLines.length > 0) {
4759
+ completed.push({ event: eventName, id: eventId, data: dataLines.join("\n") });
4760
+ }
4761
+ eventName = void 0;
4762
+ dataLines = [];
4763
+ continue;
4764
+ }
4765
+ if (line.startsWith(":")) {
4766
+ continue;
4767
+ }
4768
+ const colon = line.indexOf(":");
4769
+ const field = colon === -1 ? line : line.slice(0, colon);
4770
+ const value = colon === -1 ? "" : line.slice(colon + 1).replace(/^ /, "");
4771
+ if (field === "event") {
4772
+ eventName = value;
4773
+ } else if (field === "data") {
4774
+ dataLines.push(value);
4775
+ } else if (field === "id") {
4776
+ eventId = value;
4777
+ }
4778
+ }
4779
+ return completed;
4780
+ }
4781
+ };
4229
4782
  }
4230
- function isRcWorkerFact(value, valueIs) {
4231
- if (typeof value !== "object" || value === null) {
4232
- return false;
4233
- }
4234
- if (!("value" in value) || !("observedAt" in value)) {
4235
- return false;
4236
- }
4237
- return valueIs(value.value) && typeof value.observedAt === "number";
4783
+ function isRecord3(value) {
4784
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4238
4785
  }
4239
- function isRcSessionStatus(value) {
4240
- if (!isRcSessionSummary(value)) {
4241
- return false;
4242
- }
4243
- if (!("pending" in value) || !Array.isArray(value.pending)) {
4244
- return false;
4245
- }
4246
- if ("workerState" in value && value.workerState !== void 0 && !isRcWorkerFact(value.workerState, (candidate) => typeof candidate === "string")) {
4247
- return false;
4786
+ function sequenceNumOf(value) {
4787
+ if (typeof value === "number" && Number.isFinite(value) && Number.isInteger(value)) {
4788
+ return value;
4248
4789
  }
4249
- if ("workerIdleSeconds" in value && value.workerIdleSeconds !== void 0 && !isRcWorkerFact(value.workerIdleSeconds, (candidate) => typeof candidate === "number")) {
4250
- return false;
4790
+ if (typeof value === "string" && /^[0-9]+$/.test(value)) {
4791
+ return Number(value);
4251
4792
  }
4252
- return value.pending.every((candidate) => isRcPendingRequestSummary(candidate));
4793
+ return void 0;
4253
4794
  }
4254
- function isRcPendingRequestSummary(value) {
4255
- if (typeof value !== "object" || value === null) {
4256
- return false;
4257
- }
4258
- if (!("sessionId" in value) || !("requestId" in value) || !("type" in value) || !("summary" in value) || !("observedAt" in value)) {
4259
- return false;
4795
+ function parseRcStreamEnvelope(parsed) {
4796
+ if (parsed.event !== RC_STREAM_EVENT_NAME) {
4797
+ return void 0;
4260
4798
  }
4261
- return typeof value.sessionId === "string" && typeof value.requestId === "string" && typeof value.type === "string" && typeof value.summary === "string" && typeof value.observedAt === "number";
4262
- }
4263
- function sequenceNumsOfBody(body) {
4264
- let parsed;
4799
+ let data;
4265
4800
  try {
4266
- parsed = JSON.parse(body);
4801
+ data = JSON.parse(parsed.data);
4267
4802
  } catch {
4268
4803
  return void 0;
4269
4804
  }
4270
- if (typeof parsed !== "object" || parsed === null || !("sequenceNums" in parsed) || !Array.isArray(parsed.sequenceNums)) {
4805
+ if (!isRecord3(data)) {
4271
4806
  return void 0;
4272
4807
  }
4273
- const sequenceNums = [];
4274
- const values = parsed.sequenceNums;
4275
- for (const value of values) {
4276
- if (typeof value !== "number") {
4277
- return void 0;
4278
- }
4279
- sequenceNums.push(value);
4808
+ const eventType = data.event_type;
4809
+ const sequenceNum = sequenceNumOf(data.sequence_num);
4810
+ const source = data.source;
4811
+ if (typeof eventType !== "string" || sequenceNum === void 0 || typeof source !== "string") {
4812
+ return void 0;
4280
4813
  }
4281
- return sequenceNums;
4814
+ return { ...data, event_type: eventType, sequence_num: sequenceNum, source };
4282
4815
  }
4283
- function frontDoorRcControl(transport, token) {
4284
- const headers = { authorization: `Bearer ${token}` };
4285
- const listAnswerOf = async (path29, what) => {
4286
- try {
4287
- return await transport.request({ method: "GET", path: path29, headers });
4288
- } catch (error) {
4289
- throw new Error(`${what}: could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
4816
+ function createRcEventFanout() {
4817
+ const perSession = /* @__PURE__ */ new Map();
4818
+ const every = /* @__PURE__ */ new Set();
4819
+ return {
4820
+ publish: (event) => {
4821
+ for (const listener of every) {
4822
+ listener(event);
4823
+ }
4824
+ for (const listener of perSession.get(event.session) ?? []) {
4825
+ listener(event);
4826
+ }
4827
+ },
4828
+ subscribe: (session, listener) => {
4829
+ if (session === void 0) {
4830
+ every.add(listener);
4831
+ return () => {
4832
+ every.delete(listener);
4833
+ };
4834
+ }
4835
+ const listeners = perSession.get(session) ?? /* @__PURE__ */ new Set();
4836
+ perSession.set(session, listeners);
4837
+ listeners.add(listener);
4838
+ return () => {
4839
+ listeners.delete(listener);
4840
+ if (listeners.size === 0) {
4841
+ perSession.delete(session);
4842
+ }
4843
+ };
4290
4844
  }
4291
4845
  };
4292
- const sessionsOf = (answer, what) => {
4293
- if (answer.status !== HTTP_STATUS.ok) {
4294
- throw new Error(`${what} failed (HTTP ${String(answer.status)}): ${controlErrorOf(answer.body) ?? answer.body}`);
4295
- }
4296
- let parsed;
4297
- try {
4298
- parsed = JSON.parse(answer.body);
4299
- } catch {
4300
- throw new Error(`${what}: the door's answer was not JSON: ${answer.body}`);
4301
- }
4302
- if (typeof parsed !== "object" || parsed === null || !("sessions" in parsed) || !Array.isArray(parsed.sessions)) {
4303
- throw new Error(`${what}: the door's answer named no sessions list: ${answer.body}`);
4304
- }
4305
- const candidates = parsed.sessions;
4306
- const sessions = [];
4307
- for (const value of candidates) {
4308
- if (!isRcSessionSummary(value)) {
4309
- throw new Error(`${what}: the door's answer named no sessions list: ${answer.body}`);
4846
+ }
4847
+ function createRcStreamHub(deps) {
4848
+ const attachments = /* @__PURE__ */ new Map();
4849
+ const birth = (sessionId) => {
4850
+ const controller = new AbortController();
4851
+ let stopped = false;
4852
+ return {
4853
+ sessionId,
4854
+ clientId: deps.newClientId(),
4855
+ controller,
4856
+ isStopped: () => stopped,
4857
+ stop: () => {
4858
+ stopped = true;
4859
+ controller.abort();
4310
4860
  }
4311
- sessions.push(value);
4312
- }
4313
- return sessions;
4861
+ };
4314
4862
  };
4315
- const statusesOf = (answer, what) => {
4316
- if (answer.status !== HTTP_STATUS.ok) {
4317
- throw new Error(`${what} failed (HTTP ${String(answer.status)}): ${controlErrorOf(answer.body) ?? answer.body}`);
4318
- }
4319
- let parsed;
4320
- try {
4321
- parsed = JSON.parse(answer.body);
4322
- } catch {
4323
- throw new Error(`${what}: the door's answer was not JSON: ${answer.body}`);
4863
+ const streamHeaders = (credential) => {
4864
+ const headers = { accept: "text/event-stream" };
4865
+ if (credential.authorization !== void 0) {
4866
+ headers.authorization = credential.authorization;
4324
4867
  }
4325
- if (typeof parsed !== "object" || parsed === null || !("statuses" in parsed) || !Array.isArray(parsed.statuses)) {
4326
- throw new Error(`${what}: the door's answer named no status list: ${answer.body}`);
4868
+ if (credential.anthropicVersion !== void 0) {
4869
+ headers["anthropic-version"] = credential.anthropicVersion;
4327
4870
  }
4328
- const statuses = [];
4329
- for (const value of parsed.statuses) {
4330
- if (!isRcSessionStatus(value)) {
4331
- throw new Error(`${what}: the door's answer named no status list: ${answer.body}`);
4332
- }
4333
- statuses.push(value);
4871
+ if (credential.anthropicClientPlatform !== void 0) {
4872
+ headers["anthropic-client-platform"] = credential.anthropicClientPlatform;
4334
4873
  }
4335
- return statuses;
4874
+ return headers;
4336
4875
  };
4337
- const pendingOfAnswer = (answer, what) => {
4338
- if (answer.status !== HTTP_STATUS.ok) {
4339
- throw new Error(`${what} failed (HTTP ${String(answer.status)}): ${controlErrorOf(answer.body) ?? answer.body}`);
4340
- }
4341
- let parsed;
4342
- try {
4343
- parsed = JSON.parse(answer.body);
4344
- } catch {
4345
- throw new Error(`${what}: the door's answer was not JSON: ${answer.body}`);
4876
+ const attempt = async (attachment, credential) => {
4877
+ const headers = streamHeaders(credential);
4878
+ const presence = await deps.dial.announcePresence(attachment.sessionId, headers, attachment.clientId);
4879
+ if (presence.status === HTTP_STATUS.unauthorized) {
4880
+ return "unauthorized";
4881
+ }
4882
+ if (!isSuccessful3(presence.status)) {
4883
+ throw new Error(`the presence call answered HTTP ${String(presence.status)}: ${presence.body}`);
4884
+ }
4885
+ const cursor = deps.sequenceNumOf(attachment.sessionId);
4886
+ const resume = cursor === void 0 ? void 0 : { fromSequenceNum: cursor };
4887
+ const answer = await deps.dial.openStream(attachment.sessionId, headers, resume, attachment.controller.signal);
4888
+ if (answer.status === HTTP_STATUS.unauthorized) {
4889
+ return "unauthorized";
4346
4890
  }
4347
- if (typeof parsed !== "object" || parsed === null || !("pending" in parsed) || !Array.isArray(parsed.pending)) {
4348
- throw new Error(`${what}: the door's answer named no pending list: ${answer.body}`);
4891
+ if (!isSuccessful3(answer.status)) {
4892
+ throw new Error(`the client read stream answered HTTP ${String(answer.status)}`);
4349
4893
  }
4350
- const pending = [];
4351
- for (const value of parsed.pending) {
4352
- if (!isRcPendingRequestSummary(value)) {
4353
- throw new Error(`${what}: the door's answer named no pending list: ${answer.body}`);
4894
+ const parser = createSseParser();
4895
+ for await (const chunk of answer.chunks) {
4896
+ attachment.controller.signal.throwIfAborted();
4897
+ for (const parsed of parser.write(Buffer.from(chunk).toString("utf8"))) {
4898
+ const envelope = parseRcStreamEnvelope(parsed);
4899
+ if (envelope === void 0) {
4900
+ continue;
4901
+ }
4902
+ deps.fileStreamEvent(attachment.sessionId, envelope);
4903
+ deps.fanout.publish({ session: attachment.sessionId, envelope });
4354
4904
  }
4355
- pending.push(value);
4356
4905
  }
4357
- return pending;
4906
+ return "dropped";
4358
4907
  };
4359
- const writeOf = async (path29, body, describeFailure) => {
4360
- let answer;
4361
- try {
4362
- answer = await transport.request({ method: "POST", path: path29, headers: { ...headers, "content-type": "application/json" }, body });
4363
- } catch (error) {
4364
- return { ok: false, message: `could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}` };
4365
- }
4366
- if (answer.status === HTTP_STATUS.ok) {
4367
- const sequenceNums = sequenceNumsOfBody(answer.body);
4368
- if (sequenceNums === void 0) {
4369
- return { ok: false, message: `the door accepted the event but its answer named no sequence numbers: ${answer.body}` };
4908
+ const runLoop = async (attachment) => {
4909
+ let retriedUnauthorized = false;
4910
+ for (; ; ) {
4911
+ if (attachment.isStopped()) {
4912
+ return;
4370
4913
  }
4371
- return { ok: true, sequenceNums };
4914
+ const live = deps.trackedSessions().includes(attachment.sessionId);
4915
+ const credential = deps.credentialOf(attachment.sessionId);
4916
+ if (!live || credential?.authorization === void 0) {
4917
+ if (attachments.get(attachment.sessionId) === attachment) {
4918
+ attachments.delete(attachment.sessionId);
4919
+ }
4920
+ return;
4921
+ }
4922
+ let outcome;
4923
+ try {
4924
+ outcome = await attempt(attachment, credential);
4925
+ } catch (error) {
4926
+ if (attachment.isStopped()) {
4927
+ return;
4928
+ }
4929
+ deps.log?.(`rc stream ${attachment.sessionId}: attachment failed (${error instanceof Error ? error.message : String(error)}); backing off`);
4930
+ await deps.sleep(deps.backoffMs);
4931
+ retriedUnauthorized = false;
4932
+ continue;
4933
+ }
4934
+ if (attachment.isStopped()) {
4935
+ return;
4936
+ }
4937
+ if (outcome === "dropped") {
4938
+ retriedUnauthorized = false;
4939
+ continue;
4940
+ }
4941
+ if (!retriedUnauthorized) {
4942
+ retriedUnauthorized = true;
4943
+ continue;
4944
+ }
4945
+ deps.log?.(`rc stream ${attachment.sessionId}: the client read stream refused the observed Authorization bearer past one retry; backing off`);
4946
+ await deps.sleep(deps.backoffMs);
4947
+ retriedUnauthorized = false;
4372
4948
  }
4373
- return { ok: false, message: describeFailure(answer.status, controlErrorOf(answer.body) ?? answer.body) };
4374
4949
  };
4375
- const sessionQuery = (sessionId) => sessionId === void 0 ? "" : `?session=${encodeURIComponent(sessionId)}`;
4376
4950
  return {
4377
- listSessions: async () => sessionsOf(await listAnswerOf(`${CONTROL_PATH_PREFIX}/sessions`, "listing the door's Remote Control sessions"), "listing the door's Remote Control sessions"),
4378
- statusOf: async (sessionId) => statusesOf(await listAnswerOf(`${CONTROL_PATH_PREFIX}/status${sessionQuery(sessionId)}`, "reading the door's Remote Control session status"), "reading the door's Remote Control session status"),
4379
- pendingOf: async (sessionId) => pendingOfAnswer(await listAnswerOf(`${CONTROL_PATH_PREFIX}/pending${sessionQuery(sessionId)}`, "listing the door's pending Remote Control control requests"), "listing the door's pending Remote Control control requests"),
4380
- sendPrompt: async (sessionId, text) => await writeOf(`${CONTROL_PATH_PREFIX}/inject`, JSON.stringify({ session: sessionId, text }), (status, message) => `injecting into ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4381
- answerRequest: async (sessionId, requestId, decision) => await writeOf(
4382
- `${CONTROL_PATH_PREFIX}/answer`,
4383
- JSON.stringify({ session: sessionId, request: requestId, approve: decision.approve, ...decision.approve || decision.message === void 0 ? {} : { text: decision.message } }),
4384
- (status, message) => `answering control request ${requestId} on ${sessionId} failed (HTTP ${String(status)}): ${message}`
4385
- ),
4386
- interruptSession: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/interrupt`, JSON.stringify({ session: sessionId }), (status, message) => `interrupting ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4387
- setModel: async (sessionId, model) => await writeOf(`${CONTROL_PATH_PREFIX}/set-model`, JSON.stringify({ session: sessionId, model }), (status, message) => `setting the model on ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4388
- setPermissionMode: async (sessionId, mode) => await writeOf(`${CONTROL_PATH_PREFIX}/set-permission-mode`, JSON.stringify({ session: sessionId, mode }), (status, message) => `setting the permission mode on ${sessionId} failed (HTTP ${String(status)}): ${message}`)
4951
+ reconcile: () => {
4952
+ const live = new Set(deps.trackedSessions());
4953
+ for (const [sessionId, attachment] of attachments) {
4954
+ if (!live.has(sessionId)) {
4955
+ attachment.stop();
4956
+ attachments.delete(sessionId);
4957
+ }
4958
+ }
4959
+ for (const sessionId of live) {
4960
+ if (attachments.has(sessionId)) {
4961
+ continue;
4962
+ }
4963
+ if (deps.credentialOf(sessionId)?.authorization === void 0) {
4964
+ continue;
4965
+ }
4966
+ const attachment = birth(sessionId);
4967
+ attachments.set(sessionId, attachment);
4968
+ void runLoop(attachment).catch((error) => {
4969
+ deps.log?.(`rc stream ${sessionId}: the attachment loop ended unexpectedly (${error instanceof Error ? error.message : String(error)})`);
4970
+ });
4971
+ }
4972
+ },
4973
+ close: () => {
4974
+ for (const attachment of attachments.values()) {
4975
+ attachment.stop();
4976
+ }
4977
+ attachments.clear();
4978
+ }
4389
4979
  };
4390
4980
  }
4391
4981
 
@@ -4395,31 +4985,31 @@ import { randomUUID as randomUUID3 } from "node:crypto";
4395
4985
  // src/headroom/state.ts
4396
4986
  import { createHash as createHash3 } from "node:crypto";
4397
4987
  import path8 from "node:path";
4398
- import { z as z4 } from "zod";
4988
+ import { z as z5 } from "zod";
4399
4989
  var HEADROOM_ANTHROPIC_UPSTREAM = "https://api.anthropic.com";
4400
4990
  var HEADROOM_STATE_SCHEMA_VERSION = 2;
4401
- var HeadroomStateSchema = z4.strictObject({
4991
+ var HeadroomStateSchema = z5.strictObject({
4402
4992
  /** The supervisor process owning the daemon. Alive means someone is keeping headroom running. */
4403
- supervisorPid: z4.number().int().positive().optional(),
4993
+ supervisorPid: z5.number().int().positive().optional(),
4404
4994
  /** The headroom proxy process itself. Absent while the supervisor is between restarts. */
4405
- headroomPid: z4.number().int().positive().optional(),
4995
+ headroomPid: z5.number().int().positive().optional(),
4406
4996
  /**
4407
4997
  * The unix socket the proxy serves on, inside the owner-only socket directory. Absent until the proxy has passed its readiness check, so "socketPath is set" is itself the ready signal a launcher polls for. The front door reads it on every hop, so a restart (which keeps the generation's path) is followed without anything being relaunched.
4408
4998
  */
4409
- socketPath: z4.string().min(1).optional(),
4999
+ socketPath: z5.string().min(1).optional(),
4410
5000
  /** The `headroom --version` output of the running install. */
4411
- version: z4.string().optional(),
5001
+ version: z5.string().optional(),
4412
5002
  /** Hash of the allowlist the running proxy was started with, so a provider-file change is detected as drift. */
4413
- allowlistHash: z4.string().optional(),
5003
+ allowlistHash: z5.string().optional(),
4414
5004
  /** Hash of the token-saving settings the running proxy was started with, so a settings change is detected as drift. */
4415
- settingsHash: z4.string().optional(),
5005
+ settingsHash: z5.string().optional(),
4416
5006
  /** The install spec the running install was last installed from, so a `source` config change is detected as drift. */
4417
- installedSource: z4.string().optional(),
5007
+ installedSource: z5.string().optional(),
4418
5008
  /** The last fatal error, kept across shutdowns so `headroom status` can explain a daemon that is not running. */
4419
- lastError: z4.string().optional()
5009
+ lastError: z5.string().optional()
4420
5010
  });
4421
- var HeadroomSessionSchema = z4.strictObject({ pid: z4.number().int().positive(), startedAt: z4.number(), supervisorPid: z4.number().int().positive() });
4422
- var HeadroomLockSchema = z4.strictObject({ pid: z4.number().int().positive(), at: z4.number() });
5011
+ var HeadroomSessionSchema = z5.strictObject({ pid: z5.number().int().positive(), startedAt: z5.number(), supervisorPid: z5.number().int().positive() });
5012
+ var HeadroomLockSchema = z5.strictObject({ pid: z5.number().int().positive(), at: z5.number() });
4423
5013
  function parseJson(raw, schema) {
4424
5014
  if (raw === void 0 || raw.trim() === "") {
4425
5015
  return void 0;
@@ -4599,118 +5189,118 @@ import path10 from "node:path";
4599
5189
  import path9 from "node:path";
4600
5190
 
4601
5191
  // src/usage/schema.ts
4602
- import { z as z5 } from "zod";
5192
+ import { z as z6 } from "zod";
4603
5193
  var USAGE_SCHEMA_VERSION = 1;
4604
- var VersionSchema = z5.literal(USAGE_SCHEMA_VERSION);
4605
- var InstantSchema = z5.iso.datetime();
4606
- var TokenCountSchema = z5.number().int().nonnegative();
4607
- var TokenUsageSchema = z5.strictObject({
5194
+ var VersionSchema = z6.literal(USAGE_SCHEMA_VERSION);
5195
+ var InstantSchema = z6.iso.datetime();
5196
+ var TokenCountSchema = z6.number().int().nonnegative();
5197
+ var TokenUsageSchema = z6.strictObject({
4608
5198
  inputTokens: TokenCountSchema.optional(),
4609
5199
  outputTokens: TokenCountSchema.optional(),
4610
5200
  cacheCreationInputTokens: TokenCountSchema.optional(),
4611
5201
  cacheReadInputTokens: TokenCountSchema.optional()
4612
5202
  });
4613
5203
  var LIMIT_KINDS = ["rate-limited", "quota-exhausted"];
4614
- var LimitClassificationSchema = z5.strictObject({
4615
- kind: z5.enum(LIMIT_KINDS),
5204
+ var LimitClassificationSchema = z6.strictObject({
5205
+ kind: z6.enum(LIMIT_KINDS),
4616
5206
  /** Seconds to wait before retrying, from the response's `retry-after`. */
4617
- retryAfterSeconds: z5.number().nonnegative().optional(),
5207
+ retryAfterSeconds: z6.number().nonnegative().optional(),
4618
5208
  /** When the limiting window resets, from the upstream's own reset header. */
4619
5209
  resetAt: InstantSchema.optional(),
4620
5210
  /** The window the upstream named as the binding one (`anthropic-ratelimit-unified-representative-claim`), such as `five_hour` or `seven_day`. */
4621
- window: z5.string().optional(),
5211
+ window: z6.string().optional(),
4622
5212
  /** The signals the classification rests on, each `<name>=<value>` from a response header or the error body's `type` or `code`. Never the error message. */
4623
- evidence: z5.array(z5.string())
5213
+ evidence: z6.array(z6.string())
4624
5214
  });
4625
- var UsageRecordSchema = z5.strictObject({
5215
+ var UsageRecordSchema = z6.strictObject({
4626
5216
  schemaVersion: VersionSchema,
4627
5217
  /** When the front door received the request. */
4628
5218
  at: InstantSchema,
4629
5219
  /** The launching identity, when the launch resolved one. */
4630
- identity: z5.string().optional(),
5220
+ identity: z6.string().optional(),
4631
5221
  /** `anthropic` for an OAuth session's request to Claude Code's own API, otherwise the provider's name. */
4632
- provider: z5.string(),
5222
+ provider: z6.string(),
4633
5223
  /** The launch's session id, generated per launch. */
4634
- sessionId: z5.string().optional(),
5224
+ sessionId: z6.string().optional(),
4635
5225
  /** The project the launch ran in (its git repository root), when the launch sent one: headroom launches do. */
4636
- project: z5.string().optional(),
5226
+ project: z6.string().optional(),
4637
5227
  /** The front-door route that served the request. */
4638
- route: z5.string(),
4639
- method: z5.string(),
5228
+ route: z6.string(),
5229
+ method: z6.string(),
4640
5230
  /** The API path as the upstream sees it (`/v1/messages`), without the provider prefix or a query string. */
4641
- endpoint: z5.string(),
5231
+ endpoint: z6.string(),
4642
5232
  /** The model the response named. */
4643
- model: z5.string().optional(),
4644
- status: z5.number().int(),
5233
+ model: z6.string().optional(),
5234
+ status: z6.number().int(),
4645
5235
  /** Milliseconds from the request's arrival to its response head. */
4646
- latencyMs: z5.number().int().nonnegative(),
5236
+ latencyMs: z6.number().int().nonnegative(),
4647
5237
  /** Milliseconds from the request's arrival to the end of its response body. */
4648
- durationMs: z5.number().int().nonnegative(),
5238
+ durationMs: z6.number().int().nonnegative(),
4649
5239
  /** Whether the client received the whole body. */
4650
- outcome: z5.enum(["completed", "aborted"]),
5240
+ outcome: z6.enum(["completed", "aborted"]),
4651
5241
  usage: TokenUsageSchema.optional(),
4652
5242
  /** The response's quota and rate-limit headers, by name (see `isRateLimitHeader`). */
4653
- rateLimitHeaders: z5.record(z5.string(), z5.string()).optional(),
5243
+ rateLimitHeaders: z6.record(z6.string(), z6.string()).optional(),
4654
5244
  limit: LimitClassificationSchema.optional(),
4655
5245
  /** The upstream's request id (`request-id`), for matching a record to the provider's own logs. */
4656
- requestId: z5.string().optional()
5246
+ requestId: z6.string().optional()
4657
5247
  });
4658
- var QuotaWindowSchema = z5.strictObject({
5248
+ var QuotaWindowSchema = z6.strictObject({
4659
5249
  /** The fraction of the window used, as the upstream reports it (0 to 1). */
4660
- utilization: z5.number().nonnegative().optional(),
5250
+ utilization: z6.number().nonnegative().optional(),
4661
5251
  resetsAt: InstantSchema.optional(),
4662
5252
  /** The window's own status (`allowed`, `allowed_warning`, `rejected`). */
4663
- status: z5.string().optional()
5253
+ status: z6.string().optional()
4664
5254
  });
4665
- var UnifiedRateLimitSchema = z5.strictObject({
5255
+ var UnifiedRateLimitSchema = z6.strictObject({
4666
5256
  /** The overall status: `allowed`, `allowed_warning` or `rejected`. */
4667
- status: z5.string().optional(),
5257
+ status: z6.string().optional(),
4668
5258
  fiveHour: QuotaWindowSchema.optional(),
4669
5259
  sevenDay: QuotaWindowSchema.optional(),
4670
5260
  /** The window that currently binds. */
4671
- representativeClaim: z5.string().optional(),
5261
+ representativeClaim: z6.string().optional(),
4672
5262
  /** When the binding window resets. */
4673
5263
  resetAt: InstantSchema.optional(),
4674
5264
  /** Whether usage beyond the plan (extra usage) is available: `allowed` or `rejected`. */
4675
- overageStatus: z5.string().optional()
5265
+ overageStatus: z6.string().optional()
4676
5266
  });
4677
- var RateLimitStateSchema = z5.strictObject({
5267
+ var RateLimitStateSchema = z6.strictObject({
4678
5268
  observedAt: InstantSchema,
4679
5269
  /** Every quota and rate-limit header on that response, by name. */
4680
- headers: z5.record(z5.string(), z5.string()),
5270
+ headers: z6.record(z6.string(), z6.string()),
4681
5271
  unified: UnifiedRateLimitSchema.optional()
4682
5272
  });
4683
5273
  var LimitEventSchema = LimitClassificationSchema.extend({
4684
5274
  observedAt: InstantSchema,
4685
- status: z5.number().int()
5275
+ status: z6.number().int()
4686
5276
  });
4687
- var ProviderQuotaWindowSchema = z5.strictObject({
5277
+ var ProviderQuotaWindowSchema = z6.strictObject({
4688
5278
  /** What the window measures, in the provider's terms (`tokens`, `tool-calls`). */
4689
- measures: z5.string(),
5279
+ measures: z6.string(),
4690
5280
  /** The window's length in milliseconds, when the provider's unit is one this project knows; absent for an unrecognised unit, with `period` carrying the raw code. */
4691
- periodMs: z5.number().int().positive().optional(),
5281
+ periodMs: z6.number().int().positive().optional(),
4692
5282
  /** The provider's own description of the period when `periodMs` could not be derived, such as `unit 4 x 1`. */
4693
- period: z5.string().optional(),
5283
+ period: z6.string().optional(),
4694
5284
  /** The fraction of the window used (0 to 1). */
4695
- utilization: z5.number().nonnegative(),
5285
+ utilization: z6.number().nonnegative(),
4696
5286
  resetsAt: InstantSchema.optional(),
4697
5287
  /** Absolute counts, when the provider reports them for this window. */
4698
- limit: z5.number().nonnegative().optional(),
4699
- used: z5.number().nonnegative().optional(),
4700
- remaining: z5.number().nonnegative().optional()
5288
+ limit: z6.number().nonnegative().optional(),
5289
+ used: z6.number().nonnegative().optional(),
5290
+ remaining: z6.number().nonnegative().optional()
4701
5291
  });
4702
- var ProviderQuotaSchema = z5.strictObject({
5292
+ var ProviderQuotaSchema = z6.strictObject({
4703
5293
  observedAt: InstantSchema,
4704
5294
  /** Which usage endpoint produced it (`z.ai`). */
4705
- source: z5.string(),
5295
+ source: z6.string(),
4706
5296
  /** The subscription level the endpoint reports, when it does. */
4707
- level: z5.string().optional(),
4708
- windows: z5.array(ProviderQuotaWindowSchema)
5297
+ level: z6.string().optional(),
5298
+ windows: z6.array(ProviderQuotaWindowSchema)
4709
5299
  });
4710
- var ProviderUsageStateSchema = z5.strictObject({
5300
+ var ProviderUsageStateSchema = z6.strictObject({
4711
5301
  lastRequestAt: InstantSchema,
4712
- lastStatus: z5.number().int(),
4713
- lastModel: z5.string().optional(),
5302
+ lastStatus: z6.number().int(),
5303
+ lastModel: z6.string().optional(),
4714
5304
  /** The latest rate-limit state, from the last response that carried any rate-limit header. */
4715
5305
  rateLimit: RateLimitStateSchema.optional(),
4716
5306
  /** The latest classified refusal. It stays after later successes, so a reader compares its `resetAt` (or `observedAt` plus `retryAfterSeconds`) with the current time. */
@@ -4718,31 +5308,31 @@ var ProviderUsageStateSchema = z5.strictObject({
4718
5308
  /** The latest quota the provider's usage endpoint reported, for providers whose quota is not visible in response headers. */
4719
5309
  quota: ProviderQuotaSchema.optional()
4720
5310
  });
4721
- var AccountMetadataSchema = z5.strictObject({
4722
- accountUuid: z5.string().optional(),
4723
- emailAddress: z5.string().optional(),
4724
- displayName: z5.string().optional(),
4725
- organizationUuid: z5.string().optional(),
4726
- organizationName: z5.string().optional(),
4727
- organizationType: z5.string().optional(),
4728
- organizationRole: z5.string().optional(),
4729
- workspaceRole: z5.string().optional(),
4730
- billingType: z5.string().optional(),
4731
- seatTier: z5.string().optional(),
4732
- organizationRateLimitTier: z5.string().optional(),
4733
- userRateLimitTier: z5.string().optional(),
4734
- hasExtraUsageEnabled: z5.boolean().optional(),
4735
- subscriptionCreatedAt: z5.string().optional(),
4736
- accountCreatedAt: z5.string().optional()
5311
+ var AccountMetadataSchema = z6.strictObject({
5312
+ accountUuid: z6.string().optional(),
5313
+ emailAddress: z6.string().optional(),
5314
+ displayName: z6.string().optional(),
5315
+ organizationUuid: z6.string().optional(),
5316
+ organizationName: z6.string().optional(),
5317
+ organizationType: z6.string().optional(),
5318
+ organizationRole: z6.string().optional(),
5319
+ workspaceRole: z6.string().optional(),
5320
+ billingType: z6.string().optional(),
5321
+ seatTier: z6.string().optional(),
5322
+ organizationRateLimitTier: z6.string().optional(),
5323
+ userRateLimitTier: z6.string().optional(),
5324
+ hasExtraUsageEnabled: z6.boolean().optional(),
5325
+ subscriptionCreatedAt: z6.string().optional(),
5326
+ accountCreatedAt: z6.string().optional()
4737
5327
  });
4738
- var UsageSnapshotSchema = z5.strictObject({
5328
+ var UsageSnapshotSchema = z6.strictObject({
4739
5329
  schemaVersion: VersionSchema,
4740
- identity: z5.string(),
5330
+ identity: z6.string(),
4741
5331
  updatedAt: InstantSchema,
4742
5332
  /** The identity's account metadata as of `updatedAt`. */
4743
5333
  account: AccountMetadataSchema.optional(),
4744
5334
  /** Latest state per provider, keyed by provider name (`anthropic` for OAuth sessions). */
4745
- providers: z5.record(z5.string(), ProviderUsageStateSchema)
5335
+ providers: z6.record(z6.string(), ProviderUsageStateSchema)
4746
5336
  });
4747
5337
 
4748
5338
  // src/usage/account.ts
@@ -4946,20 +5536,20 @@ function quotaWarnings(snapshot, provider, nowMs) {
4946
5536
  }
4947
5537
 
4948
5538
  // src/usage/plan.ts
4949
- import { z as z6 } from "zod";
4950
- var PlanClassSchema = z6.union([
4951
- z6.strictObject({
4952
- kind: z6.literal("subscription"),
5539
+ import { z as z7 } from "zod";
5540
+ var PlanClassSchema = z7.union([
5541
+ z7.strictObject({
5542
+ kind: z7.literal("subscription"),
4953
5543
  /** The plan's size as a multiple of the smallest subscription tier (Max 5x is 5, Max 20x is 20). */
4954
- capacity: z6.number().positive(),
5544
+ capacity: z7.number().positive(),
4955
5545
  /** False when the tier string named no multiplier and no tier this project knows, so `capacity` is the neutral 1 rather than a reading. */
4956
- recognised: z6.boolean(),
5546
+ recognised: z7.boolean(),
4957
5547
  /** The tier string the classification rests on, when the account had one. */
4958
- tier: z6.string().optional()
5548
+ tier: z7.string().optional()
4959
5549
  }),
4960
- z6.strictObject({
4961
- kind: z6.literal("pay-per-use"),
4962
- tier: z6.string().optional()
5550
+ z7.strictObject({
5551
+ kind: z7.literal("pay-per-use"),
5552
+ tier: z7.string().optional()
4963
5553
  })
4964
5554
  ]);
4965
5555
  var TIER_MULTIPLIER = /(?:^|_)(\d+)x$/;
@@ -5028,17 +5618,17 @@ function parseUnifiedRateLimit(headers) {
5028
5618
  }
5029
5619
 
5030
5620
  // src/usage/scan.ts
5031
- import { z as z7 } from "zod";
5032
- var AnthropicUsageSchema = z7.looseObject({
5033
- input_tokens: z7.number().int().nonnegative().nullish(),
5034
- output_tokens: z7.number().int().nonnegative().nullish(),
5035
- cache_creation_input_tokens: z7.number().int().nonnegative().nullish(),
5036
- cache_read_input_tokens: z7.number().int().nonnegative().nullish()
5621
+ import { z as z8 } from "zod";
5622
+ var AnthropicUsageSchema = z8.looseObject({
5623
+ input_tokens: z8.number().int().nonnegative().nullish(),
5624
+ output_tokens: z8.number().int().nonnegative().nullish(),
5625
+ cache_creation_input_tokens: z8.number().int().nonnegative().nullish(),
5626
+ cache_read_input_tokens: z8.number().int().nonnegative().nullish()
5037
5627
  });
5038
- var ErrorObjectSchema = z7.looseObject({
5039
- type: z7.string().optional(),
5040
- code: z7.union([z7.string(), z7.number()]).optional(),
5041
- message: z7.string().optional()
5628
+ var ErrorObjectSchema = z8.looseObject({
5629
+ type: z8.string().optional(),
5630
+ code: z8.union([z8.string(), z8.number()]).optional(),
5631
+ message: z8.string().optional()
5042
5632
  });
5043
5633
 
5044
5634
  // src/usage/middleware.ts
@@ -5198,7 +5788,7 @@ function rankPool(input) {
5198
5788
 
5199
5789
  // src/usage/poolPick.ts
5200
5790
  import path11 from "node:path";
5201
- import { z as z8 } from "zod";
5791
+ import { z as z9 } from "zod";
5202
5792
 
5203
5793
  // src/usage/store.ts
5204
5794
  var DAY_MS2 = 864e5;
@@ -5207,10 +5797,10 @@ var USAGE_RETENTION_MS = DAYS_IN_WEEK * DAY_MS2;
5207
5797
 
5208
5798
  // src/usage/poolPick.ts
5209
5799
  var SCHEMA_VERSION = 1;
5210
- var PickEntrySchema = z8.strictObject({ identity: z8.string().min(1), at: z8.iso.datetime() });
5211
- var PicksFileSchema = z8.strictObject({
5212
- schemaVersion: z8.literal(SCHEMA_VERSION),
5213
- picks: z8.record(z8.string().min(1), PickEntrySchema)
5800
+ var PickEntrySchema = z9.strictObject({ identity: z9.string().min(1), at: z9.iso.datetime() });
5801
+ var PicksFileSchema = z9.strictObject({
5802
+ schemaVersion: z9.literal(SCHEMA_VERSION),
5803
+ picks: z9.record(z9.string().min(1), PickEntrySchema)
5214
5804
  });
5215
5805
  function errorMessage(error) {
5216
5806
  return error instanceof Error ? error.message : String(error);
@@ -5410,33 +6000,33 @@ function collectPoolPick(params) {
5410
6000
  }
5411
6001
 
5412
6002
  // src/usage/pickReportSchema.ts
5413
- import { z as z9 } from "zod";
5414
- var PoolPickCandidateViewSchema = z9.strictObject({
5415
- identity: z9.string(),
6003
+ import { z as z10 } from "zod";
6004
+ var PoolPickCandidateViewSchema = z10.strictObject({
6005
+ identity: z10.string(),
5416
6006
  /** In pick order: `scored` has usable quota data, `unknown` has none, `pay-per-use` bills by use, `ineligible` is refused right now. */
5417
- class: z9.enum(["scored", "unknown", "pay-per-use", "ineligible"]),
6007
+ class: z10.enum(["scored", "unknown", "pay-per-use", "ineligible"]),
5418
6008
  /** Plan-size-weighted remaining quota per hour until its reset; present for `scored` only. */
5419
- score: z9.number().optional(),
6009
+ score: z10.number().optional(),
5420
6010
  /** False when the five-hour window would run dry, at the observed pace, before it resets. */
5421
- feasible: z9.boolean(),
6011
+ feasible: z10.boolean(),
5422
6012
  /** When an `ineligible` member can be used again, as an ISO instant. */
5423
- blockedUntil: z9.iso.datetime().optional(),
6013
+ blockedUntil: z10.iso.datetime().optional(),
5424
6014
  plan: PlanClassSchema,
5425
6015
  /** The facts behind the class and score, in the order they matter. */
5426
- reasons: z9.array(z9.string())
6016
+ reasons: z10.array(z10.string())
5427
6017
  });
5428
- var PoolPickReportSchema = z9.strictObject({
5429
- pool: z9.string(),
5430
- directory: z9.string(),
6018
+ var PoolPickReportSchema = z10.strictObject({
6019
+ pool: z10.string(),
6020
+ directory: z10.string(),
5431
6021
  /** The member a launch would run as, absent when every member is refused. */
5432
- pick: z9.string().optional(),
5433
- candidates: z9.array(PoolPickCandidateViewSchema),
6022
+ pick: z10.string().optional(),
6023
+ candidates: z10.array(PoolPickCandidateViewSchema),
5434
6024
  /** Members that name an identity that does not exist; a launch skips them. */
5435
- missing: z9.array(z9.string()),
6025
+ missing: z10.array(z10.string()),
5436
6026
  /** When nothing can be picked, the soonest any member returns. */
5437
- earliestReturn: z9.strictObject({ identity: z9.string(), at: z9.iso.datetime() }).optional(),
6027
+ earliestReturn: z10.strictObject({ identity: z10.string(), at: z10.iso.datetime() }).optional(),
5438
6028
  /** Why the last-pick record could not be read, when that is the case. */
5439
- stickyProblem: z9.string().optional()
6029
+ stickyProblem: z10.string().optional()
5440
6030
  });
5441
6031
 
5442
6032
  // src/headroom/ensure.ts
@@ -5599,10 +6189,10 @@ function pruneStaleHeadroomSockets(fs12, paths, isRunning) {
5599
6189
  }
5600
6190
 
5601
6191
  // src/headroom/source.ts
5602
- import { z as z10 } from "zod";
6192
+ import { z as z11 } from "zod";
5603
6193
  var GIT_SPEC_RE = /git\+[^@\s]+(?:@([^\s#]+))?/;
5604
6194
  var COMMIT_SHA_RE = /^[0-9a-f]{40}$/i;
5605
- var DirectUrlSchema = z10.object({ vcs_info: z10.object({ commit_id: z10.string() }) });
6195
+ var DirectUrlSchema = z11.object({ vcs_info: z11.object({ commit_id: z11.string() }) });
5606
6196
  function isMovingGitSource(spec) {
5607
6197
  const match = GIT_SPEC_RE.exec(spec);
5608
6198
  if (match === null) {
@@ -5915,9 +6505,9 @@ function evaluateAmbientCredentialGuard(params) {
5915
6505
 
5916
6506
  // src/paths.ts
5917
6507
  import fs3 from "node:fs";
5918
- import os from "node:os";
6508
+ import os2 from "node:os";
5919
6509
  import path14 from "node:path";
5920
- function resolveAgentShimHome(env = process.env, home = os.homedir(), isDirectory = isExistingDirectory) {
6510
+ function resolveAgentShimHome(env = process.env, home = os2.homedir(), isDirectory = isExistingDirectory) {
5921
6511
  const fromEnv = env.AGENT_SHIM_HOME;
5922
6512
  if (fromEnv !== void 0 && fromEnv !== "") {
5923
6513
  return fromEnv;
@@ -5934,7 +6524,7 @@ function resolveClaudeHome() {
5934
6524
  if (fromEnv !== void 0 && fromEnv !== "") {
5935
6525
  return fromEnv;
5936
6526
  }
5937
- return path14.join(os.homedir(), ".claude");
6527
+ return path14.join(os2.homedir(), ".claude");
5938
6528
  }
5939
6529
  function buildLayoutPaths(root) {
5940
6530
  return {
@@ -6316,11 +6906,11 @@ import fs6 from "node:fs";
6316
6906
  import path17 from "node:path";
6317
6907
 
6318
6908
  // src/credentialCache.ts
6319
- import { z as z11 } from "zod";
6909
+ import { z as z12 } from "zod";
6320
6910
  var MS_PER_UNIT = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
6321
- var CachedCredentialSchema = z11.strictObject({
6322
- token: z11.string().min(1),
6323
- fetchedAt: z11.number().int().nonnegative(),
6911
+ var CachedCredentialSchema = z12.strictObject({
6912
+ token: z12.string().min(1),
6913
+ fetchedAt: z12.number().int().nonnegative(),
6324
6914
  source: CredentialSourceSchema
6325
6915
  });
6326
6916
  function effectiveStore(cache, platform) {
@@ -6586,14 +7176,14 @@ function providerExists(paths, name) {
6586
7176
  var LEGACY_PROVIDER_FIELDS = ["tokenEnv", "tokenCommand", "authScheme"];
6587
7177
  var LEGACY_ENV_CREDENTIAL_KEYS = Object.values(CREDENTIAL_TARGET_VARS);
6588
7178
  var LEGACY_LITERAL_PLACEHOLDER = "<the value of env.ANTHROPIC_AUTH_TOKEN>";
6589
- function isRecord3(value) {
7179
+ function isRecord4(value) {
6590
7180
  return typeof value === "object" && value !== null && !Array.isArray(value);
6591
7181
  }
6592
7182
  function legacyProviderConversion(raw) {
6593
- if (!isRecord3(raw) || "credential" in raw) {
7183
+ if (!isRecord4(raw) || "credential" in raw) {
6594
7184
  return void 0;
6595
7185
  }
6596
- const env = isRecord3(raw.env) ? raw.env : void 0;
7186
+ const env = isRecord4(raw.env) ? raw.env : void 0;
6597
7187
  const legacyEnvKeys = env === void 0 ? [] : Object.keys(env).filter((key) => LEGACY_ENV_CREDENTIAL_KEYS.includes(key));
6598
7188
  const fields = [...LEGACY_PROVIDER_FIELDS.filter((field) => field in raw), ...legacyEnvKeys.map((key) => `env.${key}`)];
6599
7189
  if (fields.length === 0) {
@@ -6906,7 +7496,7 @@ function removeDirectoryRule(paths, rulePath) {
6906
7496
  }
6907
7497
 
6908
7498
  // src/checkReport.ts
6909
- import os4 from "node:os";
7499
+ import os5 from "node:os";
6910
7500
 
6911
7501
  // src/launcher/cascade.ts
6912
7502
  import path19 from "node:path";
@@ -7079,15 +7669,15 @@ function realCredentialCacheEnv(paths) {
7079
7669
  import { spawn, spawnSync as spawnSync2 } from "node:child_process";
7080
7670
  import { createHash as createHash5 } from "node:crypto";
7081
7671
  import fs8 from "node:fs";
7082
- import os3 from "node:os";
7672
+ import os4 from "node:os";
7083
7673
  import path22 from "node:path";
7084
7674
  import { isSea } from "node:sea";
7085
7675
 
7086
7676
  // src/versionDiscovery.ts
7087
- import os2 from "node:os";
7677
+ import os3 from "node:os";
7088
7678
  import path21 from "node:path";
7089
7679
  function defaultVersionsDir() {
7090
- return path21.join(os2.homedir(), ".local", "share", "claude", "versions");
7680
+ return path21.join(os3.homedir(), ".local", "share", "claude", "versions");
7091
7681
  }
7092
7682
  var NUMERIC_DOTTED_VERSION_RE = /^\d+(?:\.\d+)*$/;
7093
7683
  function isNumericDottedVersion(name) {
@@ -7303,7 +7893,7 @@ var realRunPort = {
7303
7893
  }
7304
7894
  };
7305
7895
  function expandHome2(filePath) {
7306
- return filePath.startsWith("~/") ? path22.join(os3.homedir(), filePath.slice(1)) : filePath;
7896
+ return filePath.startsWith("~/") ? path22.join(os4.homedir(), filePath.slice(1)) : filePath;
7307
7897
  }
7308
7898
  var MACOS_GUI_SESSION_MANAGER = "Aqua";
7309
7899
  function hasDesktopSession() {
@@ -7557,7 +8147,7 @@ function realResolveClaudeBinary(isOwnBinary) {
7557
8147
 
7558
8148
  // src/checkReport.ts
7559
8149
  import path23 from "node:path";
7560
- import { z as z12 } from "zod";
8150
+ import { z as z13 } from "zod";
7561
8151
 
7562
8152
  // src/cli/bool.ts
7563
8153
  function parseBoolWord(input) {
@@ -7672,9 +8262,9 @@ function lookupKeychainService(run, farmRoot) {
7672
8262
  note: serviceName === void 0 ? `A Keychain entry was found for account "${farmRoot}" but its service name could not be parsed from security's output.` : `Keychain service name for this identity: "${serviceName}".`
7673
8263
  };
7674
8264
  }
7675
- var SettingsSecretsShapeSchema = z12.looseObject({
7676
- env: z12.record(z12.string(), z12.unknown()).optional(),
7677
- hooks: z12.record(z12.string(), z12.array(z12.unknown())).optional()
8265
+ var SettingsSecretsShapeSchema = z13.looseObject({
8266
+ env: z13.record(z13.string(), z13.unknown()).optional(),
8267
+ hooks: z13.record(z13.string(), z13.array(z13.unknown())).optional()
7678
8268
  });
7679
8269
  function hookCommandCountOf(group) {
7680
8270
  if (typeof group !== "object" || group === null) {
@@ -7888,7 +8478,7 @@ function loadCheckProvider(paths, name) {
7888
8478
  }
7889
8479
  function collectCheckReport(params) {
7890
8480
  const { paths, cwd } = params;
7891
- const home = os4.homedir();
8481
+ const home = os5.homedir();
7892
8482
  const claudeHome = resolveClaudeHome();
7893
8483
  const read = cosmiconfigReader();
7894
8484
  const classification = loadClassification(paths);
@@ -7968,11 +8558,11 @@ function collectCheckReport(params) {
7968
8558
  // src/claudeShim.ts
7969
8559
  import fs9 from "node:fs";
7970
8560
  import path24 from "node:path";
7971
- import { z as z13 } from "zod";
7972
- var ClaudeShimStateSchema = z13.strictObject({
7973
- targetPath: z13.string().min(1),
7974
- method: z13.enum(["hardlink", "copy"]),
7975
- installedAtMs: z13.number()
8561
+ import { z as z14 } from "zod";
8562
+ var ClaudeShimStateSchema = z14.strictObject({
8563
+ targetPath: z14.string().min(1),
8564
+ method: z14.enum(["hardlink", "copy"]),
8565
+ installedAtMs: z14.number()
7976
8566
  });
7977
8567
  function commandFilename(ownExecutablePath, commandName) {
7978
8568
  return ownExecutablePath.toLowerCase().endsWith(".exe") ? `${commandName}.exe` : commandName;
@@ -8531,9 +9121,11 @@ var BOOLEAN_FLAGS = [
8531
9121
  ];
8532
9122
  var LAUNCHER_FLAG_NAMES = [
8533
9123
  ...VALUED_FLAGS,
9124
+ "--native",
8534
9125
  "--no-provider",
8535
9126
  ...BOOLEAN_FLAGS.flatMap(({ flag }) => [flag, `--no-${flag.slice(2)}`])
8536
9127
  ];
9128
+ var NATIVE_FLAG = "--native";
8537
9129
  var TERMINATOR2 = "--";
8538
9130
  function matchValuedFlag(token) {
8539
9131
  for (const flag of VALUED_FLAGS) {
@@ -8561,6 +9153,7 @@ function parseLauncherArgv(argv) {
8561
9153
  const first = argv[0];
8562
9154
  const positionalIdentity = first !== void 0 && first.startsWith("@") && first.length > 1 ? first.slice(1) : void 0;
8563
9155
  const remaining = positionalIdentity === void 0 ? argv : argv.slice(1);
9156
+ let native = false;
8564
9157
  let flagIdentity;
8565
9158
  let configProfile;
8566
9159
  let provider;
@@ -8582,6 +9175,10 @@ function parseLauncherArgv(argv) {
8582
9175
  provider = false;
8583
9176
  continue;
8584
9177
  }
9178
+ if (token === NATIVE_FLAG) {
9179
+ native = true;
9180
+ continue;
9181
+ }
8585
9182
  const booleanFlag = matchBooleanFlag(token);
8586
9183
  if (booleanFlag !== void 0) {
8587
9184
  booleans[booleanFlag.key] = booleanFlag.value;
@@ -8635,6 +9232,7 @@ function parseLauncherArgv(argv) {
8635
9232
  ...identity === void 0 ? {} : { identity },
8636
9233
  ...configProfile === void 0 ? {} : { configProfile },
8637
9234
  ...provider === void 0 ? {} : { provider },
9235
+ ...native ? { native: true } : {},
8638
9236
  ...booleans,
8639
9237
  categoryFlags,
8640
9238
  shareFlags,
@@ -9161,7 +9759,7 @@ function prepareLaunch(params) {
9161
9759
  }
9162
9760
 
9163
9761
  // src/launchWiring.ts
9164
- import os5 from "node:os";
9762
+ import os6 from "node:os";
9165
9763
  import { randomUUID as randomUUID5 } from "node:crypto";
9166
9764
 
9167
9765
  // src/frontdoor/realFrontDoorPort.ts
@@ -9310,7 +9908,7 @@ function realFrontDoorPort(paths, spawnDaemon) {
9310
9908
 
9311
9909
  // src/launchWiring.ts
9312
9910
  function buildFarmRuntime(paths, cwd) {
9313
- const home = os5.homedir();
9911
+ const home = os6.homedir();
9314
9912
  const read = cosmiconfigReader();
9315
9913
  const classification = loadClassification(paths);
9316
9914
  const loaded = loadCascadeInput({ paths, home, cwd, read });
@@ -9344,6 +9942,9 @@ function buildFarmRuntime(paths, cwd) {
9344
9942
  ...loaded.globalConfig?.pools === void 0 ? {} : { pools: loaded.globalConfig.pools }
9345
9943
  };
9346
9944
  }
9945
+ function realClaudeBinaryResolver(paths) {
9946
+ return realResolveClaudeBinary(resolveOwnBinaryCheck(paths, realContentSourcePath()));
9947
+ }
9347
9948
  function realPrepareLaunchParams(paths, options) {
9348
9949
  const { farm } = options;
9349
9950
  return {
@@ -9351,7 +9952,7 @@ function realPrepareLaunchParams(paths, options) {
9351
9952
  fs: realFsPort,
9352
9953
  proc: options.proc,
9353
9954
  log: options.log,
9354
- resolveClaudeBinary: realResolveClaudeBinary(resolveOwnBinaryCheck(paths, realContentSourcePath())),
9955
+ resolveClaudeBinary: realClaudeBinaryResolver(paths),
9355
9956
  farm: farm.runtime,
9356
9957
  headroom: realHeadroomPort(paths, { spawnDaemon: options.spawnDaemon, cwd: farm.runtime.cwd }),
9357
9958
  frontdoor: realFrontDoorPort(paths, options.spawnDaemon),
@@ -9441,11 +10042,16 @@ export {
9441
10042
  ProviderNotFoundError,
9442
10043
  ProviderSchema,
9443
10044
  RC_IDLE_EXPIRY_MS,
10045
+ RC_ORPC_PATH_PREFIX,
9444
10046
  RC_PENDING_DEADLINE_MS,
9445
10047
  RC_PERMISSION_MODES,
9446
10048
  RC_REQUEST_PARSE_CAP_BYTES,
9447
10049
  RC_SESSIONS_PATH_PREFIX,
10050
+ RC_STREAM_BACKOFF_MS,
9448
10051
  ROUTED_PATH_PREFIX,
10052
+ RcPermissionModeSchema,
10053
+ RcStreamEnvelopeSchema,
10054
+ RcStreamEventSchema,
9449
10055
  UsageError,
9450
10056
  UsageSnapshotError,
9451
10057
  UsageSnapshotSchema,
@@ -9471,8 +10077,13 @@ export {
9471
10077
  collectPoolPick,
9472
10078
  createLeafCache,
9473
10079
  createProfile,
10080
+ createRcApiNodeHandler,
10081
+ createRcApiRouter,
9474
10082
  createRcControlHandler,
10083
+ createRcEventFanout,
9475
10084
  createRcSessionTracker,
10085
+ createRcStreamHub,
10086
+ createSseParser,
9476
10087
  describeProviderEndpoint,
9477
10088
  detectAmbientCredential,
9478
10089
  effectiveWindow,
@@ -9485,6 +10096,7 @@ export {
9485
10096
  formatCheckReport,
9486
10097
  formatDoctorReport,
9487
10098
  forwardableHeaders,
10099
+ frontDoorRcApiClient,
9488
10100
  frontDoorRcControl,
9489
10101
  generateCa,
9490
10102
  identityExists,
@@ -9502,6 +10114,7 @@ export {
9502
10114
  mintLeaf,
9503
10115
  observingRoutedRoute,
9504
10116
  parseConnectTarget,
10117
+ parseRcStreamEnvelope,
9505
10118
  planOf,
9506
10119
  prepareClaudeLaunch,
9507
10120
  prepareLaunch,
@@ -9523,6 +10136,7 @@ export {
9523
10136
  realConnectEffects,
9524
10137
  realRcControlTransport,
9525
10138
  realRcEventDial,
10139
+ realRcStreamDial,
9526
10140
  recoverFarm,
9527
10141
  recoveryDiagnostics,
9528
10142
  removeDirectoryRule,