agent-shim 8.5.6 → 8.6.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/README.md +3 -0
- package/dist/cli.cjs +2176 -1976
- package/dist/index.cjs +163 -1
- package/dist/index.mjs +155 -1
- package/dist/types/index.d.ts +60 -7
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -75,6 +75,7 @@ __export(index_exports, {
|
|
|
75
75
|
ProviderSchema: () => ProviderSchema,
|
|
76
76
|
RC_IDLE_EXPIRY_MS: () => RC_IDLE_EXPIRY_MS,
|
|
77
77
|
RC_PENDING_DEADLINE_MS: () => RC_PENDING_DEADLINE_MS,
|
|
78
|
+
RC_PERMISSION_MODES: () => RC_PERMISSION_MODES,
|
|
78
79
|
RC_REQUEST_PARSE_CAP_BYTES: () => RC_REQUEST_PARSE_CAP_BYTES,
|
|
79
80
|
RC_SESSIONS_PATH_PREFIX: () => RC_SESSIONS_PATH_PREFIX,
|
|
80
81
|
ROUTED_PATH_PREFIX: () => ROUTED_PATH_PREFIX,
|
|
@@ -91,6 +92,9 @@ __export(index_exports, {
|
|
|
91
92
|
buildLayoutPaths: () => buildLayoutPaths,
|
|
92
93
|
buildRcControlResponsePayload: () => buildRcControlResponsePayload,
|
|
93
94
|
buildRcEventWriteBody: () => buildRcEventWriteBody,
|
|
95
|
+
buildRcInterruptPayload: () => buildRcInterruptPayload,
|
|
96
|
+
buildRcSetModelPayload: () => buildRcSetModelPayload,
|
|
97
|
+
buildRcSetPermissionModePayload: () => buildRcSetPermissionModePayload,
|
|
94
98
|
buildRcUserMessagePayload: () => buildRcUserMessagePayload,
|
|
95
99
|
carryOver: () => carryOver,
|
|
96
100
|
checkReportHasWarnings: () => checkReportHasWarnings,
|
|
@@ -115,8 +119,10 @@ __export(index_exports, {
|
|
|
115
119
|
generateCa: () => generateCa,
|
|
116
120
|
identityExists: () => identityExists,
|
|
117
121
|
injectRcUserMessage: () => injectRcUserMessage,
|
|
122
|
+
interruptRcSession: () => interruptRcSession,
|
|
118
123
|
isIdentityDirectoryName: () => isIdentityDirectoryName,
|
|
119
124
|
isInterceptedHost: () => isInterceptedHost,
|
|
125
|
+
isRcPermissionMode: () => isRcPermissionMode,
|
|
120
126
|
listDirectoryRules: () => listDirectoryRules,
|
|
121
127
|
listIdentities: () => listIdentities,
|
|
122
128
|
listProfiles: () => listProfiles,
|
|
@@ -172,6 +178,8 @@ __export(index_exports, {
|
|
|
172
178
|
setProfileEntries: () => setProfileEntries,
|
|
173
179
|
setProfileLaunchFlags: () => setProfileLaunchFlags,
|
|
174
180
|
setProfileMetadata: () => setProfileMetadata,
|
|
181
|
+
setRcSessionModel: () => setRcSessionModel,
|
|
182
|
+
setRcSessionPermissionMode: () => setRcSessionPermissionMode,
|
|
175
183
|
snapshotPath: () => snapshotPath,
|
|
176
184
|
startConnectServer: () => startConnectServer,
|
|
177
185
|
topLevelNames: () => topLevelNames,
|
|
@@ -4070,6 +4078,19 @@ function buildRcControlResponsePayload(requestId, decision) {
|
|
|
4070
4078
|
const result = decision.approve ? { behavior: "allow" } : { behavior: "deny", message: decision.message ?? "" };
|
|
4071
4079
|
return { type: "control_response", response: { subtype: "success", request_id: requestId, response: result } };
|
|
4072
4080
|
}
|
|
4081
|
+
var RC_PERMISSION_MODES = ["default", "acceptEdits", "bypassPermissions", "plan", "dontAsk", "auto"];
|
|
4082
|
+
function isRcPermissionMode(value) {
|
|
4083
|
+
return RC_PERMISSION_MODES.some((mode) => mode === value);
|
|
4084
|
+
}
|
|
4085
|
+
function buildRcInterruptPayload(requestId) {
|
|
4086
|
+
return { type: "control_request", request_id: requestId, request: { subtype: "interrupt" } };
|
|
4087
|
+
}
|
|
4088
|
+
function buildRcSetModelPayload(requestId, model) {
|
|
4089
|
+
return { type: "control_request", request_id: requestId, request: { subtype: "set_model", model } };
|
|
4090
|
+
}
|
|
4091
|
+
function buildRcSetPermissionModePayload(requestId, mode) {
|
|
4092
|
+
return { type: "control_request", request_id: requestId, request: { subtype: "set_permission_mode", mode } };
|
|
4093
|
+
}
|
|
4073
4094
|
async function answerRcControlRequest(deps, sessionId, requestId, decision) {
|
|
4074
4095
|
const credential = deps.credentialOf(sessionId);
|
|
4075
4096
|
if (credential === void 0) {
|
|
@@ -4094,9 +4115,64 @@ async function answerRcControlRequest(deps, sessionId, requestId, decision) {
|
|
|
4094
4115
|
}
|
|
4095
4116
|
return result;
|
|
4096
4117
|
}
|
|
4118
|
+
async function interruptRcSession(deps, sessionId) {
|
|
4119
|
+
const credential = deps.credentialOf(sessionId);
|
|
4120
|
+
if (credential === void 0) {
|
|
4121
|
+
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)` };
|
|
4122
|
+
}
|
|
4123
|
+
if (credential.authorization === void 0) {
|
|
4124
|
+
return noOAuthCredentialResult(sessionId);
|
|
4125
|
+
}
|
|
4126
|
+
const body = JSON.stringify(buildRcEventWriteBody(buildRcInterruptPayload(deps.newUuid())));
|
|
4127
|
+
let answer;
|
|
4128
|
+
try {
|
|
4129
|
+
answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
|
|
4130
|
+
} catch (error) {
|
|
4131
|
+
return { ok: false, message: `the door could not reach the API host to interrupt session ${sessionId}: ${error instanceof Error ? error.message : String(error)}` };
|
|
4132
|
+
}
|
|
4133
|
+
return rcEventWriteResultFromAnswer(answer);
|
|
4134
|
+
}
|
|
4135
|
+
async function setRcSessionModel(deps, sessionId, model) {
|
|
4136
|
+
if (model === "") {
|
|
4137
|
+
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" };
|
|
4138
|
+
}
|
|
4139
|
+
const credential = deps.credentialOf(sessionId);
|
|
4140
|
+
if (credential === void 0) {
|
|
4141
|
+
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)` };
|
|
4142
|
+
}
|
|
4143
|
+
if (credential.authorization === void 0) {
|
|
4144
|
+
return noOAuthCredentialResult(sessionId);
|
|
4145
|
+
}
|
|
4146
|
+
const body = JSON.stringify(buildRcEventWriteBody(buildRcSetModelPayload(deps.newUuid(), model)));
|
|
4147
|
+
let answer;
|
|
4148
|
+
try {
|
|
4149
|
+
answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
|
|
4150
|
+
} catch (error) {
|
|
4151
|
+
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)}` };
|
|
4152
|
+
}
|
|
4153
|
+
return rcEventWriteResultFromAnswer(answer);
|
|
4154
|
+
}
|
|
4155
|
+
async function setRcSessionPermissionMode(deps, sessionId, mode) {
|
|
4156
|
+
const credential = deps.credentialOf(sessionId);
|
|
4157
|
+
if (credential === void 0) {
|
|
4158
|
+
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)` };
|
|
4159
|
+
}
|
|
4160
|
+
if (credential.authorization === void 0) {
|
|
4161
|
+
return noOAuthCredentialResult(sessionId);
|
|
4162
|
+
}
|
|
4163
|
+
const body = JSON.stringify(buildRcEventWriteBody(buildRcSetPermissionModePayload(deps.newUuid(), mode)));
|
|
4164
|
+
let answer;
|
|
4165
|
+
try {
|
|
4166
|
+
answer = await deps.dial.writeEvents(sessionId, rcWriteHeaders(credential), body);
|
|
4167
|
+
} catch (error) {
|
|
4168
|
+
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)}` };
|
|
4169
|
+
}
|
|
4170
|
+
return rcEventWriteResultFromAnswer(answer);
|
|
4171
|
+
}
|
|
4097
4172
|
|
|
4098
4173
|
// src/frontdoor/rcControl.ts
|
|
4099
4174
|
var https2 = __toESM(require("node:https"), 1);
|
|
4175
|
+
var RC_PERMISSION_MODE_LIST = RC_PERMISSION_MODES.join(", ");
|
|
4100
4176
|
var CONTROL_PATH_PREFIX = "/__agent-shim/rc";
|
|
4101
4177
|
var CONTROL_BODY_CAP_BYTES = 10485760;
|
|
4102
4178
|
function answerJson(response, status, body) {
|
|
@@ -4131,6 +4207,9 @@ function createRcControlHandler(deps) {
|
|
|
4131
4207
|
const pendingPath = `${CONTROL_PATH_PREFIX}/pending`;
|
|
4132
4208
|
const injectPath = `${CONTROL_PATH_PREFIX}/inject`;
|
|
4133
4209
|
const answerPath = `${CONTROL_PATH_PREFIX}/answer`;
|
|
4210
|
+
const interruptPath = `${CONTROL_PATH_PREFIX}/interrupt`;
|
|
4211
|
+
const setModelPath = `${CONTROL_PATH_PREFIX}/set-model`;
|
|
4212
|
+
const setPermissionModePath = `${CONTROL_PATH_PREFIX}/set-permission-mode`;
|
|
4134
4213
|
return (request4, response) => {
|
|
4135
4214
|
const presented = request4.headers.authorization;
|
|
4136
4215
|
const bearerPrefix = "bearer ".length;
|
|
@@ -4215,6 +4294,78 @@ function createRcControlHandler(deps) {
|
|
|
4215
4294
|
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
4216
4295
|
return;
|
|
4217
4296
|
}
|
|
4297
|
+
if (url.pathname === interruptPath) {
|
|
4298
|
+
if (request4.method !== "POST") {
|
|
4299
|
+
answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the interrupt operation is a POST" });
|
|
4300
|
+
return;
|
|
4301
|
+
}
|
|
4302
|
+
let parsed;
|
|
4303
|
+
try {
|
|
4304
|
+
parsed = JSON.parse(await readBody(request4));
|
|
4305
|
+
} catch {
|
|
4306
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body is not readable JSON within the control body cap" });
|
|
4307
|
+
return;
|
|
4308
|
+
}
|
|
4309
|
+
if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || typeof parsed.session !== "string" || parsed.session === "") {
|
|
4310
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: "the interrupt body must be JSON naming a non-empty session id" });
|
|
4311
|
+
return;
|
|
4312
|
+
}
|
|
4313
|
+
const result = await deps.interrupt(parsed.session);
|
|
4314
|
+
if (result.ok) {
|
|
4315
|
+
answerJson(response, HTTP_STATUS.ok, { session: parsed.session, sequenceNums: result.sequenceNums });
|
|
4316
|
+
return;
|
|
4317
|
+
}
|
|
4318
|
+
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
4319
|
+
return;
|
|
4320
|
+
}
|
|
4321
|
+
if (url.pathname === setModelPath) {
|
|
4322
|
+
if (request4.method !== "POST") {
|
|
4323
|
+
answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-model operation is a POST" });
|
|
4324
|
+
return;
|
|
4325
|
+
}
|
|
4326
|
+
let parsed;
|
|
4327
|
+
try {
|
|
4328
|
+
parsed = JSON.parse(await readBody(request4));
|
|
4329
|
+
} catch {
|
|
4330
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body is not readable JSON within the control body cap" });
|
|
4331
|
+
return;
|
|
4332
|
+
}
|
|
4333
|
+
if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("model" in parsed) || typeof parsed.session !== "string" || typeof parsed.model !== "string" || parsed.session === "" || parsed.model === "") {
|
|
4334
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: "the set-model body must be JSON naming a non-empty session id and model id" });
|
|
4335
|
+
return;
|
|
4336
|
+
}
|
|
4337
|
+
const result = await deps.setModel(parsed.session, parsed.model);
|
|
4338
|
+
if (result.ok) {
|
|
4339
|
+
answerJson(response, HTTP_STATUS.ok, { session: parsed.session, model: parsed.model, sequenceNums: result.sequenceNums });
|
|
4340
|
+
return;
|
|
4341
|
+
}
|
|
4342
|
+
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
4343
|
+
return;
|
|
4344
|
+
}
|
|
4345
|
+
if (url.pathname === setPermissionModePath) {
|
|
4346
|
+
if (request4.method !== "POST") {
|
|
4347
|
+
answerJson(response, HTTP_STATUS.methodNotAllowed, { error: "the set-permission-mode operation is a POST" });
|
|
4348
|
+
return;
|
|
4349
|
+
}
|
|
4350
|
+
let parsed;
|
|
4351
|
+
try {
|
|
4352
|
+
parsed = JSON.parse(await readBody(request4));
|
|
4353
|
+
} catch {
|
|
4354
|
+
answerJson(response, HTTP_STATUS.badRequest, { error: "the set-permission-mode body is not readable JSON within the control body cap" });
|
|
4355
|
+
return;
|
|
4356
|
+
}
|
|
4357
|
+
if (typeof parsed !== "object" || parsed === null || !("session" in parsed) || !("mode" in parsed) || typeof parsed.session !== "string" || parsed.session === "" || !isRcPermissionMode(parsed.mode)) {
|
|
4358
|
+
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})` });
|
|
4359
|
+
return;
|
|
4360
|
+
}
|
|
4361
|
+
const result = await deps.setPermissionMode(parsed.session, parsed.mode);
|
|
4362
|
+
if (result.ok) {
|
|
4363
|
+
answerJson(response, HTTP_STATUS.ok, { session: parsed.session, mode: parsed.mode, sequenceNums: result.sequenceNums });
|
|
4364
|
+
return;
|
|
4365
|
+
}
|
|
4366
|
+
answerJson(response, HTTP_STATUS.badGateway, { error: result.message });
|
|
4367
|
+
return;
|
|
4368
|
+
}
|
|
4218
4369
|
answerJson(response, HTTP_STATUS.notFound, { error: `no such control route: ${url.pathname}` });
|
|
4219
4370
|
};
|
|
4220
4371
|
handle().catch((error) => {
|
|
@@ -4429,7 +4580,10 @@ function frontDoorRcControl(transport, token) {
|
|
|
4429
4580
|
`${CONTROL_PATH_PREFIX}/answer`,
|
|
4430
4581
|
JSON.stringify({ session: sessionId, request: requestId, approve: decision.approve, ...decision.approve || decision.message === void 0 ? {} : { text: decision.message } }),
|
|
4431
4582
|
(status, message) => `answering control request ${requestId} on ${sessionId} failed (HTTP ${String(status)}): ${message}`
|
|
4432
|
-
)
|
|
4583
|
+
),
|
|
4584
|
+
interruptSession: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/interrupt`, JSON.stringify({ session: sessionId }), (status, message) => `interrupting ${sessionId} failed (HTTP ${String(status)}): ${message}`),
|
|
4585
|
+
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}`),
|
|
4586
|
+
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}`)
|
|
4433
4587
|
};
|
|
4434
4588
|
}
|
|
4435
4589
|
|
|
@@ -9379,6 +9533,7 @@ function prepareClaudeLaunch(options) {
|
|
|
9379
9533
|
ProviderSchema,
|
|
9380
9534
|
RC_IDLE_EXPIRY_MS,
|
|
9381
9535
|
RC_PENDING_DEADLINE_MS,
|
|
9536
|
+
RC_PERMISSION_MODES,
|
|
9382
9537
|
RC_REQUEST_PARSE_CAP_BYTES,
|
|
9383
9538
|
RC_SESSIONS_PATH_PREFIX,
|
|
9384
9539
|
ROUTED_PATH_PREFIX,
|
|
@@ -9395,6 +9550,9 @@ function prepareClaudeLaunch(options) {
|
|
|
9395
9550
|
buildLayoutPaths,
|
|
9396
9551
|
buildRcControlResponsePayload,
|
|
9397
9552
|
buildRcEventWriteBody,
|
|
9553
|
+
buildRcInterruptPayload,
|
|
9554
|
+
buildRcSetModelPayload,
|
|
9555
|
+
buildRcSetPermissionModePayload,
|
|
9398
9556
|
buildRcUserMessagePayload,
|
|
9399
9557
|
carryOver,
|
|
9400
9558
|
checkReportHasWarnings,
|
|
@@ -9419,8 +9577,10 @@ function prepareClaudeLaunch(options) {
|
|
|
9419
9577
|
generateCa,
|
|
9420
9578
|
identityExists,
|
|
9421
9579
|
injectRcUserMessage,
|
|
9580
|
+
interruptRcSession,
|
|
9422
9581
|
isIdentityDirectoryName,
|
|
9423
9582
|
isInterceptedHost,
|
|
9583
|
+
isRcPermissionMode,
|
|
9424
9584
|
listDirectoryRules,
|
|
9425
9585
|
listIdentities,
|
|
9426
9586
|
listProfiles,
|
|
@@ -9476,6 +9636,8 @@ function prepareClaudeLaunch(options) {
|
|
|
9476
9636
|
setProfileEntries,
|
|
9477
9637
|
setProfileLaunchFlags,
|
|
9478
9638
|
setProfileMetadata,
|
|
9639
|
+
setRcSessionModel,
|
|
9640
|
+
setRcSessionPermissionMode,
|
|
9479
9641
|
snapshotPath,
|
|
9480
9642
|
startConnectServer,
|
|
9481
9643
|
topLevelNames,
|
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
|
|
|
@@ -9194,6 +9340,7 @@ export {
|
|
|
9194
9340
|
ProviderSchema,
|
|
9195
9341
|
RC_IDLE_EXPIRY_MS,
|
|
9196
9342
|
RC_PENDING_DEADLINE_MS,
|
|
9343
|
+
RC_PERMISSION_MODES,
|
|
9197
9344
|
RC_REQUEST_PARSE_CAP_BYTES,
|
|
9198
9345
|
RC_SESSIONS_PATH_PREFIX,
|
|
9199
9346
|
ROUTED_PATH_PREFIX,
|
|
@@ -9210,6 +9357,9 @@ export {
|
|
|
9210
9357
|
buildLayoutPaths,
|
|
9211
9358
|
buildRcControlResponsePayload,
|
|
9212
9359
|
buildRcEventWriteBody,
|
|
9360
|
+
buildRcInterruptPayload,
|
|
9361
|
+
buildRcSetModelPayload,
|
|
9362
|
+
buildRcSetPermissionModePayload,
|
|
9213
9363
|
buildRcUserMessagePayload,
|
|
9214
9364
|
carryOver,
|
|
9215
9365
|
checkReportHasWarnings,
|
|
@@ -9234,8 +9384,10 @@ export {
|
|
|
9234
9384
|
generateCa,
|
|
9235
9385
|
identityExists,
|
|
9236
9386
|
injectRcUserMessage,
|
|
9387
|
+
interruptRcSession,
|
|
9237
9388
|
isIdentityDirectoryName,
|
|
9238
9389
|
isInterceptedHost,
|
|
9390
|
+
isRcPermissionMode,
|
|
9239
9391
|
listDirectoryRules,
|
|
9240
9392
|
listIdentities,
|
|
9241
9393
|
listProfiles,
|
|
@@ -9291,6 +9443,8 @@ export {
|
|
|
9291
9443
|
setProfileEntries,
|
|
9292
9444
|
setProfileLaunchFlags,
|
|
9293
9445
|
setProfileMetadata,
|
|
9446
|
+
setRcSessionModel,
|
|
9447
|
+
setRcSessionPermissionMode,
|
|
9294
9448
|
snapshotPath,
|
|
9295
9449
|
startConnectServer,
|
|
9296
9450
|
topLevelNames,
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1662,11 +1662,11 @@ interface FrontDoorRoute {
|
|
|
1662
1662
|
}
|
|
1663
1663
|
|
|
1664
1664
|
/**
|
|
1665
|
-
* Remote Control session tracking and the client-half writes the front door performs: the record of the `cse_` sessions whose traffic crosses its terminated API-host session, the prompt injected into one,
|
|
1665
|
+
* Remote Control session tracking and the client-half writes the front door performs: the record of the `cse_` sessions whose traffic crosses its terminated API-host session, the prompt injected into one, the answer delivered to one of its pending control requests, and the client-originated control requests sent into one (interrupt, set_model, set_permission_mode).
|
|
1666
1666
|
*
|
|
1667
1667
|
* The tracker is observational: the door already terminates the API host's TLS, so it sees the CLI's own `POST /v1/code/sessions` (whose answer carries the new session's `cse_` id) and every recurring `/v1/code/sessions/...` call (heartbeat, presence), each carrying the session's Authorization bearer and the protocol headers a client-half replay needs. Per session the tracker retains the latest of each, because those calls recur and the freshest value is the live one. It also reads the request bodies of the exchanges whose payloads matter: the worker's event batches (whose `control_request` payloads, `can_use_tool` above all, are the approvals awaiting an answer), the worker heartbeats (whose bodies carry `idle_seconds`), the worker registration (whose body carries the worker state), and the client-half event writes (whose `control_response` payloads complete a pending request). Everything is held in this process's memory only: nothing here is written to a log, to the capture, or to disk, and the summary the control surface lists never includes the credential. The capture's existing redaction is untouched; a capture and this tracker observe the same exchanges through entirely separate paths.
|
|
1668
1668
|
*
|
|
1669
|
-
*
|
|
1669
|
+
* Every write replays the protocol's documented client half rather than splicing frames into any relayed stream: `POST /v1/code/sessions/{id}/events` with one payload event (a user message, a `control_response` answering an observed request by echoing its id, or a `control_request` a client originates), authenticated by the observed bearer and the observed protocol headers, sent from the door over its own interception-proof dials. Sequence numbers therefore come from the real service and the event is mirrored to every attached client, exactly as a message typed into claude.ai would be.
|
|
1670
1670
|
*/
|
|
1671
1671
|
/** Every Remote Control exchange on the API host sits under this path prefix. */
|
|
1672
1672
|
declare const RC_SESSIONS_PATH_PREFIX = "/v1/code/sessions";
|
|
@@ -1819,6 +1819,26 @@ interface RcAnswerDecision {
|
|
|
1819
1819
|
* Builds the `control_response` payload event for one answer: the SDK's response envelope, `subtype: "success"` with the request's id echoed in `request_id`, carrying the permission result the worker is waiting for (`behavior: "allow"`, or `"deny"` with its message).
|
|
1820
1820
|
*/
|
|
1821
1821
|
declare function buildRcControlResponsePayload(requestId: string, decision: RcAnswerDecision): Record<string, unknown>;
|
|
1822
|
+
/**
|
|
1823
|
+
* The permission modes the SDK's own `set_permission_mode` control request accepts: exactly the strings its `PermissionMode` type declares (`@anthropic-ai/claude-agent-sdk` `sdk.d.ts`), nothing looser. A runtime list as well as a type, because the CLI and the control route both narrow untrusted strings through it before a write is ever attempted.
|
|
1824
|
+
*/
|
|
1825
|
+
declare const RC_PERMISSION_MODES: readonly ["default", "acceptEdits", "bypassPermissions", "plan", "dontAsk", "auto"];
|
|
1826
|
+
/** One permission mode the SDK's `set_permission_mode` control request accepts. */
|
|
1827
|
+
type RcPermissionMode = (typeof RC_PERMISSION_MODES)[number];
|
|
1828
|
+
/** Whether a value is one of the SDK's own permission modes, the narrowing the CLI and the control route apply to untrusted input. */
|
|
1829
|
+
declare function isRcPermissionMode(value: unknown): value is RcPermissionMode;
|
|
1830
|
+
/**
|
|
1831
|
+
* Builds the `control_request` payload event for one interrupt: the SDK's request envelope (`SDKControlRequest`: `type`, `request_id`, `request`), carrying the `interrupt` subtype's own shape (`subtype` alone; its optional `cancel_queued` is absent, which the SDK reads as false, so queued commands survive the interrupt exactly as a plain stop button's would). The request id is the caller's minted uuid, the id the worker's `control_response` echoes.
|
|
1832
|
+
*/
|
|
1833
|
+
declare function buildRcInterruptPayload(requestId: string): Record<string, unknown>;
|
|
1834
|
+
/**
|
|
1835
|
+
* Builds the `control_request` payload event for one model switch: the SDK's request envelope carrying the `set_model` subtype's own shape, whose one field is the `model` id string (its omitted and null forms mean a reset to the session default, which this builder never sends; the operation refuses an empty id for the same reason).
|
|
1836
|
+
*/
|
|
1837
|
+
declare function buildRcSetModelPayload(requestId: string, model: string): Record<string, unknown>;
|
|
1838
|
+
/**
|
|
1839
|
+
* Builds the `control_request` payload event for one permission-mode change: the SDK's request envelope carrying the `set_permission_mode` subtype's own shape, whose one field is the `mode` from the SDK's own `PermissionMode` enum (`RC_PERMISSION_MODES`).
|
|
1840
|
+
*/
|
|
1841
|
+
declare function buildRcSetPermissionModePayload(requestId: string, mode: RcPermissionMode): Record<string, unknown>;
|
|
1822
1842
|
/** Everything one answer needs, injected so the decision logic runs against fakes in unit tests. */
|
|
1823
1843
|
interface RcAnswerDeps {
|
|
1824
1844
|
/** Reads the observed credential for a session: the tracker's accessor, in production. */
|
|
@@ -1834,6 +1854,27 @@ interface RcAnswerDeps {
|
|
|
1834
1854
|
* Answers one pending control request on an observed session: the same documented client-half write the inject path performs, carrying a `control_response` that echoes the request's id, authenticated by the observed bearer and protocol headers. The request must still be pending (an answered or deadline-expired request is refused verbosely, since writing a response nothing waits for would mislead the caller), and a successful write retires it. A dial that cannot reach the API host is reported rather than thrown.
|
|
1835
1855
|
*/
|
|
1836
1856
|
declare function answerRcControlRequest(deps: RcAnswerDeps, sessionId: string, requestId: string, decision: RcAnswerDecision): Promise<RcEventWriteResult>;
|
|
1857
|
+
/** Everything one client-originated control request needs, injected so the decision logic runs against fakes in unit tests. */
|
|
1858
|
+
interface RcControlRequestDeps {
|
|
1859
|
+
/** Reads the observed credential for a session: the tracker's accessor, in production. */
|
|
1860
|
+
readonly credentialOf: (sessionId: string) => RcObservedCredential | undefined;
|
|
1861
|
+
/** The dial to the real API host. */
|
|
1862
|
+
readonly dial: RcEventDial;
|
|
1863
|
+
/** Mints the request's id: a v4 UUID or better in production, the id the worker's `control_response` echoes. */
|
|
1864
|
+
readonly newUuid: () => string;
|
|
1865
|
+
}
|
|
1866
|
+
/**
|
|
1867
|
+
* Interrupts one observed session's running turn: the same documented client-half write the inject and answer paths perform, carrying the SDK's `control_request` envelope for the `interrupt` subtype, authenticated by the observed bearer and protocol headers. The result is the sequence numbers the real service assigned, or a verbose failure. A dial that cannot reach the API host is reported rather than thrown, so a caller with no exception handling still shows the user exactly what failed.
|
|
1868
|
+
*/
|
|
1869
|
+
declare function interruptRcSession(deps: RcControlRequestDeps, sessionId: string): Promise<RcEventWriteResult>;
|
|
1870
|
+
/**
|
|
1871
|
+
* Sets the model one observed session's subsequent turns use: the same documented client-half write the inject and answer paths perform, carrying the SDK's `control_request` envelope for the `set_model` subtype with the model id its own field names. The id is validated only as non-empty, because the SDK's field gives the empty-free forms their own meaning (omitted or null resets to the session default) and this operation sends neither. The result is the sequence numbers the real service assigned, or a verbose failure.
|
|
1872
|
+
*/
|
|
1873
|
+
declare function setRcSessionModel(deps: RcControlRequestDeps, sessionId: string, model: string): Promise<RcEventWriteResult>;
|
|
1874
|
+
/**
|
|
1875
|
+
* Sets one observed session's permission mode: the same documented client-half write the inject and answer paths perform, carrying the SDK's `control_request` envelope for the `set_permission_mode` subtype with a mode from the SDK's own enum (`RC_PERMISSION_MODES`; the type accepts nothing looser, and the CLI and control route narrow untrusted strings through `isRcPermissionMode` before they ever reach this operation). The result is the sequence numbers the real service assigned, or a verbose failure.
|
|
1876
|
+
*/
|
|
1877
|
+
declare function setRcSessionPermissionMode(deps: RcControlRequestDeps, sessionId: string, mode: RcPermissionMode): Promise<RcEventWriteResult>;
|
|
1837
1878
|
|
|
1838
1879
|
/** Where the Remote Control inject path dials: the API host's own address by default, over TLS with the door's interception-proof agent (real-name DNS resolution and the pf-exempt source-port range), so an injected event cannot be looped back into the door's own transparent surface. A test redirects the dial at a local stand-in presenting the host's name, exactly as the forwarding targets are redirected. */
|
|
1839
1880
|
interface RcDialTarget {
|
|
@@ -1853,9 +1894,9 @@ declare function realRcEventDial(target?: RcDialTarget): RcEventDial;
|
|
|
1853
1894
|
declare function realConnectEffects(): ConnectEffects;
|
|
1854
1895
|
|
|
1855
1896
|
/**
|
|
1856
|
-
* The Remote Control control surface: the small HTTP namespace the serving door answers on its provider listener, next to `/healthz`, through which `agent-shim frontdoor rc` lists the observed sessions, reports each one's status and pending control requests, injects a prompt into one,
|
|
1897
|
+
* The Remote Control control surface: the small HTTP namespace the serving door answers on its provider listener, next to `/healthz`, through which `agent-shim frontdoor rc` lists the observed sessions, reports each one's status and pending control requests, injects a prompt into one, answers one of its pending requests, and sends its own control requests into one (interrupt, set-model, set-permission-mode).
|
|
1857
1898
|
*
|
|
1858
|
-
* This rides the listener `frontdoor status` already knows the door by (its address comes from the same state file, its trust anchor from the same CA path) rather than any second channel. The routes sit under one prefix the routed pipeline never serves, and every request must present the serving generation's control token as a Bearer credential: a fresh random value per door start, written owner-only under the front door's state directory so only this user's CLI can read it, and checked in constant time like every other capability the door accepts. An inject or
|
|
1899
|
+
* This rides the listener `frontdoor status` already knows the door by (its address comes from the same state file, its trust anchor from the same CA path) rather than any second channel. The routes sit under one prefix the routed pipeline never serves, and every request must present the serving generation's control token as a Bearer credential: a fresh random value per door start, written owner-only under the front door's state directory so only this user's CLI can read it, and checked in constant time like every other capability the door accepts. An inject, answer or control request is carried out by the door itself over its interception-proof dials, which is the whole point: the CLI never dials the API host directly, so nothing about the write depends on the caller's own network path.
|
|
1859
1900
|
*/
|
|
1860
1901
|
/** The one path prefix every Remote Control control route sits under, answered before the routed pipeline like `/healthz` is. */
|
|
1861
1902
|
declare const CONTROL_PATH_PREFIX = "/__agent-shim/rc";
|
|
@@ -1873,6 +1914,12 @@ interface RcControlHandlerDeps {
|
|
|
1873
1914
|
readonly inject: (sessionId: string, text: string) => Promise<RcEventWriteResult>;
|
|
1874
1915
|
/** The answer operation, already wired to the tracker and the door's API-host dial. */
|
|
1875
1916
|
readonly answer: (sessionId: string, requestId: string, decision: RcAnswerDecision) => Promise<RcEventWriteResult>;
|
|
1917
|
+
/** The interrupt operation, already wired to the tracker and the door's API-host dial. */
|
|
1918
|
+
readonly interrupt: (sessionId: string) => Promise<RcEventWriteResult>;
|
|
1919
|
+
/** The set-model operation, already wired to the tracker and the door's API-host dial. */
|
|
1920
|
+
readonly setModel: (sessionId: string, model: string) => Promise<RcEventWriteResult>;
|
|
1921
|
+
/** The set-permission-mode operation, already wired to the tracker and the door's API-host dial. */
|
|
1922
|
+
readonly setPermissionMode: (sessionId: string, mode: RcPermissionMode) => Promise<RcEventWriteResult>;
|
|
1876
1923
|
}
|
|
1877
1924
|
/**
|
|
1878
1925
|
* Builds the control handler the provider listener serves under `CONTROL_PATH_PREFIX`. Every route demands the generation's control token first, so nothing about the sessions (their ids, their timing, and most of all the prompts and approvals carried into them) is reachable by a process that did not read the owner-only token file.
|
|
@@ -1894,7 +1941,7 @@ interface RcControlTransport {
|
|
|
1894
1941
|
}
|
|
1895
1942
|
/** The real transport: HTTPS to the door's provider listener on loopback, trusting only the CA file the door's own state names, so a process merely holding the port cannot answer as the door. */
|
|
1896
1943
|
declare function realRcControlTransport(port: number, ca: string): RcControlTransport;
|
|
1897
|
-
/** The door-facing client the `frontdoor rc` verbs use: the session list, the status and pending reads,
|
|
1944
|
+
/** The door-facing client the `frontdoor rc` verbs use: the session list, the status and pending reads, the inject and answer operations, and the door's own control requests (interrupt, set-model, set-permission-mode), over the injected transport. */
|
|
1898
1945
|
interface FrontDoorRcControl {
|
|
1899
1946
|
/** The observed sessions, oldest-created first. Throws with the door's verbose reason when the list cannot be had. */
|
|
1900
1947
|
readonly listSessions: () => Promise<readonly RcSessionSummary[]>;
|
|
@@ -1906,6 +1953,12 @@ interface FrontDoorRcControl {
|
|
|
1906
1953
|
readonly sendPrompt: (sessionId: string, text: string) => Promise<RcEventWriteResult>;
|
|
1907
1954
|
/** Answers one pending control request, returning the sequence numbers the real service assigned or the verbose failure. */
|
|
1908
1955
|
readonly answerRequest: (sessionId: string, requestId: string, decision: RcAnswerDecision) => Promise<RcEventWriteResult>;
|
|
1956
|
+
/** Interrupts one session's running turn, returning the sequence numbers the real service assigned or the verbose failure. */
|
|
1957
|
+
readonly interruptSession: (sessionId: string) => Promise<RcEventWriteResult>;
|
|
1958
|
+
/** Sets the model one session's subsequent turns use, returning the sequence numbers the real service assigned or the verbose failure. */
|
|
1959
|
+
readonly setModel: (sessionId: string, model: string) => Promise<RcEventWriteResult>;
|
|
1960
|
+
/** Sets one session's permission mode, returning the sequence numbers the real service assigned or the verbose failure. */
|
|
1961
|
+
readonly setPermissionMode: (sessionId: string, mode: RcPermissionMode) => Promise<RcEventWriteResult>;
|
|
1909
1962
|
}
|
|
1910
1963
|
/** Builds the control client: every request presents the control token, and a non-2xx answer becomes the verbose message the door sent with it. */
|
|
1911
1964
|
declare function frontDoorRcControl(transport: RcControlTransport, token: string): FrontDoorRcControl;
|
|
@@ -3131,5 +3184,5 @@ interface PrepareClaudeLaunchOptions {
|
|
|
3131
3184
|
*/
|
|
3132
3185
|
declare function prepareClaudeLaunch(options: PrepareClaudeLaunchOptions): LaunchPlan;
|
|
3133
3186
|
|
|
3134
|
-
export { AMBIENT_CREDENTIAL_VARS, CONNECT_INTERCEPT_HOST, CONNECT_INTERCEPT_HOSTS, CONNECT_LIMITS, CONTROL_PATH_PREFIX, CategoryClassificationOverlaySchema, CategoryClassificationSchema, CategoryMapSchema, CliError, ConfigProfileSchema, CredentialCacheSchema, CredentialSchema, CredentialSourceSchema, DirectoryRuleAlreadyExistsError, DirectoryRuleMissingTargetError, DirectoryRuleNotFoundError, DirectoryRuleSchema, DirectoryRulesSchema, EXIT_FAILURE, EXIT_USAGE, EntryValueSchema, FARM_MANIFEST_FILENAME, FrontDoorStartError, GlobalConfigSchema, HeadroomStartError, IdentityAlreadyExistsError, IdentityNotFoundError, IdentitySchema, InvalidCategoryNameError, InvalidIdentityNameError, InvalidProviderNameError, LaunchRefusedError, LegacyProviderFileError, PoolNameSchema, PoolNotFoundError, PoolSchema, PortableConfigSchema, ProfileAlreadyExistsError, ProfileNotFoundError, ProviderAlreadyExistsError, ProviderKindMismatchError, ProviderNotFoundError, ProviderSchema, RC_IDLE_EXPIRY_MS, RC_PENDING_DEADLINE_MS, RC_REQUEST_PARSE_CAP_BYTES, RC_SESSIONS_PATH_PREFIX, ROUTED_PATH_PREFIX, UsageError, UsageSnapshotError, UsageSnapshotSchema, WhenSchema, addDirectoryRule, addIdentity, addPool, addProvider, answerRcControlRequest, buildEntryFacts, buildLayoutPaths, buildRcControlResponsePayload, buildRcEventWriteBody, buildRcUserMessagePayload, carryOver, checkReportHasWarnings, checkReportToJson, collectCheckReport, collectDoctorReport, createLeafCache, createProfile, createRcControlHandler, createRcSessionTracker, describeProviderEndpoint, detectAmbientCredential, ensureCa, ensureFrontDoor, ensureHeadroom, evaluateAmbientCredentialGuard, formatAmbientCredentialGuardMessage, formatCheckReport, formatDoctorReport, forwardableHeaders, frontDoorRcControl, generateCa, identityExists, injectRcUserMessage, isIdentityDirectoryName, isInterceptedHost, listDirectoryRules, listIdentities, listProfiles, listProviders, listUsageSnapshots, mintLeaf, observingRoutedRoute, parseConnectTarget, prepareClaudeLaunch, prepareLaunch, profileExists, providerExists, rcEventWriteResultFromAnswer, rcObserverAsPassthrough, readActiveIdentity, readDirectoryRules, readFarmManifest, readGlobalConfig, readIdentity, readPools, readProfile, readProvider, readUsageSnapshot, realConnectCertStore, realConnectEffects, realRcControlTransport, realRcEventDial, recoverFarm, recoveryDiagnostics, removeDirectoryRule, removeIdentity, removePool, removeProfile, removeProvider, requirePool, resolveAgentShimHome, resolveClaudeHome, resolveDecisions, resolveLayoutPaths, resolveSupervisorConfig, resyncFarm, runCheck, runDoctor, runFrontDoorSupervisor, runSupervisor, servedByPipeline, setAllowAmbientCredential, setDefaultConfigProfile, setGlobalDefaultProfile, setIdentityCredential, setPool, setProfileCategories, setProfileEntries, setProfileLaunchFlags, setProfileMetadata, snapshotPath, startConnectServer, topLevelNames, updateDirectoryRule, updateProvider, useIdentity, writeDirectoryRules };
|
|
3135
|
-
export type { AddDirectoryRuleOptions, AddProviderInput, AmbientCredentialDetection, AmbientCredentialGuardResult, AmbientCredentialVar, BuildEntryFactsParams, CaMaterial, CarryOverParams, CarryOverResult, CategoryMap, CheckReport, CollectCheckReportParams, CollectDoctorReportParams, CompiledRule, ConfigProfile, ConnectCertStore, ConnectEffects, ConnectLimits, ConnectServerConfig, ConnectServerHandle, ConnectTarget, Credential, CredentialSource, Decision, Diagnostic, DirectoryRule, DirectoryRules, DoctorReport, EliminatedRule, EnsureFrontDoorPorts, EnsureHeadroomPorts, EntryFact, EntryFacts, EntryValue, EvaluateAmbientCredentialGuardParams, FlattenedCascade, FrontDoorRcControl, FrontDoorSupervisorPorts, GlobalConfig, HeadroomSupervisorConfig, Identity, IdentityCredentialChange, IdentityListEntry, IdentityListing, InjectedCredential, LaunchPlan, Layer, LayerId, LayoutPaths, LeafCert, Pool, PortableConfig, PrepareClaudeLaunchOptions, PrepareLaunchParams, ProfileListEntry, Provider, ProviderListEntry, RcAnswerDecision, RcAnswerDeps, RcControlAnswer, RcControlHandlerDeps, RcControlTransport, RcDialAnswer, RcDialTarget, RcEventDial, RcEventWriteResult, RcExchangeObserver, RcInjectDeps, RcObservedCredential, RcObservedRequest, RcPendingRequestSummary, RcSessionStatus, RcSessionSummary, RcSessionTracker, RcSessionTrackerDeps, RcWorkerFact, RcWriteHeaders, RecoverFarmParams, RecoveryResult, ResolveDecisionsInput, ResolvedState, ResyncFarmParams, ResyncFarmResult, RunCheckParams, RunDoctorParams, RunFrontDoorSupervisorOptions, RunSupervisorOptions, SupervisorPorts, UnreadableIdentityListEntry, UpdateDirectoryRuleOptions, UpdateProviderInput, UsageReadFs, UsageSnapshot, WhenCondition };
|
|
3187
|
+
export { AMBIENT_CREDENTIAL_VARS, CONNECT_INTERCEPT_HOST, CONNECT_INTERCEPT_HOSTS, CONNECT_LIMITS, CONTROL_PATH_PREFIX, CategoryClassificationOverlaySchema, CategoryClassificationSchema, CategoryMapSchema, CliError, ConfigProfileSchema, CredentialCacheSchema, CredentialSchema, CredentialSourceSchema, DirectoryRuleAlreadyExistsError, DirectoryRuleMissingTargetError, DirectoryRuleNotFoundError, DirectoryRuleSchema, DirectoryRulesSchema, EXIT_FAILURE, EXIT_USAGE, EntryValueSchema, FARM_MANIFEST_FILENAME, FrontDoorStartError, GlobalConfigSchema, HeadroomStartError, IdentityAlreadyExistsError, IdentityNotFoundError, IdentitySchema, InvalidCategoryNameError, InvalidIdentityNameError, InvalidProviderNameError, LaunchRefusedError, LegacyProviderFileError, PoolNameSchema, PoolNotFoundError, PoolSchema, PortableConfigSchema, ProfileAlreadyExistsError, ProfileNotFoundError, ProviderAlreadyExistsError, ProviderKindMismatchError, ProviderNotFoundError, ProviderSchema, RC_IDLE_EXPIRY_MS, RC_PENDING_DEADLINE_MS, RC_PERMISSION_MODES, RC_REQUEST_PARSE_CAP_BYTES, RC_SESSIONS_PATH_PREFIX, ROUTED_PATH_PREFIX, UsageError, UsageSnapshotError, UsageSnapshotSchema, WhenSchema, addDirectoryRule, addIdentity, addPool, addProvider, answerRcControlRequest, buildEntryFacts, buildLayoutPaths, buildRcControlResponsePayload, buildRcEventWriteBody, buildRcInterruptPayload, buildRcSetModelPayload, buildRcSetPermissionModePayload, buildRcUserMessagePayload, carryOver, checkReportHasWarnings, checkReportToJson, collectCheckReport, collectDoctorReport, createLeafCache, createProfile, createRcControlHandler, createRcSessionTracker, describeProviderEndpoint, detectAmbientCredential, ensureCa, ensureFrontDoor, ensureHeadroom, evaluateAmbientCredentialGuard, formatAmbientCredentialGuardMessage, formatCheckReport, formatDoctorReport, forwardableHeaders, frontDoorRcControl, generateCa, identityExists, injectRcUserMessage, interruptRcSession, isIdentityDirectoryName, isInterceptedHost, isRcPermissionMode, listDirectoryRules, listIdentities, listProfiles, listProviders, listUsageSnapshots, mintLeaf, observingRoutedRoute, parseConnectTarget, prepareClaudeLaunch, prepareLaunch, profileExists, providerExists, rcEventWriteResultFromAnswer, rcObserverAsPassthrough, readActiveIdentity, readDirectoryRules, readFarmManifest, readGlobalConfig, readIdentity, readPools, readProfile, readProvider, readUsageSnapshot, realConnectCertStore, realConnectEffects, realRcControlTransport, realRcEventDial, recoverFarm, recoveryDiagnostics, removeDirectoryRule, removeIdentity, removePool, removeProfile, removeProvider, requirePool, resolveAgentShimHome, resolveClaudeHome, resolveDecisions, resolveLayoutPaths, resolveSupervisorConfig, resyncFarm, runCheck, runDoctor, runFrontDoorSupervisor, runSupervisor, servedByPipeline, setAllowAmbientCredential, setDefaultConfigProfile, setGlobalDefaultProfile, setIdentityCredential, setPool, setProfileCategories, setProfileEntries, setProfileLaunchFlags, setProfileMetadata, setRcSessionModel, setRcSessionPermissionMode, snapshotPath, startConnectServer, topLevelNames, updateDirectoryRule, updateProvider, useIdentity, writeDirectoryRules };
|
|
3188
|
+
export type { AddDirectoryRuleOptions, AddProviderInput, AmbientCredentialDetection, AmbientCredentialGuardResult, AmbientCredentialVar, BuildEntryFactsParams, CaMaterial, CarryOverParams, CarryOverResult, CategoryMap, CheckReport, CollectCheckReportParams, CollectDoctorReportParams, CompiledRule, ConfigProfile, ConnectCertStore, ConnectEffects, ConnectLimits, ConnectServerConfig, ConnectServerHandle, ConnectTarget, Credential, CredentialSource, Decision, Diagnostic, DirectoryRule, DirectoryRules, DoctorReport, EliminatedRule, EnsureFrontDoorPorts, EnsureHeadroomPorts, EntryFact, EntryFacts, EntryValue, EvaluateAmbientCredentialGuardParams, FlattenedCascade, FrontDoorRcControl, FrontDoorSupervisorPorts, GlobalConfig, HeadroomSupervisorConfig, Identity, IdentityCredentialChange, IdentityListEntry, IdentityListing, InjectedCredential, LaunchPlan, Layer, LayerId, LayoutPaths, LeafCert, Pool, PortableConfig, PrepareClaudeLaunchOptions, PrepareLaunchParams, ProfileListEntry, Provider, ProviderListEntry, RcAnswerDecision, RcAnswerDeps, RcControlAnswer, RcControlHandlerDeps, RcControlRequestDeps, RcControlTransport, RcDialAnswer, RcDialTarget, RcEventDial, RcEventWriteResult, RcExchangeObserver, RcInjectDeps, RcObservedCredential, RcObservedRequest, RcPendingRequestSummary, RcPermissionMode, RcSessionStatus, RcSessionSummary, RcSessionTracker, RcSessionTrackerDeps, RcWorkerFact, RcWriteHeaders, RecoverFarmParams, RecoveryResult, ResolveDecisionsInput, ResolvedState, ResyncFarmParams, ResyncFarmResult, RunCheckParams, RunDoctorParams, RunFrontDoorSupervisorOptions, RunSupervisorOptions, SupervisorPorts, UnreadableIdentityListEntry, UpdateDirectoryRuleOptions, UpdateProviderInput, UsageReadFs, UsageSnapshot, WhenCondition };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-shim",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.6.0",
|
|
4
4
|
"description": "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Joseph Mearman <joseph@mearman.co.uk>",
|