agent-shim 8.5.6 → 8.7.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
@@ -3886,6 +3886,19 @@ function buildRcControlResponsePayload(requestId, decision) {
3886
3886
  const result = decision.approve ? { behavior: "allow" } : { behavior: "deny", message: decision.message ?? "" };
3887
3887
  return { type: "control_response", response: { subtype: "success", request_id: requestId, response: result } };
3888
3888
  }
3889
+ var RC_PERMISSION_MODES = ["default", "acceptEdits", "bypassPermissions", "plan", "dontAsk", "auto"];
3890
+ function isRcPermissionMode(value) {
3891
+ return RC_PERMISSION_MODES.some((mode) => mode === value);
3892
+ }
3893
+ function buildRcInterruptPayload(requestId) {
3894
+ return { type: "control_request", request_id: requestId, request: { subtype: "interrupt" } };
3895
+ }
3896
+ function buildRcSetModelPayload(requestId, model) {
3897
+ return { type: "control_request", request_id: requestId, request: { subtype: "set_model", model } };
3898
+ }
3899
+ function buildRcSetPermissionModePayload(requestId, mode) {
3900
+ return { type: "control_request", request_id: requestId, request: { subtype: "set_permission_mode", mode } };
3901
+ }
3889
3902
  async function answerRcControlRequest(deps, sessionId, requestId, decision) {
3890
3903
  const credential = deps.credentialOf(sessionId);
3891
3904
  if (credential === void 0) {
@@ -3910,9 +3923,64 @@ async function answerRcControlRequest(deps, sessionId, requestId, decision) {
3910
3923
  }
3911
3924
  return result;
3912
3925
  }
3926
+ async function interruptRcSession(deps, sessionId) {
3927
+ const credential = deps.credentialOf(sessionId);
3928
+ if (credential === void 0) {
3929
+ 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)` };
3930
+ }
3931
+ if (credential.authorization === void 0) {
3932
+ return noOAuthCredentialResult(sessionId);
3933
+ }
3934
+ const body = JSON.stringify(buildRcEventWriteBody(buildRcInterruptPayload(deps.newUuid())));
3935
+ let answer;
3936
+ try {
3937
+ answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3938
+ } catch (error) {
3939
+ return { ok: false, message: `the door could not reach the API host to interrupt session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
3940
+ }
3941
+ return rcEventWriteResultFromAnswer(answer);
3942
+ }
3943
+ async function setRcSessionModel(deps, sessionId, model) {
3944
+ if (model === "") {
3945
+ 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" };
3946
+ }
3947
+ const credential = deps.credentialOf(sessionId);
3948
+ if (credential === void 0) {
3949
+ 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)` };
3950
+ }
3951
+ if (credential.authorization === void 0) {
3952
+ return noOAuthCredentialResult(sessionId);
3953
+ }
3954
+ const body = JSON.stringify(buildRcEventWriteBody(buildRcSetModelPayload(deps.newUuid(), model)));
3955
+ let answer;
3956
+ try {
3957
+ answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3958
+ } catch (error) {
3959
+ 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)}` };
3960
+ }
3961
+ return rcEventWriteResultFromAnswer(answer);
3962
+ }
3963
+ async function setRcSessionPermissionMode(deps, sessionId, mode) {
3964
+ const credential = deps.credentialOf(sessionId);
3965
+ if (credential === void 0) {
3966
+ 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)` };
3967
+ }
3968
+ if (credential.authorization === void 0) {
3969
+ return noOAuthCredentialResult(sessionId);
3970
+ }
3971
+ const body = JSON.stringify(buildRcEventWriteBody(buildRcSetPermissionModePayload(deps.newUuid(), mode)));
3972
+ let answer;
3973
+ try {
3974
+ answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
3975
+ } catch (error) {
3976
+ 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)}` };
3977
+ }
3978
+ return rcEventWriteResultFromAnswer(answer);
3979
+ }
3913
3980
 
3914
3981
  // src/frontdoor/rcControl.ts
3915
3982
  import * as https2 from "node:https";
3983
+ var RC_PERMISSION_MODE_LIST = RC_PERMISSION_MODES.join(", ");
3916
3984
  var CONTROL_PATH_PREFIX = "/__agent-shim/rc";
3917
3985
  var CONTROL_BODY_CAP_BYTES = 10485760;
3918
3986
  function answerJson(response, status, body) {
@@ -3947,6 +4015,9 @@ function createRcControlHandler(deps) {
3947
4015
  const pendingPath = `${CONTROL_PATH_PREFIX}/pending`;
3948
4016
  const injectPath = `${CONTROL_PATH_PREFIX}/inject`;
3949
4017
  const answerPath = `${CONTROL_PATH_PREFIX}/answer`;
4018
+ const interruptPath = `${CONTROL_PATH_PREFIX}/interrupt`;
4019
+ const setModelPath = `${CONTROL_PATH_PREFIX}/set-model`;
4020
+ const setPermissionModePath = `${CONTROL_PATH_PREFIX}/set-permission-mode`;
3950
4021
  return (request4, response) => {
3951
4022
  const presented = request4.headers.authorization;
3952
4023
  const bearerPrefix = "bearer ".length;
@@ -4031,6 +4102,78 @@ function createRcControlHandler(deps) {
4031
4102
  answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
4032
4103
  return;
4033
4104
  }
4105
+ if (url.pathname === interruptPath) {
4106
+ if (request4.method !== "POST") {
4107
+ answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the interrupt operation is a POST" });
4108
+ return;
4109
+ }
4110
+ let parsed;
4111
+ try {
4112
+ parsed = JSON.parse(await readBody(request4));
4113
+ } catch {
4114
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body is not readable JSON within the control body cap" });
4115
+ return;
4116
+ }
4117
+ if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || typeof parsed.session !== "string" || parsed.session === "") {
4118
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body must be JSON naming a non-empty session id" });
4119
+ return;
4120
+ }
4121
+ const result = await deps.interrupt(parsed.session);
4122
+ if (result.ok) {
4123
+ answerJson(response, HTTP_STATUS.ok, { session: parsed.session, sequenceNums: result.sequenceNums });
4124
+ return;
4125
+ }
4126
+ answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
4127
+ return;
4128
+ }
4129
+ if (url.pathname === setModelPath) {
4130
+ if (request4.method !== "POST") {
4131
+ answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-model operation is a POST" });
4132
+ return;
4133
+ }
4134
+ let parsed;
4135
+ try {
4136
+ parsed = JSON.parse(await readBody(request4));
4137
+ } catch {
4138
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body is not readable JSON within the control body cap" });
4139
+ return;
4140
+ }
4141
+ if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("model" in parsed) || typeof parsed.session !== "string" || typeof parsed.model !== "string" || parsed.session === "" || parsed.model === "") {
4142
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body must be JSON naming a non-empty session id and model id" });
4143
+ return;
4144
+ }
4145
+ const result = await deps.setModel(parsed.session, parsed.model);
4146
+ if (result.ok) {
4147
+ answerJson(response, HTTP_STATUS.ok, { session: parsed.session, model: parsed.model, sequenceNums: result.sequenceNums });
4148
+ return;
4149
+ }
4150
+ answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
4151
+ return;
4152
+ }
4153
+ if (url.pathname === setPermissionModePath) {
4154
+ if (request4.method !== "POST") {
4155
+ answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-permission-mode operation is a POST" });
4156
+ return;
4157
+ }
4158
+ let parsed;
4159
+ try {
4160
+ parsed = JSON.parse(await readBody(request4));
4161
+ } catch {
4162
+ answerJson(response, HTTP_STATUS.badRequest, { error: "the set-permission-mode body is not readable JSON within the control body cap" });
4163
+ return;
4164
+ }
4165
+ if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("mode" in parsed) || typeof parsed.session !== "string" || parsed.session === "" || !isRcPermissionMode(parsed.mode)) {
4166
+ 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})` });
4167
+ return;
4168
+ }
4169
+ const result = await deps.setPermissionMode(parsed.session, parsed.mode);
4170
+ if (result.ok) {
4171
+ answerJson(response, HTTP_STATUS.ok, { session: parsed.session, mode: parsed.mode, sequenceNums: result.sequenceNums });
4172
+ return;
4173
+ }
4174
+ answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
4175
+ return;
4176
+ }
4034
4177
  answerJson(response, HTTP_STATUS.notFound, { error: `no such control route: ${url.pathname}` });
4035
4178
  };
4036
4179
  handle().catch((error) => {
@@ -4245,7 +4388,10 @@ function frontDoorRcControl(transport, token) {
4245
4388
  `${CONTROL_PATH_PREFIX}/answer`,
4246
4389
  JSON.stringify({ session: sessionId, request: requestId, approve: decision.approve, ...decision.approve || decision.message === void 0 ? {} : { text: decision.message } }),
4247
4390
  (status, message) => `answering control request ${requestId} on ${sessionId} failed (HTTP ${String(status)}): ${message}`
4248
- )
4391
+ ),
4392
+ interruptSession: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/interrupt`, JSON.stringify({ session: sessionId }), (status, message) => `interrupting ${sessionId} failed (HTTP ${String(status)}): ${message}`),
4393
+ 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}`),
4394
+ 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}`)
4249
4395
  };
4250
4396
  }
4251
4397
 
@@ -5688,6 +5834,56 @@ import path16 from "node:path";
5688
5834
 
5689
5835
  // src/credentialCache.ts
5690
5836
  import { z as z7 } from "zod";
5837
+
5838
+ // src/usage/preflight.ts
5839
+ var MS_PER_SECOND2 = 1e3;
5840
+ var SECONDS_PER_MINUTE = 60;
5841
+ var MINUTES_PER_HOUR = 60;
5842
+ var HOURS_PER_DAY = 24;
5843
+ var WARNING_STATUSES = /* @__PURE__ */ new Set(["allowed_warning", "rejected"]);
5844
+ function formatAge(ms) {
5845
+ const minutes = Math.floor(ms / (MS_PER_SECOND2 * SECONDS_PER_MINUTE));
5846
+ if (minutes < MINUTES_PER_HOUR) {
5847
+ return `${String(Math.max(minutes, 0))}m`;
5848
+ }
5849
+ const hours = Math.floor(minutes / MINUTES_PER_HOUR);
5850
+ return hours < HOURS_PER_DAY ? `${String(hours)}h` : `${String(Math.floor(hours / HOURS_PER_DAY))}d`;
5851
+ }
5852
+ function effectiveWindow(window, nowMs) {
5853
+ const resetsAtMs = window.resetsAt === void 0 ? void 0 : Date.parse(window.resetsAt);
5854
+ if (resetsAtMs !== void 0 && resetsAtMs <= nowMs) {
5855
+ return { reset: true, utilization: 0 };
5856
+ }
5857
+ return {
5858
+ reset: false,
5859
+ ...window.utilization === void 0 ? {} : { utilization: window.utilization },
5860
+ ...resetsAtMs === void 0 ? {} : { resetsAtMs },
5861
+ ...window.status === void 0 ? {} : { status: window.status }
5862
+ };
5863
+ }
5864
+ function quotaWarnings(snapshot, provider, nowMs) {
5865
+ const rateLimit = snapshot?.providers[provider]?.rateLimit;
5866
+ const unified = rateLimit?.unified;
5867
+ if (snapshot === void 0 || rateLimit === void 0 || unified === void 0) {
5868
+ return [];
5869
+ }
5870
+ const age = formatAge(nowMs - Date.parse(rateLimit.observedAt));
5871
+ const windows = [
5872
+ { name: "five-hour", window: unified.fiveHour },
5873
+ { name: "seven-day", window: unified.sevenDay }
5874
+ ];
5875
+ return windows.flatMap(({ name, window }) => {
5876
+ const effective = window === void 0 ? void 0 : effectiveWindow(window, nowMs);
5877
+ if (window === void 0 || effective?.status === void 0 || !WARNING_STATUSES.has(effective.status)) {
5878
+ return [];
5879
+ }
5880
+ const reset = window.resetsAt === void 0 ? "" : `, resets ${window.resetsAt}`;
5881
+ const verdict = effective.status === "rejected" ? "exhausted" : "nearly used";
5882
+ return [`agent-shim: identity ${snapshot.identity}: the ${name} quota is ${verdict}${reset} (last seen ${age} ago)`];
5883
+ });
5884
+ }
5885
+
5886
+ // src/credentialCache.ts
5691
5887
  var MS_PER_UNIT = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
5692
5888
  var CachedCredentialSchema = z7.strictObject({
5693
5889
  token: z7.string().min(1),
@@ -5714,6 +5910,45 @@ function isFresh(entry, cache, now) {
5714
5910
  }
5715
5911
  return limit === void 0 || now - entry.fetchedAt < limit;
5716
5912
  }
5913
+ function cacheAgeMs(entry, now) {
5914
+ return Math.max(0, now - entry.fetchedAt);
5915
+ }
5916
+ function describeCachedCredential(params) {
5917
+ const store = effectiveStore(params.cache, params.platform);
5918
+ let entry;
5919
+ try {
5920
+ entry = params.port.read(store, params.owner);
5921
+ } catch (error) {
5922
+ return { store, status: "unreadable", reason: error instanceof Error ? error.message : String(error) };
5923
+ }
5924
+ if (entry === void 0) {
5925
+ return { store, status: "empty" };
5926
+ }
5927
+ const limit = ttlMs(params.cache);
5928
+ const ageMs = cacheAgeMs(entry, params.nowMs);
5929
+ return {
5930
+ store,
5931
+ status: isFresh(entry, params.cache, params.nowMs) ? "fresh" : "expired",
5932
+ ageMs,
5933
+ ...limit === void 0 ? {} : { expiresInMs: limit - ageMs }
5934
+ };
5935
+ }
5936
+ function formatCachedCredentialState(state) {
5937
+ if (state.status === "empty") {
5938
+ return `no cached entry in the ${state.store} store yet`;
5939
+ }
5940
+ if (state.status === "unreadable") {
5941
+ return `the ${state.store} store cannot be read here: ${state.reason}`;
5942
+ }
5943
+ if (state.status === "expired" && (state.expiresInMs === void 0 || state.expiresInMs > 0)) {
5944
+ return "cached with a timestamp in the future, treated as expired";
5945
+ }
5946
+ const age = `cached ${formatAge(state.ageMs)} ago`;
5947
+ if (state.expiresInMs === void 0) {
5948
+ return `${age}, no expiry`;
5949
+ }
5950
+ return state.status === "fresh" ? `${age}, expires in ${formatAge(state.expiresInMs)}` : `${age}, expired ${formatAge(-state.expiresInMs)} ago`;
5951
+ }
5717
5952
 
5718
5953
  // src/credential.ts
5719
5954
  var CREDENTIAL_UNAVAILABLE_EXIT = 64;
@@ -6329,54 +6564,6 @@ function readDirectorySelections(loaded) {
6329
6564
  return { ...identity === void 0 ? {} : { identity }, ...configProfile === void 0 ? {} : { configProfile } };
6330
6565
  }
6331
6566
 
6332
- // src/usage/preflight.ts
6333
- var MS_PER_SECOND2 = 1e3;
6334
- var SECONDS_PER_MINUTE = 60;
6335
- var MINUTES_PER_HOUR = 60;
6336
- var HOURS_PER_DAY = 24;
6337
- var WARNING_STATUSES = /* @__PURE__ */ new Set(["allowed_warning", "rejected"]);
6338
- function formatAge(ms) {
6339
- const minutes = Math.floor(ms / (MS_PER_SECOND2 * SECONDS_PER_MINUTE));
6340
- if (minutes < MINUTES_PER_HOUR) {
6341
- return `${String(Math.max(minutes, 0))}m`;
6342
- }
6343
- const hours = Math.floor(minutes / MINUTES_PER_HOUR);
6344
- return hours < HOURS_PER_DAY ? `${String(hours)}h` : `${String(Math.floor(hours / HOURS_PER_DAY))}d`;
6345
- }
6346
- function effectiveWindow(window, nowMs) {
6347
- const resetsAtMs = window.resetsAt === void 0 ? void 0 : Date.parse(window.resetsAt);
6348
- if (resetsAtMs !== void 0 && resetsAtMs <= nowMs) {
6349
- return { reset: true, utilization: 0 };
6350
- }
6351
- return {
6352
- reset: false,
6353
- ...window.utilization === void 0 ? {} : { utilization: window.utilization },
6354
- ...resetsAtMs === void 0 ? {} : { resetsAtMs },
6355
- ...window.status === void 0 ? {} : { status: window.status }
6356
- };
6357
- }
6358
- function quotaWarnings(snapshot, provider, nowMs) {
6359
- const rateLimit = snapshot?.providers[provider]?.rateLimit;
6360
- const unified = rateLimit?.unified;
6361
- if (snapshot === void 0 || rateLimit === void 0 || unified === void 0) {
6362
- return [];
6363
- }
6364
- const age = formatAge(nowMs - Date.parse(rateLimit.observedAt));
6365
- const windows = [
6366
- { name: "five-hour", window: unified.fiveHour },
6367
- { name: "seven-day", window: unified.sevenDay }
6368
- ];
6369
- return windows.flatMap(({ name, window }) => {
6370
- const effective = window === void 0 ? void 0 : effectiveWindow(window, nowMs);
6371
- if (window === void 0 || effective?.status === void 0 || !WARNING_STATUSES.has(effective.status)) {
6372
- return [];
6373
- }
6374
- const reset = window.resetsAt === void 0 ? "" : `, resets ${window.resetsAt}`;
6375
- const verdict = effective.status === "rejected" ? "exhausted" : "nearly used";
6376
- return [`agent-shim: identity ${snapshot.identity}: the ${name} quota is ${verdict}${reset} (last seen ${age} ago)`];
6377
- });
6378
- }
6379
-
6380
6567
  // src/usage/poolPick.ts
6381
6568
  import path19 from "node:path";
6382
6569
  import { z as z9 } from "zod";
@@ -6772,19 +6959,97 @@ function collectPoolPick(params) {
6772
6959
  };
6773
6960
  }
6774
6961
 
6962
+ // src/realCredentialCache.ts
6963
+ import { spawnSync } from "node:child_process";
6964
+ import fs7 from "node:fs";
6965
+ import path20 from "node:path";
6966
+ var KEYCHAIN_CACHE_SERVICE = "agent-shim-credential-cache";
6967
+ var CACHE_DIR_MODE = 448;
6968
+ var CACHE_FILE_MODE = 384;
6969
+ function cacheFileNameFor(owner) {
6970
+ return `${owner.replace(/[^A-Za-z0-9._-]/g, "_")}.json`;
6971
+ }
6972
+ function parseEntry(raw) {
6973
+ let json;
6974
+ try {
6975
+ json = JSON.parse(raw);
6976
+ } catch {
6977
+ return void 0;
6978
+ }
6979
+ const parsed = CachedCredentialSchema.safeParse(json);
6980
+ return parsed.success ? parsed.data : void 0;
6981
+ }
6982
+ function requireKeychain() {
6983
+ if (process.platform !== "darwin") {
6984
+ throw new Error("the keychain credential store needs macOS; use --credential-cache-store file");
6985
+ }
6986
+ }
6987
+ function security(args, stdin) {
6988
+ const result = spawnSync("security", args, { encoding: "utf8", ...stdin === void 0 ? {} : { input: stdin }, stdio: ["pipe", "pipe", "ignore"] });
6989
+ return { status: result.status, stdout: result.stdout };
6990
+ }
6991
+ function createRealCredentialCache(root) {
6992
+ const dir = path20.join(root, "credential-cache");
6993
+ const filePath = (owner) => path20.join(dir, cacheFileNameFor(owner));
6994
+ return {
6995
+ read(store, owner) {
6996
+ if (store === "file") {
6997
+ try {
6998
+ return parseEntry(fs7.readFileSync(filePath(owner), "utf8"));
6999
+ } catch {
7000
+ return void 0;
7001
+ }
7002
+ }
7003
+ requireKeychain();
7004
+ const result = security(["find-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner, "-w"]);
7005
+ return result.status === 0 ? parseEntry(Buffer.from(result.stdout.trim(), "base64").toString("utf8")) : void 0;
7006
+ },
7007
+ write(store, owner, entry) {
7008
+ const serialised = JSON.stringify(entry);
7009
+ if (store === "file") {
7010
+ fs7.mkdirSync(dir, { recursive: true, mode: CACHE_DIR_MODE });
7011
+ fs7.chmodSync(dir, CACHE_DIR_MODE);
7012
+ const target = filePath(owner);
7013
+ const temp = `${target}.${String(process.pid)}.tmp`;
7014
+ fs7.writeFileSync(temp, serialised, { mode: CACHE_FILE_MODE });
7015
+ fs7.renameSync(temp, target);
7016
+ return;
7017
+ }
7018
+ requireKeychain();
7019
+ const encoded = Buffer.from(serialised, "utf8").toString("base64");
7020
+ const result = security(["-i"], `add-generic-password -U -s ${KEYCHAIN_CACHE_SERVICE} -a ${JSON.stringify(owner)} -w ${encoded}
7021
+ `);
7022
+ if (result.status !== 0) {
7023
+ throw new Error(`could not write the keychain credential cache for ${owner}`);
7024
+ }
7025
+ },
7026
+ remove(store, owner) {
7027
+ if (store === "file") {
7028
+ fs7.rmSync(filePath(owner), { force: true });
7029
+ return;
7030
+ }
7031
+ requireKeychain();
7032
+ security(["delete-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner]);
7033
+ }
7034
+ };
7035
+ }
7036
+ function realCredentialCacheEnv(paths) {
7037
+ return { port: createRealCredentialCache(paths.root), platform: process.platform, now: () => Date.now() };
7038
+ }
7039
+
6775
7040
  // src/realPorts.ts
6776
- import { spawn, spawnSync } from "node:child_process";
7041
+ import { spawn, spawnSync as spawnSync2 } from "node:child_process";
6777
7042
  import { createHash as createHash5 } from "node:crypto";
6778
- import fs7 from "node:fs";
7043
+ import fs8 from "node:fs";
6779
7044
  import os3 from "node:os";
6780
- import path21 from "node:path";
7045
+ import path22 from "node:path";
6781
7046
  import { isSea } from "node:sea";
6782
7047
 
6783
7048
  // src/versionDiscovery.ts
6784
7049
  import os2 from "node:os";
6785
- import path20 from "node:path";
7050
+ import path21 from "node:path";
6786
7051
  function defaultVersionsDir() {
6787
- return path20.join(os2.homedir(), ".local", "share", "claude", "versions");
7052
+ return path21.join(os2.homedir(), ".local", "share", "claude", "versions");
6788
7053
  }
6789
7054
  var NUMERIC_DOTTED_VERSION_RE = /^\d+(?:\.\d+)*$/;
6790
7055
  function isNumericDottedVersion(name) {
@@ -6826,7 +7091,7 @@ function discoverClaudeBinary(options) {
6826
7091
  const highestVersion = pickHighestVersion(entries);
6827
7092
  if (highestVersion !== void 0) {
6828
7093
  return {
6829
- path: path20.join(versionsDir, highestVersion),
7094
+ path: path21.join(versionsDir, highestVersion),
6830
7095
  source: "versions-dir",
6831
7096
  version: highestVersion
6832
7097
  };
@@ -6849,7 +7114,7 @@ function isEnoent3(error) {
6849
7114
  var realFsPort = {
6850
7115
  readFileUtf8(filePath) {
6851
7116
  try {
6852
- return fs7.readFileSync(filePath, "utf8");
7117
+ return fs8.readFileSync(filePath, "utf8");
6853
7118
  } catch (error) {
6854
7119
  if (isEnoent3(error)) {
6855
7120
  return void 0;
@@ -6860,7 +7125,7 @@ var realFsPort = {
6860
7125
  readConfigFile: cosmiconfigReader(),
6861
7126
  readdir(dirPath) {
6862
7127
  try {
6863
- return fs7.readdirSync(dirPath);
7128
+ return fs8.readdirSync(dirPath);
6864
7129
  } catch (error) {
6865
7130
  if (isEnoent3(error)) {
6866
7131
  return [];
@@ -6879,7 +7144,7 @@ var realFarmFs = {
6879
7144
  lstat(filePath) {
6880
7145
  let stat;
6881
7146
  try {
6882
- stat = fs7.lstatSync(filePath);
7147
+ stat = fs8.lstatSync(filePath);
6883
7148
  } catch (error) {
6884
7149
  if (isEnoent3(error)) {
6885
7150
  return void 0;
@@ -6891,7 +7156,7 @@ var realFarmFs = {
6891
7156
  },
6892
7157
  readdir(dirPath) {
6893
7158
  try {
6894
- return fs7.readdirSync(dirPath);
7159
+ return fs8.readdirSync(dirPath);
6895
7160
  } catch (error) {
6896
7161
  if (isEnoent3(error) || isErrorWithCode(error, "ENOTDIR")) {
6897
7162
  return [];
@@ -6900,23 +7165,23 @@ var realFarmFs = {
6900
7165
  }
6901
7166
  },
6902
7167
  mkdirp(dirPath) {
6903
- fs7.mkdirSync(dirPath, { recursive: true });
7168
+ fs8.mkdirSync(dirPath, { recursive: true });
6904
7169
  },
6905
7170
  symlink(target, linkPath) {
6906
- fs7.symlinkSync(target, linkPath);
7171
+ fs8.symlinkSync(target, linkPath);
6907
7172
  },
6908
7173
  rename(from, to) {
6909
- fs7.renameSync(from, to);
7174
+ fs8.renameSync(from, to);
6910
7175
  },
6911
7176
  removeRecursive(targetPath) {
6912
- fs7.rmSync(targetPath, { recursive: true, force: true });
7177
+ fs8.rmSync(targetPath, { recursive: true, force: true });
6913
7178
  },
6914
7179
  copyRecursive(from, to) {
6915
- fs7.cpSync(from, to, { recursive: true, force: true, verbatimSymlinks: true });
7180
+ fs8.cpSync(from, to, { recursive: true, force: true, verbatimSymlinks: true });
6916
7181
  },
6917
7182
  readFileUtf8(filePath) {
6918
7183
  try {
6919
- return fs7.readFileSync(filePath, "utf8");
7184
+ return fs8.readFileSync(filePath, "utf8");
6920
7185
  } catch (error) {
6921
7186
  if (isEnoent3(error)) {
6922
7187
  return void 0;
@@ -6925,36 +7190,36 @@ var realFarmFs = {
6925
7190
  }
6926
7191
  },
6927
7192
  writeFileUtf8(filePath, contents) {
6928
- fs7.writeFileSync(filePath, contents, "utf8");
7193
+ fs8.writeFileSync(filePath, contents, "utf8");
6929
7194
  },
6930
7195
  mkdirPrivate(dirPath) {
6931
- fs7.mkdirSync(dirPath, { recursive: true, mode: OWNER_ONLY_DIR_MODE });
6932
- fs7.chmodSync(dirPath, OWNER_ONLY_DIR_MODE);
7196
+ fs8.mkdirSync(dirPath, { recursive: true, mode: OWNER_ONLY_DIR_MODE });
7197
+ fs8.chmodSync(dirPath, OWNER_ONLY_DIR_MODE);
6933
7198
  },
6934
7199
  writeFilePrivate(filePath, contents) {
6935
7200
  const temp = `${filePath}.${String(process.pid)}.tmp`;
6936
- fs7.writeFileSync(temp, contents, { encoding: "utf8", mode: OWNER_ONLY_FILE_MODE });
6937
- fs7.chmodSync(temp, OWNER_ONLY_FILE_MODE);
6938
- fs7.renameSync(temp, filePath);
7201
+ fs8.writeFileSync(temp, contents, { encoding: "utf8", mode: OWNER_ONLY_FILE_MODE });
7202
+ fs8.chmodSync(temp, OWNER_ONLY_FILE_MODE);
7203
+ fs8.renameSync(temp, filePath);
6939
7204
  },
6940
7205
  appendFilePrivate(filePath, contents) {
6941
- const descriptor = fs7.openSync(filePath, "a", OWNER_ONLY_FILE_MODE);
7206
+ const descriptor = fs8.openSync(filePath, "a", OWNER_ONLY_FILE_MODE);
6942
7207
  try {
6943
- if ((fs7.fstatSync(descriptor).mode & PERMISSION_BITS2) !== OWNER_ONLY_FILE_MODE) {
6944
- fs7.fchmodSync(descriptor, OWNER_ONLY_FILE_MODE);
7208
+ if ((fs8.fstatSync(descriptor).mode & PERMISSION_BITS2) !== OWNER_ONLY_FILE_MODE) {
7209
+ fs8.fchmodSync(descriptor, OWNER_ONLY_FILE_MODE);
6945
7210
  }
6946
7211
  const bytes = Buffer.from(contents, "utf8");
6947
7212
  let written = 0;
6948
7213
  while (written < bytes.length) {
6949
- written += fs7.writeSync(descriptor, bytes, written, bytes.length - written);
7214
+ written += fs8.writeSync(descriptor, bytes, written, bytes.length - written);
6950
7215
  }
6951
7216
  } finally {
6952
- fs7.closeSync(descriptor);
7217
+ fs8.closeSync(descriptor);
6953
7218
  }
6954
7219
  },
6955
7220
  writeFileExclusive(filePath, contents) {
6956
7221
  try {
6957
- fs7.writeFileSync(filePath, contents, { encoding: "utf8", flag: "wx" });
7222
+ fs8.writeFileSync(filePath, contents, { encoding: "utf8", flag: "wx" });
6958
7223
  return true;
6959
7224
  } catch (error) {
6960
7225
  if (isErrorWithCode(error, "EEXIST")) {
@@ -6965,7 +7230,7 @@ var realFarmFs = {
6965
7230
  },
6966
7231
  hashFile(filePath) {
6967
7232
  try {
6968
- return createHash5("sha256").update(fs7.readFileSync(filePath)).digest("hex");
7233
+ return createHash5("sha256").update(fs8.readFileSync(filePath)).digest("hex");
6969
7234
  } catch (error) {
6970
7235
  if (isEnoent3(error) || isErrorWithCode(error, "EISDIR")) {
6971
7236
  return void 0;
@@ -6987,7 +7252,7 @@ function realIsProcessAlive(pid) {
6987
7252
  }
6988
7253
  }
6989
7254
  function realIsProcessZombie(pid) {
6990
- const result = spawnSync("ps", ["-o", "stat=", "-p", String(pid)], { encoding: "utf8" });
7255
+ const result = spawnSync2("ps", ["-o", "stat=", "-p", String(pid)], { encoding: "utf8" });
6991
7256
  return result.status === 0 && result.stdout.trim().startsWith("Z");
6992
7257
  }
6993
7258
  function realIsProcessRunning(pid) {
@@ -6995,17 +7260,17 @@ function realIsProcessRunning(pid) {
6995
7260
  }
6996
7261
  var realRunPort = {
6997
7262
  run(command, args) {
6998
- const result = spawnSync(command, [...args], { encoding: "utf8" });
7263
+ const result = spawnSync2(command, [...args], { encoding: "utf8" });
6999
7264
  return { status: result.status, stdout: result.stdout, stderr: result.stderr };
7000
7265
  }
7001
7266
  };
7002
7267
  function expandHome2(filePath) {
7003
- return filePath.startsWith("~/") ? path21.join(os3.homedir(), filePath.slice(1)) : filePath;
7268
+ return filePath.startsWith("~/") ? path22.join(os3.homedir(), filePath.slice(1)) : filePath;
7004
7269
  }
7005
7270
  var MACOS_GUI_SESSION_MANAGER = "Aqua";
7006
7271
  function hasDesktopSession() {
7007
7272
  if (process.platform === "darwin") {
7008
- const result = spawnSync("launchctl", ["managername"], { encoding: "utf8" });
7273
+ const result = spawnSync2("launchctl", ["managername"], { encoding: "utf8" });
7009
7274
  return result.status === 0 && result.stdout.trim() === MACOS_GUI_SESSION_MANAGER;
7010
7275
  }
7011
7276
  if (process.platform === "win32") {
@@ -7019,8 +7284,8 @@ var realCredentialPort = {
7019
7284
  let content;
7020
7285
  let stat;
7021
7286
  try {
7022
- stat = fs7.statSync(resolved);
7023
- content = fs7.readFileSync(resolved, "utf8");
7287
+ stat = fs8.statSync(resolved);
7288
+ content = fs8.readFileSync(resolved, "utf8");
7024
7289
  } catch (error) {
7025
7290
  if (isEnoent3(error)) {
7026
7291
  return { found: false };
@@ -7031,7 +7296,7 @@ var realCredentialPort = {
7031
7296
  },
7032
7297
  runCommand(argv, options) {
7033
7298
  const [command, ...args] = argv;
7034
- const result = spawnSync(command, args, {
7299
+ const result = spawnSync2(command, args, {
7035
7300
  encoding: "utf8",
7036
7301
  timeout: options.timeoutMs,
7037
7302
  stdio: options.interactive ? ["inherit", "pipe", "inherit"] : ["ignore", "pipe", "pipe"]
@@ -7073,8 +7338,8 @@ function spawnDaemonThrough(executable) {
7073
7338
  return (paths, subcommand, logPath) => spawnDetachedDaemon({ command: executable, args: [subcommand] }, paths, subcommand, logPath);
7074
7339
  }
7075
7340
  function spawnDetachedDaemon(invocation, paths, subcommand, logPath) {
7076
- fs7.mkdirSync(path21.dirname(logPath), { recursive: true });
7077
- const logFd = fs7.openSync(logPath, "a");
7341
+ fs8.mkdirSync(path22.dirname(logPath), { recursive: true });
7342
+ const logFd = fs8.openSync(logPath, "a");
7078
7343
  try {
7079
7344
  const child = spawn(invocation.command, invocation.args, detachedDaemonSpawnOptions(logFd, { ...process.env, AGENT_SHIM_HOME: paths.root }));
7080
7345
  child.unref();
@@ -7083,7 +7348,7 @@ function spawnDetachedDaemon(invocation, paths, subcommand, logPath) {
7083
7348
  }
7084
7349
  return child.pid;
7085
7350
  } finally {
7086
- fs7.closeSync(logFd);
7351
+ fs8.closeSync(logFd);
7087
7352
  }
7088
7353
  }
7089
7354
  function spawnHeadroomSupervisor(paths, spawnDaemon) {
@@ -7101,7 +7366,7 @@ var realHeadroomSocketTrust = {
7101
7366
  lstat: (target) => {
7102
7367
  let stat;
7103
7368
  try {
7104
- stat = fs7.lstatSync(target);
7369
+ stat = fs8.lstatSync(target);
7105
7370
  } catch (error) {
7106
7371
  if (isEnoent3(error)) {
7107
7372
  return void 0;
@@ -7147,7 +7412,7 @@ var EXECUTE_BITS_MASK = 73;
7147
7412
  function listVersionsDir(dir) {
7148
7413
  let entries;
7149
7414
  try {
7150
- entries = fs7.readdirSync(dir, { withFileTypes: true });
7415
+ entries = fs8.readdirSync(dir, { withFileTypes: true });
7151
7416
  } catch (error) {
7152
7417
  if (isEnoent3(error)) {
7153
7418
  return [];
@@ -7155,8 +7420,8 @@ function listVersionsDir(dir) {
7155
7420
  throw error;
7156
7421
  }
7157
7422
  return entries.filter((entry) => entry.isFile()).map((entry) => {
7158
- const fullPath = path21.join(dir, entry.name);
7159
- const stat = fs7.statSync(fullPath);
7423
+ const fullPath = path22.join(dir, entry.name);
7424
+ const stat = fs8.statSync(fullPath);
7160
7425
  return {
7161
7426
  name: entry.name,
7162
7427
  isFile: true,
@@ -7170,7 +7435,7 @@ function windowsPathExtensions(pathext) {
7170
7435
  return (pathext ?? WINDOWS_DEFAULT_PATHEXT).split(";").map((ext) => ext.trim()).filter((ext) => ext.length > 0);
7171
7436
  }
7172
7437
  function resolveExecutableCandidate(dir, name, env) {
7173
- const candidate = path21.join(dir, name);
7438
+ const candidate = path22.join(dir, name);
7174
7439
  if (env.platform === "win32") {
7175
7440
  const extensions = windowsPathExtensions(env.pathext);
7176
7441
  const alreadyHasRecognisedExtension = extensions.some((ext) => candidate.toLowerCase().endsWith(ext.toLowerCase()));
@@ -7190,7 +7455,7 @@ function resolveExecutableCandidate(dir, name, env) {
7190
7455
  }
7191
7456
  function realStatFileMode(candidate) {
7192
7457
  try {
7193
- const stat = fs7.statSync(candidate);
7458
+ const stat = fs8.statSync(candidate);
7194
7459
  return stat.isFile() ? stat.mode : void 0;
7195
7460
  } catch (error) {
7196
7461
  if (isEnoent3(error)) {
@@ -7219,13 +7484,13 @@ function resolveOwnExecutablePath(env) {
7219
7484
  if (env.argv1 !== void 0 && !env.argv1.includes("/") && !env.argv1.includes("\\")) {
7220
7485
  return searchPathForExecutable(env.pathDirs, env.argv1, env.findExecutableInDir) ?? env.execPath;
7221
7486
  }
7222
- const candidate = env.argv1 === void 0 ? env.execPath : path21.resolve(env.cwd, env.argv1);
7223
- const candidateDir = path21.dirname(candidate);
7224
- const candidateDirOnPath = env.pathDirs.some((dir) => path21.resolve(dir) === candidateDir);
7487
+ const candidate = env.argv1 === void 0 ? env.execPath : path22.resolve(env.cwd, env.argv1);
7488
+ const candidateDir = path22.dirname(candidate);
7489
+ const candidateDirOnPath = env.pathDirs.some((dir) => path22.resolve(dir) === candidateDir);
7225
7490
  if (candidateDirOnPath) {
7226
7491
  return candidate;
7227
7492
  }
7228
- const viaPath = searchPathForExecutable(env.pathDirs, path21.basename(candidate), env.findExecutableInDir);
7493
+ const viaPath = searchPathForExecutable(env.pathDirs, path22.basename(candidate), env.findExecutableInDir);
7229
7494
  return viaPath ?? candidate;
7230
7495
  }
7231
7496
  function realOwnExecutablePath() {
@@ -7233,7 +7498,7 @@ function realOwnExecutablePath() {
7233
7498
  argv1: process.argv[1],
7234
7499
  execPath: process.execPath,
7235
7500
  cwd: process.cwd(),
7236
- pathDirs: (process.env.PATH ?? "").split(path21.delimiter).filter((dir) => dir !== ""),
7501
+ pathDirs: (process.env.PATH ?? "").split(path22.delimiter).filter((dir) => dir !== ""),
7237
7502
  findExecutableInDir
7238
7503
  });
7239
7504
  }
@@ -7246,14 +7511,14 @@ function realContentSourcePath() {
7246
7511
  function realResolveClaudeBinary(isOwnBinary) {
7247
7512
  return () => discoverClaudeBinary({
7248
7513
  listVersionsDir,
7249
- pathDirs: (process.env.PATH ?? "").split(path21.delimiter).filter((dir) => dir !== ""),
7514
+ pathDirs: (process.env.PATH ?? "").split(path22.delimiter).filter((dir) => dir !== ""),
7250
7515
  findExecutableInDir,
7251
7516
  isOwnBinary
7252
7517
  });
7253
7518
  }
7254
7519
 
7255
7520
  // src/checkReport.ts
7256
- import path22 from "node:path";
7521
+ import path23 from "node:path";
7257
7522
  import { z as z10 } from "zod";
7258
7523
 
7259
7524
  // src/cli/bool.ts
@@ -7423,14 +7688,23 @@ function inspectSettingsExposure(params) {
7423
7688
  }
7424
7689
  return reports;
7425
7690
  }
7426
- function buildCredentialReport(provider, identity) {
7691
+ function cachedState(cacheEnv, owner, block) {
7692
+ if (cacheEnv === void 0 || block.cache === void 0) {
7693
+ return void 0;
7694
+ }
7695
+ return describeCachedCredential({ port: cacheEnv.port, platform: cacheEnv.platform, owner, cache: block.cache, nowMs: cacheEnv.now() });
7696
+ }
7697
+ function buildCredentialReport(provider, identity, identityName, cacheEnv) {
7427
7698
  const identityCredential = identity?.credential === void 0 ? void 0 : summariseCredential(identity.credential);
7699
+ const identityCached = identity?.credential === void 0 || identityName === void 0 ? void 0 : cachedState(cacheEnv, `identity ${identityName}`, identity.credential);
7700
+ const providerCached = provider?.definition === void 0 ? void 0 : cachedState(cacheEnv, `provider ${provider.name}`, provider.definition.credential);
7428
7701
  return {
7429
7702
  applies: provider !== void 0 ? "provider" : identityCredential !== void 0 ? "identity" : "stored-login",
7430
7703
  ...provider === void 0 ? {} : {
7431
- provider: provider.problem === void 0 ? { name: provider.name, credential: summariseCredential(provider.definition.credential) } : { name: provider.name, problem: provider.problem }
7704
+ provider: provider.problem === void 0 ? { name: provider.name, credential: summariseCredential(provider.definition.credential), ...providerCached === void 0 ? {} : { cached: providerCached } } : { name: provider.name, problem: provider.problem }
7432
7705
  },
7433
- ...identityCredential === void 0 ? {} : { identity: identityCredential }
7706
+ ...identityCredential === void 0 ? {} : { identity: identityCredential },
7707
+ ...identityCached === void 0 ? {} : { identityCached }
7434
7708
  };
7435
7709
  }
7436
7710
  function runCheck(params) {
@@ -7469,7 +7743,7 @@ function runCheck(params) {
7469
7743
  ambientCredential,
7470
7744
  ...keychain === void 0 ? {} : { keychain },
7471
7745
  settingsExposure,
7472
- credential: buildCredentialReport(params.provider, params.identity)
7746
+ credential: buildCredentialReport(params.provider, params.identity, params.identityName, params.credentialCache)
7473
7747
  };
7474
7748
  }
7475
7749
  function formatCheckReport(report) {
@@ -7507,12 +7781,12 @@ function formatCheckReport(report) {
7507
7781
  const { credential } = report;
7508
7782
  if (credential.provider !== void 0) {
7509
7783
  lines.push(
7510
- ` Provider ${credential.provider.name}: ${credential.provider.credential === void 0 ? `unusable: ${credential.provider.problem ?? ""}` : formatCredentialSummary(credential.provider.credential)}`
7784
+ ` Provider ${credential.provider.name}: ${credential.provider.credential === void 0 ? `unusable: ${credential.provider.problem ?? ""}` : formatCredentialSummary(credential.provider.credential)}${credential.provider.cached === void 0 ? "" : `; ${formatCachedCredentialState(credential.provider.cached)}`}`
7511
7785
  );
7512
7786
  }
7513
7787
  if (credential.identity !== void 0) {
7514
7788
  lines.push(
7515
- ` Identity ${report.identityName ?? "(none)"}: ${formatCredentialSummary(credential.identity)}${credential.applies === "provider" ? " (not used: the provider's credential applies)" : ""}`
7789
+ ` Identity ${report.identityName ?? "(none)"}: ${formatCredentialSummary(credential.identity)}${credential.identityCached === void 0 ? "" : `; ${formatCachedCredentialState(credential.identityCached)}`}${credential.applies === "provider" ? " (not used: the provider's credential applies)" : ""}`
7516
7790
  );
7517
7791
  }
7518
7792
  if (credential.applies === "stored-login") {
@@ -7621,10 +7895,10 @@ function collectCheckReport(params) {
7621
7895
  }).input;
7622
7896
  const providerName = cascadeProviderName(cascade);
7623
7897
  const provider = providerName === void 0 ? void 0 : loadCheckProvider(paths, providerName);
7624
- const farmRoot = identityDecision.name === void 0 ? void 0 : path22.join(paths.identitiesDir, identityDecision.name);
7898
+ const farmRoot = identityDecision.name === void 0 ? void 0 : path23.join(paths.identitiesDir, identityDecision.name);
7625
7899
  const settingsFiles = {
7626
- "settings.json": realFsPort.readFileUtf8(path22.join(claudeHome, "settings.json")),
7627
- "settings.local.json": realFsPort.readFileUtf8(path22.join(claudeHome, "settings.local.json"))
7900
+ "settings.json": realFsPort.readFileUtf8(path23.join(claudeHome, "settings.json")),
7901
+ "settings.local.json": realFsPort.readFileUtf8(path23.join(claudeHome, "settings.local.json"))
7628
7902
  };
7629
7903
  const report = runCheck({
7630
7904
  cwd,
@@ -7645,6 +7919,7 @@ function collectCheckReport(params) {
7645
7919
  ...loadedIdentity === void 0 ? {} : { identity: loadedIdentity.config },
7646
7920
  settingsFiles,
7647
7921
  run: realRunPort,
7922
+ credentialCache: realCredentialCacheEnv(paths),
7648
7923
  ...farmRoot === void 0 ? {} : { farmRoot },
7649
7924
  platform: process.platform,
7650
7925
  ...provider === void 0 ? {} : { provider }
@@ -7653,8 +7928,8 @@ function collectCheckReport(params) {
7653
7928
  }
7654
7929
 
7655
7930
  // src/claudeShim.ts
7656
- import fs8 from "node:fs";
7657
- import path23 from "node:path";
7931
+ import fs9 from "node:fs";
7932
+ import path24 from "node:path";
7658
7933
  import { z as z11 } from "zod";
7659
7934
  var ClaudeShimStateSchema = z11.strictObject({
7660
7935
  targetPath: z11.string().min(1),
@@ -7666,10 +7941,10 @@ function commandFilename(ownExecutablePath, commandName) {
7666
7941
  }
7667
7942
  function resolveOwnBinaryCheck(paths, contentSourcePath) {
7668
7943
  const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
7669
- const recordedTarget = state === void 0 ? void 0 : path23.resolve(state.targetPath);
7944
+ const recordedTarget = state === void 0 ? void 0 : path24.resolve(state.targetPath);
7670
7945
  let ownStat;
7671
7946
  return (candidate) => {
7672
- if (recordedTarget !== void 0 && path23.resolve(candidate) === recordedTarget) {
7947
+ if (recordedTarget !== void 0 && path24.resolve(candidate) === recordedTarget) {
7673
7948
  return true;
7674
7949
  }
7675
7950
  ownStat ??= statOrUndefined(contentSourcePath);
@@ -7683,7 +7958,7 @@ function isEnoent4(error) {
7683
7958
  }
7684
7959
  function statOrUndefined(target) {
7685
7960
  try {
7686
- return fs8.statSync(target);
7961
+ return fs9.statSync(target);
7687
7962
  } catch (error) {
7688
7963
  if (isEnoent4(error)) {
7689
7964
  return void 0;
@@ -7691,22 +7966,22 @@ function statOrUndefined(target) {
7691
7966
  throw error;
7692
7967
  }
7693
7968
  }
7694
- var nodeLinkFs = { link: fs8.linkSync, copyFile: fs8.copyFileSync, chmod: fs8.chmodSync };
7969
+ var nodeLinkFs = { link: fs9.linkSync, copyFile: fs9.copyFileSync, chmod: fs9.chmodSync };
7695
7970
  function findPathShadow(params) {
7696
- const resolvedTargetDir = path23.resolve(params.targetDir);
7971
+ const resolvedTargetDir = path24.resolve(params.targetDir);
7697
7972
  for (const dir of params.pathDirs) {
7698
7973
  const found = params.findExecutableInDir(dir, params.targetFilename);
7699
7974
  if (found === void 0) {
7700
7975
  continue;
7701
7976
  }
7702
- return path23.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
7977
+ return path24.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
7703
7978
  }
7704
- return params.pathDirs.some((dir) => path23.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
7979
+ return params.pathDirs.some((dir) => path24.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
7705
7980
  }
7706
7981
 
7707
7982
  // src/doctorReport.ts
7708
- import fs9 from "node:fs";
7709
- import path24 from "node:path";
7983
+ import fs10 from "node:fs";
7984
+ import path25 from "node:path";
7710
7985
  function refinePathShadow(status, ownExecutablePath, realpath) {
7711
7986
  if (status.status !== "shadowed") {
7712
7987
  return status;
@@ -7730,7 +8005,7 @@ function validateJson(schema, input) {
7730
8005
  return { ok: true, data: result.data };
7731
8006
  }
7732
8007
  function pushPathResolution(push, resolution) {
7733
- const ownDir = path24.dirname(resolution.ownExecutablePath);
8008
+ const ownDir = path25.dirname(resolution.ownExecutablePath);
7734
8009
  switch (resolution.agentShim.status) {
7735
8010
  case "ok":
7736
8011
  push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
@@ -7772,7 +8047,14 @@ function pushPathResolution(push, resolution) {
7772
8047
  break;
7773
8048
  }
7774
8049
  }
7775
- function pushProvider(push, entry) {
8050
+ function cacheClause(cacheEnv, owner, block) {
8051
+ if (cacheEnv === void 0 || block.cache === void 0) {
8052
+ return { text: "", unreadable: false };
8053
+ }
8054
+ const state = describeCachedCredential({ port: cacheEnv.port, platform: cacheEnv.platform, owner, cache: block.cache, nowMs: cacheEnv.now() });
8055
+ return { text: `; ${formatCachedCredentialState(state)}`, unreadable: state.status === "unreadable" };
8056
+ }
8057
+ function pushProvider(push, entry, cacheEnv) {
7776
8058
  if (entry.raw === void 0) {
7777
8059
  push("provider", "fail", `${entry.path} is missing.`, entry.name);
7778
8060
  return;
@@ -7794,18 +8076,19 @@ function pushProvider(push, entry) {
7794
8076
  push("provider", "fail", new ConfigValidationError(entry.path, validated.error.issues).message, entry.name);
7795
8077
  return;
7796
8078
  }
7797
- push("provider", "pass", `${entry.name} is valid (${describeProviderEndpoint(validated.data)}, credential ${describeCredential(validated.data.credential)}).`, entry.name);
8079
+ const cache = cacheClause(cacheEnv, `provider ${entry.name}`, validated.data.credential);
8080
+ push("provider", cache.unreadable ? "fail" : "pass", `${entry.name} is valid (${describeProviderEndpoint(validated.data)}, credential ${describeCredential(validated.data.credential)})${cache.text}.`, entry.name);
7798
8081
  }
7799
8082
  function pushLegacyName(push, params) {
7800
8083
  const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
7801
- if (legacyVariables.length === 0 && path24.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
8084
+ if (legacyVariables.length === 0 && path25.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
7802
8085
  push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
7803
8086
  return;
7804
8087
  }
7805
8088
  for (const name of legacyVariables) {
7806
8089
  push("legacy-name", "warn", `${name} is the former name of ${name.replace(LEGACY_ENV_PREFIX, ENV_PREFIX)}. Rename it; the old name still works but is only a fallback.`, name);
7807
8090
  }
7808
- if (path24.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
8091
+ if (path25.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
7809
8092
  push(
7810
8093
  "legacy-name",
7811
8094
  "warn",
@@ -7887,10 +8170,11 @@ function runDoctor(params) {
7887
8170
  );
7888
8171
  } else {
7889
8172
  const credential = validated.data.credential;
8173
+ const cache = credential === void 0 ? { text: "", unreadable: false } : cacheClause(params.credentialCache, `identity ${entry.name}`, credential);
7890
8174
  push(
7891
8175
  "identity",
7892
- "pass",
7893
- `${entry.name} is valid and authenticates with ${credential === void 0 ? "its stored login" : `credential ${describeCredential(credential)}`}.`,
8176
+ cache.unreadable ? "fail" : "pass",
8177
+ `${entry.name} is valid and authenticates with ${credential === void 0 ? "its stored login" : `credential ${describeCredential(credential)}`}${cache.text}.`,
7894
8178
  entry.name
7895
8179
  );
7896
8180
  }
@@ -7910,7 +8194,7 @@ function runDoctor(params) {
7910
8194
  return poolName === void 0 ? validIdentityNames.has(selector) : poolName in pools;
7911
8195
  };
7912
8196
  for (const entry of params.providers) {
7913
- pushProvider(push, entry);
8197
+ pushProvider(push, entry, params.credentialCache);
7914
8198
  }
7915
8199
  if (params.platform !== "darwin" || params.run === void 0) {
7916
8200
  push("keychain", "pass", "Skipped (not macOS).");
@@ -8104,27 +8388,27 @@ function formatDoctorReport(report) {
8104
8388
  }
8105
8389
  function realpathOrSelf(target) {
8106
8390
  try {
8107
- return fs9.realpathSync(target);
8391
+ return fs10.realpathSync(target);
8108
8392
  } catch {
8109
8393
  return target;
8110
8394
  }
8111
8395
  }
8112
8396
  function collectDoctorReport(params) {
8113
8397
  const { paths } = params;
8114
- const identityNames = fs9.existsSync(paths.identitiesDir) ? fs9.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort() : [];
8398
+ const identityNames = fs10.existsSync(paths.identitiesDir) ? fs10.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort() : [];
8115
8399
  const identities = identityNames.map((name) => {
8116
- const farmRoot = path24.join(paths.identitiesDir, name);
8117
- const identityPath = path24.join(farmRoot, "identity.json");
8400
+ const farmRoot = path25.join(paths.identitiesDir, name);
8401
+ const identityPath = path25.join(farmRoot, "identity.json");
8118
8402
  return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot };
8119
8403
  });
8120
- const profileNames = fs9.existsSync(paths.configProfilesDir) ? fs9.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
8404
+ const profileNames = fs10.existsSync(paths.configProfilesDir) ? fs10.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
8121
8405
  const configProfiles = profileNames.map((name) => {
8122
- const profilePath = path24.join(paths.configProfilesDir, `${name}.json`);
8406
+ const profilePath = path25.join(paths.configProfilesDir, `${name}.json`);
8123
8407
  return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
8124
8408
  });
8125
- const providerNames = fs9.existsSync(paths.providersDir) ? fs9.readdirSync(paths.providersDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
8409
+ const providerNames = fs10.existsSync(paths.providersDir) ? fs10.readdirSync(paths.providersDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
8126
8410
  const providers = providerNames.map((name) => {
8127
- const providerPath = path24.join(paths.providersDir, `${name}.json`);
8411
+ const providerPath = path25.join(paths.providersDir, `${name}.json`);
8128
8412
  return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
8129
8413
  });
8130
8414
  const ownExecutablePath = realOwnExecutablePath();
@@ -8136,12 +8420,12 @@ function collectDoctorReport(params) {
8136
8420
  binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
8137
8421
  }
8138
8422
  const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
8139
- const pathDirs = (params.env.PATH ?? "").split(path24.delimiter).filter((dir) => dir !== "");
8423
+ const pathDirs = (params.env.PATH ?? "").split(path25.delimiter).filter((dir) => dir !== "");
8140
8424
  const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
8141
8425
  findPathShadow({
8142
8426
  pathDirs,
8143
- targetDir: path24.dirname(shimState.targetPath),
8144
- targetFilename: path24.basename(shimState.targetPath),
8427
+ targetDir: path25.dirname(shimState.targetPath),
8428
+ targetFilename: path25.basename(shimState.targetPath),
8145
8429
  findExecutableInDir
8146
8430
  }),
8147
8431
  shimState.targetPath,
@@ -8149,6 +8433,7 @@ function collectDoctorReport(params) {
8149
8433
  );
8150
8434
  const report = runDoctor({
8151
8435
  env: params.env,
8436
+ credentialCache: realCredentialCacheEnv(paths),
8152
8437
  identities,
8153
8438
  configProfiles,
8154
8439
  providers,
@@ -8157,14 +8442,14 @@ function collectDoctorReport(params) {
8157
8442
  categoriesLocal: { path: paths.categoriesLocalFile, raw: realFsPort.readFileUtf8(paths.categoriesLocalFile) },
8158
8443
  activeIdentity: { path: paths.activeIdentityFile, raw: realFsPort.readFileUtf8(paths.activeIdentityFile) },
8159
8444
  binaryDiscovery,
8160
- claudeShim: { state: shimState, targetExists: shimState !== void 0 && fs9.existsSync(shimState.targetPath) },
8445
+ claudeShim: { state: shimState, targetExists: shimState !== void 0 && fs10.existsSync(shimState.targetPath) },
8161
8446
  rootPath: paths.root,
8162
8447
  pathResolution: {
8163
8448
  ownExecutablePath,
8164
8449
  agentShim: refinePathShadow(
8165
8450
  findPathShadow({
8166
8451
  pathDirs,
8167
- targetDir: path24.dirname(ownExecutablePath),
8452
+ targetDir: path25.dirname(ownExecutablePath),
8168
8453
  targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
8169
8454
  findExecutableInDir
8170
8455
  }),
@@ -8185,7 +8470,7 @@ function collectDoctorReport(params) {
8185
8470
 
8186
8471
  // src/launcher.ts
8187
8472
  import { randomUUID as randomUUID4 } from "node:crypto";
8188
- import path26 from "node:path";
8473
+ import path27 from "node:path";
8189
8474
 
8190
8475
  // src/launcher/argv.ts
8191
8476
  var ConflictingIdentityError = class extends UsageError {
@@ -8437,7 +8722,7 @@ function buildCliOverride(params) {
8437
8722
  }
8438
8723
 
8439
8724
  // src/launcher/flags.ts
8440
- import path25 from "node:path";
8725
+ import path26 from "node:path";
8441
8726
 
8442
8727
  // src/headroom/headers.ts
8443
8728
  function mergeAnthropicCustomHeaders(existing, additions) {
@@ -8507,7 +8792,7 @@ function buildEnv(params) {
8507
8792
  const parentCustomHeaders = params.baseEnv.ANTHROPIC_CUSTOM_HEADERS;
8508
8793
  let providerCustomHeaders;
8509
8794
  if (!params.configDirEscapeHatch && params.resolvedIdentityName !== void 0) {
8510
- env.CLAUDE_CONFIG_DIR = path25.join(params.identitiesDir, params.resolvedIdentityName);
8795
+ env.CLAUDE_CONFIG_DIR = path26.join(params.identitiesDir, params.resolvedIdentityName);
8511
8796
  }
8512
8797
  if (params.provider !== void 0) {
8513
8798
  const providerEnv = params.provider.definition.env ?? {};
@@ -8668,7 +8953,7 @@ function prepareLaunch(params) {
8668
8953
  identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
8669
8954
  globalDefaultConfigProfile: params.globalDefaultConfigProfile
8670
8955
  });
8671
- if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(path26.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
8956
+ if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(path27.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
8672
8957
  log.error(
8673
8958
  `agent-shim: no configuration profile named "${configProfileDecision.name}" (selected via ${configProfileDecision.source}). Run \`agent-shim profile add ${configProfileDecision.name}\` first.`
8674
8959
  );
@@ -8841,84 +9126,6 @@ function prepareLaunch(params) {
8841
9126
  import os5 from "node:os";
8842
9127
  import { randomUUID as randomUUID5 } from "node:crypto";
8843
9128
 
8844
- // src/realCredentialCache.ts
8845
- import { spawnSync as spawnSync2 } from "node:child_process";
8846
- import fs10 from "node:fs";
8847
- import path27 from "node:path";
8848
- var KEYCHAIN_CACHE_SERVICE = "agent-shim-credential-cache";
8849
- var CACHE_DIR_MODE = 448;
8850
- var CACHE_FILE_MODE = 384;
8851
- function cacheFileNameFor(owner) {
8852
- return `${owner.replace(/[^A-Za-z0-9._-]/g, "_")}.json`;
8853
- }
8854
- function parseEntry(raw) {
8855
- let json;
8856
- try {
8857
- json = JSON.parse(raw);
8858
- } catch {
8859
- return void 0;
8860
- }
8861
- const parsed = CachedCredentialSchema.safeParse(json);
8862
- return parsed.success ? parsed.data : void 0;
8863
- }
8864
- function requireKeychain() {
8865
- if (process.platform !== "darwin") {
8866
- throw new Error("the keychain credential store needs macOS; use --credential-cache-store file");
8867
- }
8868
- }
8869
- function security(args, stdin) {
8870
- const result = spawnSync2("security", args, { encoding: "utf8", ...stdin === void 0 ? {} : { input: stdin }, stdio: ["pipe", "pipe", "ignore"] });
8871
- return { status: result.status, stdout: result.stdout };
8872
- }
8873
- function createRealCredentialCache(root) {
8874
- const dir = path27.join(root, "credential-cache");
8875
- const filePath = (owner) => path27.join(dir, cacheFileNameFor(owner));
8876
- return {
8877
- read(store, owner) {
8878
- if (store === "file") {
8879
- try {
8880
- return parseEntry(fs10.readFileSync(filePath(owner), "utf8"));
8881
- } catch {
8882
- return void 0;
8883
- }
8884
- }
8885
- requireKeychain();
8886
- const result = security(["find-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner, "-w"]);
8887
- return result.status === 0 ? parseEntry(Buffer.from(result.stdout.trim(), "base64").toString("utf8")) : void 0;
8888
- },
8889
- write(store, owner, entry) {
8890
- const serialised = JSON.stringify(entry);
8891
- if (store === "file") {
8892
- fs10.mkdirSync(dir, { recursive: true, mode: CACHE_DIR_MODE });
8893
- fs10.chmodSync(dir, CACHE_DIR_MODE);
8894
- const target = filePath(owner);
8895
- const temp = `${target}.${String(process.pid)}.tmp`;
8896
- fs10.writeFileSync(temp, serialised, { mode: CACHE_FILE_MODE });
8897
- fs10.renameSync(temp, target);
8898
- return;
8899
- }
8900
- requireKeychain();
8901
- const encoded = Buffer.from(serialised, "utf8").toString("base64");
8902
- const result = security(["-i"], `add-generic-password -U -s ${KEYCHAIN_CACHE_SERVICE} -a ${JSON.stringify(owner)} -w ${encoded}
8903
- `);
8904
- if (result.status !== 0) {
8905
- throw new Error(`could not write the keychain credential cache for ${owner}`);
8906
- }
8907
- },
8908
- remove(store, owner) {
8909
- if (store === "file") {
8910
- fs10.rmSync(filePath(owner), { force: true });
8911
- return;
8912
- }
8913
- requireKeychain();
8914
- security(["delete-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner]);
8915
- }
8916
- };
8917
- }
8918
- function realCredentialCacheEnv(paths) {
8919
- return { port: createRealCredentialCache(paths.root), platform: process.platform, now: () => Date.now() };
8920
- }
8921
-
8922
9129
  // src/frontdoor/realFrontDoorPort.ts
8923
9130
  import fs11 from "node:fs";
8924
9131
 
@@ -9194,6 +9401,7 @@ export {
9194
9401
  ProviderSchema,
9195
9402
  RC_IDLE_EXPIRY_MS,
9196
9403
  RC_PENDING_DEADLINE_MS,
9404
+ RC_PERMISSION_MODES,
9197
9405
  RC_REQUEST_PARSE_CAP_BYTES,
9198
9406
  RC_SESSIONS_PATH_PREFIX,
9199
9407
  ROUTED_PATH_PREFIX,
@@ -9210,6 +9418,9 @@ export {
9210
9418
  buildLayoutPaths,
9211
9419
  buildRcControlResponsePayload,
9212
9420
  buildRcEventWriteBody,
9421
+ buildRcInterruptPayload,
9422
+ buildRcSetModelPayload,
9423
+ buildRcSetPermissionModePayload,
9213
9424
  buildRcUserMessagePayload,
9214
9425
  carryOver,
9215
9426
  checkReportHasWarnings,
@@ -9234,8 +9445,10 @@ export {
9234
9445
  generateCa,
9235
9446
  identityExists,
9236
9447
  injectRcUserMessage,
9448
+ interruptRcSession,
9237
9449
  isIdentityDirectoryName,
9238
9450
  isInterceptedHost,
9451
+ isRcPermissionMode,
9239
9452
  listDirectoryRules,
9240
9453
  listIdentities,
9241
9454
  listProfiles,
@@ -9291,6 +9504,8 @@ export {
9291
9504
  setProfileEntries,
9292
9505
  setProfileLaunchFlags,
9293
9506
  setProfileMetadata,
9507
+ setRcSessionModel,
9508
+ setRcSessionPermissionMode,
9294
9509
  snapshotPath,
9295
9510
  startConnectServer,
9296
9511
  topLevelNames,