happy-coder 0.8.0 → 0.9.0-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +598 -405
- package/dist/index.mjs +599 -406
- package/dist/lib.d.cts +77 -72
- package/dist/lib.d.mts +77 -72
- package/package.json +11 -9
package/dist/index.cjs
CHANGED
|
@@ -20,18 +20,18 @@ require('node:events');
|
|
|
20
20
|
require('socket.io-client');
|
|
21
21
|
var tweetnacl = require('tweetnacl');
|
|
22
22
|
require('expo-server-sdk');
|
|
23
|
-
var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
24
|
-
var node_http = require('node:http');
|
|
25
|
-
var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
26
|
-
var z = require('zod');
|
|
27
23
|
var child_process = require('child_process');
|
|
28
24
|
var util = require('util');
|
|
29
25
|
var crypto = require('crypto');
|
|
26
|
+
var z = require('zod');
|
|
30
27
|
var fastify = require('fastify');
|
|
31
28
|
var fastifyTypeProviderZod = require('fastify-type-provider-zod');
|
|
32
29
|
var os$1 = require('os');
|
|
33
30
|
var qrcode = require('qrcode-terminal');
|
|
34
31
|
var open = require('open');
|
|
32
|
+
var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
33
|
+
var node_http = require('node:http');
|
|
34
|
+
var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
35
35
|
var fs = require('fs');
|
|
36
36
|
|
|
37
37
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
@@ -923,16 +923,19 @@ async function streamToStdin(stream, stdin, abort) {
|
|
|
923
923
|
}
|
|
924
924
|
|
|
925
925
|
class Query {
|
|
926
|
-
constructor(childStdin, childStdout, processExitPromise) {
|
|
926
|
+
constructor(childStdin, childStdout, processExitPromise, canCallTool) {
|
|
927
927
|
this.childStdin = childStdin;
|
|
928
928
|
this.childStdout = childStdout;
|
|
929
929
|
this.processExitPromise = processExitPromise;
|
|
930
|
+
this.canCallTool = canCallTool;
|
|
930
931
|
this.readMessages();
|
|
931
932
|
this.sdkMessages = this.readSdkMessages();
|
|
932
933
|
}
|
|
933
934
|
pendingControlResponses = /* @__PURE__ */ new Map();
|
|
935
|
+
cancelControllers = /* @__PURE__ */ new Map();
|
|
934
936
|
sdkMessages;
|
|
935
937
|
inputStream = new Stream();
|
|
938
|
+
canCallTool;
|
|
936
939
|
/**
|
|
937
940
|
* Set an error on the stream
|
|
938
941
|
*/
|
|
@@ -977,6 +980,12 @@ class Query {
|
|
|
977
980
|
handler(controlResponse.response);
|
|
978
981
|
}
|
|
979
982
|
continue;
|
|
983
|
+
} else if (message.type === "control_request") {
|
|
984
|
+
await this.handleControlRequest(message);
|
|
985
|
+
continue;
|
|
986
|
+
} else if (message.type === "control_cancel_request") {
|
|
987
|
+
this.handleControlCancelRequest(message);
|
|
988
|
+
continue;
|
|
980
989
|
}
|
|
981
990
|
this.inputStream.enqueue(message);
|
|
982
991
|
} catch (e) {
|
|
@@ -989,6 +998,7 @@ class Query {
|
|
|
989
998
|
this.inputStream.error(error);
|
|
990
999
|
} finally {
|
|
991
1000
|
this.inputStream.done();
|
|
1001
|
+
this.cleanupControllers();
|
|
992
1002
|
rl.close();
|
|
993
1003
|
}
|
|
994
1004
|
}
|
|
@@ -1032,6 +1042,77 @@ class Query {
|
|
|
1032
1042
|
childStdin.write(JSON.stringify(sdkRequest) + "\n");
|
|
1033
1043
|
});
|
|
1034
1044
|
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Handle incoming control requests for tool permissions
|
|
1047
|
+
* Replicates the exact logic from the SDK's handleControlRequest method
|
|
1048
|
+
*/
|
|
1049
|
+
async handleControlRequest(request) {
|
|
1050
|
+
if (!this.childStdin) {
|
|
1051
|
+
logDebug("Cannot handle control request - no stdin available");
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
const controller = new AbortController();
|
|
1055
|
+
this.cancelControllers.set(request.request_id, controller);
|
|
1056
|
+
try {
|
|
1057
|
+
const response = await this.processControlRequest(request, controller.signal);
|
|
1058
|
+
const controlResponse = {
|
|
1059
|
+
type: "control_response",
|
|
1060
|
+
response: {
|
|
1061
|
+
subtype: "success",
|
|
1062
|
+
request_id: request.request_id,
|
|
1063
|
+
response
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
this.childStdin.write(JSON.stringify(controlResponse) + "\n");
|
|
1067
|
+
} catch (error) {
|
|
1068
|
+
const controlErrorResponse = {
|
|
1069
|
+
type: "control_response",
|
|
1070
|
+
response: {
|
|
1071
|
+
subtype: "error",
|
|
1072
|
+
request_id: request.request_id,
|
|
1073
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
this.childStdin.write(JSON.stringify(controlErrorResponse) + "\n");
|
|
1077
|
+
} finally {
|
|
1078
|
+
this.cancelControllers.delete(request.request_id);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Handle control cancel requests
|
|
1083
|
+
* Replicates the exact logic from the SDK's handleControlCancelRequest method
|
|
1084
|
+
*/
|
|
1085
|
+
handleControlCancelRequest(request) {
|
|
1086
|
+
const controller = this.cancelControllers.get(request.request_id);
|
|
1087
|
+
if (controller) {
|
|
1088
|
+
controller.abort();
|
|
1089
|
+
this.cancelControllers.delete(request.request_id);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Process control requests based on subtype
|
|
1094
|
+
* Replicates the exact logic from the SDK's processControlRequest method
|
|
1095
|
+
*/
|
|
1096
|
+
async processControlRequest(request, signal) {
|
|
1097
|
+
if (request.request.subtype === "can_use_tool") {
|
|
1098
|
+
if (!this.canCallTool) {
|
|
1099
|
+
throw new Error("canCallTool callback is not provided.");
|
|
1100
|
+
}
|
|
1101
|
+
return this.canCallTool(request.request.tool_name, request.request.input, {
|
|
1102
|
+
signal
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
throw new Error("Unsupported control request subtype: " + request.request.subtype);
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Cleanup method to abort all pending control requests
|
|
1109
|
+
*/
|
|
1110
|
+
cleanupControllers() {
|
|
1111
|
+
for (const [requestId, controller] of this.cancelControllers.entries()) {
|
|
1112
|
+
controller.abort();
|
|
1113
|
+
this.cancelControllers.delete(requestId);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1035
1116
|
}
|
|
1036
1117
|
function query(config) {
|
|
1037
1118
|
const {
|
|
@@ -1048,12 +1129,12 @@ function query(config) {
|
|
|
1048
1129
|
mcpServers,
|
|
1049
1130
|
pathToClaudeCodeExecutable = getDefaultClaudeCodePath(),
|
|
1050
1131
|
permissionMode = "default",
|
|
1051
|
-
permissionPromptToolName,
|
|
1052
1132
|
continue: continueConversation,
|
|
1053
1133
|
resume,
|
|
1054
1134
|
model,
|
|
1055
1135
|
fallbackModel,
|
|
1056
|
-
strictMcpConfig
|
|
1136
|
+
strictMcpConfig,
|
|
1137
|
+
canCallTool
|
|
1057
1138
|
} = {}
|
|
1058
1139
|
} = config;
|
|
1059
1140
|
if (!process.env.CLAUDE_CODE_ENTRYPOINT) {
|
|
@@ -1064,7 +1145,12 @@ function query(config) {
|
|
|
1064
1145
|
if (appendSystemPrompt) args.push("--append-system-prompt", appendSystemPrompt);
|
|
1065
1146
|
if (maxTurns) args.push("--max-turns", maxTurns.toString());
|
|
1066
1147
|
if (model) args.push("--model", model);
|
|
1067
|
-
if (
|
|
1148
|
+
if (canCallTool) {
|
|
1149
|
+
if (typeof prompt === "string") {
|
|
1150
|
+
throw new Error("canCallTool callback requires --input-format stream-json. Please set prompt as an AsyncIterable.");
|
|
1151
|
+
}
|
|
1152
|
+
args.push("--permission-prompt-tool", "stdio");
|
|
1153
|
+
}
|
|
1068
1154
|
if (continueConversation) args.push("--continue");
|
|
1069
1155
|
if (resume) args.push("--resume", resume);
|
|
1070
1156
|
if (allowedTools.length > 0) args.push("--allowedTools", allowedTools.join(","));
|
|
@@ -1128,7 +1214,7 @@ function query(config) {
|
|
|
1128
1214
|
}
|
|
1129
1215
|
});
|
|
1130
1216
|
});
|
|
1131
|
-
const query2 = new Query(childStdin, child.stdout, processExitPromise);
|
|
1217
|
+
const query2 = new Query(childStdin, child.stdout, processExitPromise, canCallTool);
|
|
1132
1218
|
child.on("error", (error) => {
|
|
1133
1219
|
if (config.options?.abort?.aborted) {
|
|
1134
1220
|
query2.setError(new AbortError("Claude Code process aborted by user"));
|
|
@@ -1146,17 +1232,48 @@ function query(config) {
|
|
|
1146
1232
|
return query2;
|
|
1147
1233
|
}
|
|
1148
1234
|
|
|
1149
|
-
|
|
1150
|
-
const
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
}
|
|
1156
|
-
await types$1.delay(1e3);
|
|
1157
|
-
}
|
|
1235
|
+
function parseCompact(message) {
|
|
1236
|
+
const trimmed = message.trim();
|
|
1237
|
+
if (trimmed === "/compact") {
|
|
1238
|
+
return {
|
|
1239
|
+
isCompact: true,
|
|
1240
|
+
originalMessage: trimmed
|
|
1241
|
+
};
|
|
1158
1242
|
}
|
|
1159
|
-
|
|
1243
|
+
if (trimmed.startsWith("/compact ")) {
|
|
1244
|
+
return {
|
|
1245
|
+
isCompact: true,
|
|
1246
|
+
originalMessage: trimmed
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
return {
|
|
1250
|
+
isCompact: false,
|
|
1251
|
+
originalMessage: message
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
function parseClear(message) {
|
|
1255
|
+
const trimmed = message.trim();
|
|
1256
|
+
return {
|
|
1257
|
+
isClear: trimmed === "/clear"
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
function parseSpecialCommand(message) {
|
|
1261
|
+
const compactResult = parseCompact(message);
|
|
1262
|
+
if (compactResult.isCompact) {
|
|
1263
|
+
return {
|
|
1264
|
+
type: "compact",
|
|
1265
|
+
originalMessage: compactResult.originalMessage
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
const clearResult = parseClear(message);
|
|
1269
|
+
if (clearResult.isClear) {
|
|
1270
|
+
return {
|
|
1271
|
+
type: "clear"
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
return {
|
|
1275
|
+
type: null
|
|
1276
|
+
};
|
|
1160
1277
|
}
|
|
1161
1278
|
|
|
1162
1279
|
class PushableAsyncIterable {
|
|
@@ -1285,48 +1402,17 @@ class PushableAsyncIterable {
|
|
|
1285
1402
|
}
|
|
1286
1403
|
}
|
|
1287
1404
|
|
|
1288
|
-
function
|
|
1289
|
-
const
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
return {
|
|
1298
|
-
isCompact: true,
|
|
1299
|
-
originalMessage: trimmed
|
|
1300
|
-
};
|
|
1301
|
-
}
|
|
1302
|
-
return {
|
|
1303
|
-
isCompact: false,
|
|
1304
|
-
originalMessage: message
|
|
1305
|
-
};
|
|
1306
|
-
}
|
|
1307
|
-
function parseClear(message) {
|
|
1308
|
-
const trimmed = message.trim();
|
|
1309
|
-
return {
|
|
1310
|
-
isClear: trimmed === "/clear"
|
|
1311
|
-
};
|
|
1312
|
-
}
|
|
1313
|
-
function parseSpecialCommand(message) {
|
|
1314
|
-
const compactResult = parseCompact(message);
|
|
1315
|
-
if (compactResult.isCompact) {
|
|
1316
|
-
return {
|
|
1317
|
-
type: "compact",
|
|
1318
|
-
originalMessage: compactResult.originalMessage
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
const clearResult = parseClear(message);
|
|
1322
|
-
if (clearResult.isClear) {
|
|
1323
|
-
return {
|
|
1324
|
-
type: "clear"
|
|
1325
|
-
};
|
|
1405
|
+
async function awaitFileExist(file, timeout = 1e4) {
|
|
1406
|
+
const startTime = Date.now();
|
|
1407
|
+
while (Date.now() - startTime < timeout) {
|
|
1408
|
+
try {
|
|
1409
|
+
await promises.access(file);
|
|
1410
|
+
return true;
|
|
1411
|
+
} catch (e) {
|
|
1412
|
+
await types$1.delay(1e3);
|
|
1413
|
+
}
|
|
1326
1414
|
}
|
|
1327
|
-
return
|
|
1328
|
-
type: null
|
|
1329
|
-
};
|
|
1415
|
+
return false;
|
|
1330
1416
|
}
|
|
1331
1417
|
|
|
1332
1418
|
async function claudeRemote(opts) {
|
|
@@ -1339,32 +1425,12 @@ async function claudeRemote(opts) {
|
|
|
1339
1425
|
process.env[key] = value;
|
|
1340
1426
|
});
|
|
1341
1427
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
resume: startFrom ?? void 0,
|
|
1346
|
-
mcpServers: opts.mcpServers,
|
|
1347
|
-
permissionPromptToolName: opts.permissionPromptToolName,
|
|
1348
|
-
permissionMode: opts.permissionMode,
|
|
1349
|
-
model: opts.model,
|
|
1350
|
-
fallbackModel: opts.fallbackModel,
|
|
1351
|
-
customSystemPrompt: opts.customSystemPrompt,
|
|
1352
|
-
appendSystemPrompt: opts.appendSystemPrompt,
|
|
1353
|
-
allowedTools: opts.allowedTools,
|
|
1354
|
-
disallowedTools: opts.disallowedTools,
|
|
1355
|
-
executable: "node",
|
|
1356
|
-
abort: opts.signal,
|
|
1357
|
-
pathToClaudeCodeExecutable: (() => {
|
|
1358
|
-
return node_path.resolve(node_path.join(projectPath(), "scripts", "claude_remote_launcher.cjs"));
|
|
1359
|
-
})()
|
|
1360
|
-
};
|
|
1361
|
-
if (opts.claudeArgs && opts.claudeArgs.length > 0) {
|
|
1362
|
-
sdkOptions.executableArgs = [...sdkOptions.executableArgs || [], ...opts.claudeArgs];
|
|
1428
|
+
const initial = await opts.nextMessage();
|
|
1429
|
+
if (!initial) {
|
|
1430
|
+
return;
|
|
1363
1431
|
}
|
|
1364
|
-
|
|
1365
|
-
const specialCommand = parseSpecialCommand(opts.message);
|
|
1432
|
+
const specialCommand = parseSpecialCommand(initial.message);
|
|
1366
1433
|
if (specialCommand.type === "clear") {
|
|
1367
|
-
types$1.logger.debug("[claudeRemote] /clear command detected - should not reach here, handled in start.ts");
|
|
1368
1434
|
if (opts.onCompletionEvent) {
|
|
1369
1435
|
opts.onCompletionEvent("Context was reset");
|
|
1370
1436
|
}
|
|
@@ -1373,29 +1439,33 @@ async function claudeRemote(opts) {
|
|
|
1373
1439
|
}
|
|
1374
1440
|
return;
|
|
1375
1441
|
}
|
|
1442
|
+
let isCompactCommand = false;
|
|
1376
1443
|
if (specialCommand.type === "compact") {
|
|
1377
1444
|
types$1.logger.debug("[claudeRemote] /compact command detected - will process as normal but with compaction behavior");
|
|
1378
|
-
|
|
1379
|
-
const isCompactCommand = specialCommand.type === "compact";
|
|
1380
|
-
if (isCompactCommand) {
|
|
1381
|
-
types$1.logger.debug("[claudeRemote] Compaction started");
|
|
1445
|
+
isCompactCommand = true;
|
|
1382
1446
|
if (opts.onCompletionEvent) {
|
|
1383
1447
|
opts.onCompletionEvent("Compaction started");
|
|
1384
1448
|
}
|
|
1385
1449
|
}
|
|
1386
|
-
let
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1450
|
+
let mode = initial.mode;
|
|
1451
|
+
const sdkOptions = {
|
|
1452
|
+
cwd: opts.path,
|
|
1453
|
+
resume: startFrom ?? void 0,
|
|
1454
|
+
mcpServers: opts.mcpServers,
|
|
1455
|
+
permissionMode: initial.mode.permissionMode === "plan" ? "plan" : "default",
|
|
1456
|
+
model: initial.mode.model,
|
|
1457
|
+
fallbackModel: initial.mode.fallbackModel,
|
|
1458
|
+
customSystemPrompt: initial.mode.customSystemPrompt ? initial.mode.customSystemPrompt + "\n\n" + systemPrompt : void 0,
|
|
1459
|
+
appendSystemPrompt: initial.mode.appendSystemPrompt ? initial.mode.appendSystemPrompt + "\n\n" + systemPrompt : systemPrompt,
|
|
1460
|
+
allowedTools: initial.mode.allowedTools ? initial.mode.allowedTools.concat(opts.allowedTools) : opts.allowedTools,
|
|
1461
|
+
disallowedTools: initial.mode.disallowedTools,
|
|
1462
|
+
canCallTool: (toolName, input, options) => opts.canCallTool(toolName, input, mode, options),
|
|
1463
|
+
executable: "node",
|
|
1464
|
+
abort: opts.signal,
|
|
1465
|
+
pathToClaudeCodeExecutable: (() => {
|
|
1466
|
+
return node_path.resolve(node_path.join(projectPath(), "scripts", "claude_remote_launcher.cjs"));
|
|
1467
|
+
})()
|
|
1468
|
+
};
|
|
1399
1469
|
let thinking = false;
|
|
1400
1470
|
const updateThinking = (newThinking) => {
|
|
1401
1471
|
if (thinking !== newThinking) {
|
|
@@ -1406,15 +1476,27 @@ async function claudeRemote(opts) {
|
|
|
1406
1476
|
}
|
|
1407
1477
|
}
|
|
1408
1478
|
};
|
|
1479
|
+
let messages = new PushableAsyncIterable();
|
|
1480
|
+
messages.push({
|
|
1481
|
+
type: "user",
|
|
1482
|
+
message: {
|
|
1483
|
+
role: "user",
|
|
1484
|
+
content: initial.message
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
const response = query({
|
|
1488
|
+
prompt: messages,
|
|
1489
|
+
options: sdkOptions
|
|
1490
|
+
});
|
|
1409
1491
|
updateThinking(true);
|
|
1410
1492
|
try {
|
|
1411
1493
|
types$1.logger.debug(`[claudeRemote] Starting to iterate over response`);
|
|
1412
|
-
for await (const
|
|
1413
|
-
types$1.logger.debugLargeJson(`[claudeRemote] Message ${
|
|
1414
|
-
opts.onMessage(
|
|
1415
|
-
if (
|
|
1494
|
+
for await (const message of response) {
|
|
1495
|
+
types$1.logger.debugLargeJson(`[claudeRemote] Message ${message.type}`, message);
|
|
1496
|
+
opts.onMessage(message);
|
|
1497
|
+
if (message.type === "system" && message.subtype === "init") {
|
|
1416
1498
|
updateThinking(true);
|
|
1417
|
-
const systemInit =
|
|
1499
|
+
const systemInit = message;
|
|
1418
1500
|
if (systemInit.session_id) {
|
|
1419
1501
|
types$1.logger.debug(`[claudeRemote] Waiting for session file to be written to disk: ${systemInit.session_id}`);
|
|
1420
1502
|
const projectDir = getProjectPath(opts.path);
|
|
@@ -1423,7 +1505,7 @@ async function claudeRemote(opts) {
|
|
|
1423
1505
|
opts.onSessionFound(systemInit.session_id);
|
|
1424
1506
|
}
|
|
1425
1507
|
}
|
|
1426
|
-
if (
|
|
1508
|
+
if (message.type === "result") {
|
|
1427
1509
|
updateThinking(false);
|
|
1428
1510
|
types$1.logger.debug("[claudeRemote] Result received, exiting claudeRemote");
|
|
1429
1511
|
if (isCompactCommand) {
|
|
@@ -1431,26 +1513,28 @@ async function claudeRemote(opts) {
|
|
|
1431
1513
|
if (opts.onCompletionEvent) {
|
|
1432
1514
|
opts.onCompletionEvent("Compaction completed");
|
|
1433
1515
|
}
|
|
1516
|
+
isCompactCommand = false;
|
|
1434
1517
|
}
|
|
1435
|
-
|
|
1518
|
+
const next = await opts.nextMessage();
|
|
1519
|
+
if (!next) {
|
|
1520
|
+
messages.end();
|
|
1521
|
+
return;
|
|
1522
|
+
}
|
|
1523
|
+
mode = next.mode;
|
|
1524
|
+
messages.push({ type: "user", message: { role: "user", content: next.message } });
|
|
1436
1525
|
}
|
|
1437
|
-
if (
|
|
1438
|
-
const msg =
|
|
1526
|
+
if (message.type === "user") {
|
|
1527
|
+
const msg = message;
|
|
1439
1528
|
if (msg.message.role === "user" && Array.isArray(msg.message.content)) {
|
|
1440
1529
|
for (let c of msg.message.content) {
|
|
1441
|
-
if (c.type === "tool_result" &&
|
|
1442
|
-
types$1.logger.debug("[claudeRemote]
|
|
1443
|
-
return;
|
|
1444
|
-
}
|
|
1445
|
-
if (c.type === "tool_result" && c.tool_use_id && opts.responses.has(c.tool_use_id) && !opts.responses.get(c.tool_use_id).approved) {
|
|
1446
|
-
types$1.logger.debug("[claudeRemote] Tool rejected, exiting claudeRemote");
|
|
1530
|
+
if (c.type === "tool_result" && c.tool_use_id && opts.isAborted(c.tool_use_id)) {
|
|
1531
|
+
types$1.logger.debug("[claudeRemote] Tool aborted, exiting claudeRemote");
|
|
1447
1532
|
return;
|
|
1448
1533
|
}
|
|
1449
1534
|
}
|
|
1450
1535
|
}
|
|
1451
1536
|
}
|
|
1452
1537
|
}
|
|
1453
|
-
types$1.logger.debug(`[claudeRemote] Finished iterating over response`);
|
|
1454
1538
|
} catch (e) {
|
|
1455
1539
|
if (e instanceof AbortError) {
|
|
1456
1540
|
types$1.logger.debug(`[claudeRemote] Aborted`);
|
|
@@ -1460,71 +1544,11 @@ async function claudeRemote(opts) {
|
|
|
1460
1544
|
} finally {
|
|
1461
1545
|
updateThinking(false);
|
|
1462
1546
|
}
|
|
1463
|
-
types$1.logger.debug(`[claudeRemote] Function completed`);
|
|
1464
1547
|
}
|
|
1465
1548
|
|
|
1466
1549
|
const PLAN_FAKE_REJECT = `User approved plan, but you need to be restarted. STOP IMMEDIATELY TO SWITCH FROM PLAN MODE. DO NOT REPLY TO THIS MESSAGE.`;
|
|
1467
1550
|
const PLAN_FAKE_RESTART = `PlEaZe Continue with plan.`;
|
|
1468
1551
|
|
|
1469
|
-
async function startPermissionServerV2(handler) {
|
|
1470
|
-
const mcp = new mcp_js.McpServer({
|
|
1471
|
-
name: "Permission Server",
|
|
1472
|
-
version: "1.0.0",
|
|
1473
|
-
description: "A server that allows you to request permissions from the user"
|
|
1474
|
-
});
|
|
1475
|
-
mcp.registerTool("ask_permission", {
|
|
1476
|
-
description: "Request permission to execute a tool",
|
|
1477
|
-
title: "Request Permission",
|
|
1478
|
-
inputSchema: {
|
|
1479
|
-
tool_name: z.z.string().describe("The tool that needs permission"),
|
|
1480
|
-
input: z.z.any().describe("The arguments for the tool")
|
|
1481
|
-
}
|
|
1482
|
-
}, async (args) => {
|
|
1483
|
-
const response = await handler({ name: args.tool_name, arguments: args.input });
|
|
1484
|
-
types$1.logger.debugLargeJson("[permissionServerV2] Response", response);
|
|
1485
|
-
const result = response.approved ? { behavior: "allow", updatedInput: args.input || {} } : { behavior: "deny", message: response.reason || `The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file). STOP what you are doing and wait for the user to tell you how to proceed.` };
|
|
1486
|
-
return {
|
|
1487
|
-
content: [
|
|
1488
|
-
{
|
|
1489
|
-
type: "text",
|
|
1490
|
-
text: JSON.stringify(result)
|
|
1491
|
-
}
|
|
1492
|
-
],
|
|
1493
|
-
isError: false
|
|
1494
|
-
};
|
|
1495
|
-
});
|
|
1496
|
-
const transport = new streamableHttp_js.StreamableHTTPServerTransport({
|
|
1497
|
-
// NOTE: Returning session id here will result in claude
|
|
1498
|
-
// sdk spawn to fail with `Invalid Request: Server already initialized`
|
|
1499
|
-
sessionIdGenerator: void 0
|
|
1500
|
-
});
|
|
1501
|
-
await mcp.connect(transport);
|
|
1502
|
-
const server = node_http.createServer(async (req, res) => {
|
|
1503
|
-
try {
|
|
1504
|
-
await transport.handleRequest(req, res);
|
|
1505
|
-
} catch (error) {
|
|
1506
|
-
types$1.logger.debug("Error handling request:", error);
|
|
1507
|
-
if (!res.headersSent) {
|
|
1508
|
-
res.writeHead(500).end();
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
});
|
|
1512
|
-
const baseUrl = await new Promise((resolve) => {
|
|
1513
|
-
server.listen(0, "127.0.0.1", () => {
|
|
1514
|
-
const addr = server.address();
|
|
1515
|
-
resolve(new URL(`http://127.0.0.1:${addr.port}`));
|
|
1516
|
-
});
|
|
1517
|
-
});
|
|
1518
|
-
return {
|
|
1519
|
-
url: baseUrl.toString(),
|
|
1520
|
-
toolName: "ask_permission",
|
|
1521
|
-
stop: () => {
|
|
1522
|
-
mcp.close();
|
|
1523
|
-
server.close();
|
|
1524
|
-
}
|
|
1525
|
-
};
|
|
1526
|
-
}
|
|
1527
|
-
|
|
1528
1552
|
function deepEqual(a, b) {
|
|
1529
1553
|
if (a === b) return true;
|
|
1530
1554
|
if (a == null || b == null) return false;
|
|
@@ -1539,133 +1563,218 @@ function deepEqual(a, b) {
|
|
|
1539
1563
|
return true;
|
|
1540
1564
|
}
|
|
1541
1565
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1566
|
+
const STANDARD_TOOLS = {
|
|
1567
|
+
// File operations
|
|
1568
|
+
"Read": "Read File",
|
|
1569
|
+
"Write": "Write File",
|
|
1570
|
+
"Edit": "Edit File",
|
|
1571
|
+
"MultiEdit": "Edit File",
|
|
1572
|
+
"NotebookEdit": "Edit Notebook",
|
|
1573
|
+
// Search and navigation
|
|
1574
|
+
"Glob": "Find Files",
|
|
1575
|
+
"Grep": "Search in Files",
|
|
1576
|
+
"LS": "List Directory",
|
|
1577
|
+
// Command execution
|
|
1578
|
+
"Bash": "Run Command",
|
|
1579
|
+
"BashOutput": "Check Command Output",
|
|
1580
|
+
"KillBash": "Stop Command",
|
|
1581
|
+
// Task management
|
|
1582
|
+
"TodoWrite": "Update Tasks",
|
|
1583
|
+
"TodoRead": "Read Tasks",
|
|
1584
|
+
"Task": "Launch Agent",
|
|
1585
|
+
// Web tools
|
|
1586
|
+
"WebFetch": "Fetch Web Page",
|
|
1587
|
+
"WebSearch": "Search Web",
|
|
1588
|
+
// Special cases
|
|
1589
|
+
"exit_plan_mode": "Execute Plan",
|
|
1590
|
+
"ExitPlanMode": "Execute Plan"
|
|
1591
|
+
};
|
|
1592
|
+
function toTitleCase(str) {
|
|
1593
|
+
return str.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
1594
|
+
}
|
|
1595
|
+
function getToolName(toolName) {
|
|
1596
|
+
if (STANDARD_TOOLS[toolName]) {
|
|
1597
|
+
return STANDARD_TOOLS[toolName];
|
|
1598
|
+
}
|
|
1599
|
+
if (toolName.startsWith("mcp__")) {
|
|
1600
|
+
const parts = toolName.split("__");
|
|
1601
|
+
if (parts.length >= 3) {
|
|
1602
|
+
const server = toTitleCase(parts[1]);
|
|
1603
|
+
const action = toTitleCase(parts.slice(2).join("_"));
|
|
1604
|
+
return `${server}: ${action}`;
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
return toTitleCase(toolName);
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
function getToolDescriptor(toolName) {
|
|
1611
|
+
if (toolName === "exit_plan_mode" || toolName === "ExitPlanMode") {
|
|
1612
|
+
return { edit: false, exitPlan: true };
|
|
1613
|
+
}
|
|
1614
|
+
if (toolName === "Edit" || toolName === "MultiEdit" || toolName === "Write" || toolName === "NotebookEdit") {
|
|
1615
|
+
return { edit: true, exitPlan: false };
|
|
1616
|
+
}
|
|
1617
|
+
return { edit: false, exitPlan: false };
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
class PermissionHandler {
|
|
1621
|
+
toolCalls = [];
|
|
1622
|
+
responses = /* @__PURE__ */ new Map();
|
|
1623
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
1624
|
+
session;
|
|
1625
|
+
allowedTools = /* @__PURE__ */ new Set();
|
|
1626
|
+
allowedBashLiterals = /* @__PURE__ */ new Set();
|
|
1627
|
+
allowedBashPrefixes = /* @__PURE__ */ new Set();
|
|
1628
|
+
permissionMode = "default";
|
|
1629
|
+
constructor(session) {
|
|
1630
|
+
this.session = session;
|
|
1631
|
+
this.setupClientHandler();
|
|
1632
|
+
}
|
|
1633
|
+
handleModeChange(mode) {
|
|
1634
|
+
this.permissionMode = mode;
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Handler response
|
|
1638
|
+
*/
|
|
1639
|
+
handlePermissionResponse(response, pending) {
|
|
1640
|
+
if (response.allowTools && response.allowTools.length > 0) {
|
|
1641
|
+
response.allowTools.forEach((tool) => {
|
|
1642
|
+
if (tool.startsWith("Bash(") || tool === "Bash") {
|
|
1643
|
+
this.parseBashPermission(tool);
|
|
1644
|
+
} else {
|
|
1645
|
+
this.allowedTools.add(tool);
|
|
1646
|
+
}
|
|
1560
1647
|
});
|
|
1561
1648
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
resolve({ approved: false, reason: PLAN_FAKE_REJECT });
|
|
1576
|
-
} else {
|
|
1577
|
-
resolve(response);
|
|
1578
|
-
}
|
|
1579
|
-
};
|
|
1580
|
-
requests.set(id, wrappedResolve);
|
|
1649
|
+
if (response.mode) {
|
|
1650
|
+
this.permissionMode = response.mode;
|
|
1651
|
+
}
|
|
1652
|
+
if (pending.toolName === "exit_plan_mode" || pending.toolName === "ExitPlanMode") {
|
|
1653
|
+
types$1.logger.debug("Plan mode result received", response);
|
|
1654
|
+
if (response.approved) {
|
|
1655
|
+
types$1.logger.debug("Plan approved - injecting PLAN_FAKE_RESTART");
|
|
1656
|
+
if (response.mode && ["default", "acceptEdits", "bypassPermissions"].includes(response.mode)) {
|
|
1657
|
+
this.session.queue.unshift(PLAN_FAKE_RESTART, { permissionMode: response.mode });
|
|
1658
|
+
} else {
|
|
1659
|
+
this.session.queue.unshift(PLAN_FAKE_RESTART, { permissionMode: "default" });
|
|
1660
|
+
}
|
|
1661
|
+
pending.resolve({ behavior: "deny", message: PLAN_FAKE_REJECT });
|
|
1581
1662
|
} else {
|
|
1582
|
-
|
|
1663
|
+
pending.resolve({ behavior: "deny", message: response.reason || "Plan rejected" });
|
|
1583
1664
|
}
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
reason: "Timeout"
|
|
1603
|
-
}
|
|
1665
|
+
} else {
|
|
1666
|
+
const result = response.approved ? { behavior: "allow", updatedInput: pending.input || {} } : { behavior: "deny", message: response.reason || `The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file). STOP what you are doing and wait for the user to tell you how to proceed.` };
|
|
1667
|
+
pending.resolve(result);
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
/**
|
|
1671
|
+
* Creates the canCallTool callback for the SDK
|
|
1672
|
+
*/
|
|
1673
|
+
handleToolCall = async (toolName, input, mode, options) => {
|
|
1674
|
+
if (toolName === "Bash") {
|
|
1675
|
+
const inputObj = input;
|
|
1676
|
+
if (inputObj?.command) {
|
|
1677
|
+
if (this.allowedBashLiterals.has(inputObj.command)) {
|
|
1678
|
+
return { behavior: "allow", updatedInput: input };
|
|
1679
|
+
}
|
|
1680
|
+
for (const prefix of this.allowedBashPrefixes) {
|
|
1681
|
+
if (inputObj.command.startsWith(prefix)) {
|
|
1682
|
+
return { behavior: "allow", updatedInput: input };
|
|
1604
1683
|
}
|
|
1605
|
-
};
|
|
1606
|
-
});
|
|
1607
|
-
}, 1e3 * 60 * 4.5);
|
|
1608
|
-
types$1.logger.debug("Permission request" + id + " " + JSON.stringify(request));
|
|
1609
|
-
session.api.push().sendToAllDevices(
|
|
1610
|
-
"Permission Request",
|
|
1611
|
-
`Claude wants to use ${request.name}`,
|
|
1612
|
-
{
|
|
1613
|
-
sessionId: session.client.sessionId,
|
|
1614
|
-
requestId: id,
|
|
1615
|
-
tool: request.name,
|
|
1616
|
-
type: "permission_request"
|
|
1617
|
-
}
|
|
1618
|
-
);
|
|
1619
|
-
session.client.updateAgentState((currentState) => ({
|
|
1620
|
-
...currentState,
|
|
1621
|
-
requests: {
|
|
1622
|
-
...currentState.requests,
|
|
1623
|
-
[id]: {
|
|
1624
|
-
tool: request.name,
|
|
1625
|
-
arguments: request.arguments,
|
|
1626
|
-
createdAt: Date.now()
|
|
1627
1684
|
}
|
|
1628
1685
|
}
|
|
1629
|
-
}))
|
|
1630
|
-
|
|
1631
|
-
return promise;
|
|
1632
|
-
}
|
|
1633
|
-
session.client.setHandler("permission", async (message) => {
|
|
1634
|
-
types$1.logger.debug("Permission response" + JSON.stringify(message));
|
|
1635
|
-
const id = message.id;
|
|
1636
|
-
const resolve = requests.get(id);
|
|
1637
|
-
if (resolve) {
|
|
1638
|
-
responses.set(id, message);
|
|
1639
|
-
resolve({ approved: message.approved, reason: message.reason, mode: message.mode });
|
|
1640
|
-
requests.delete(id);
|
|
1641
|
-
} else {
|
|
1642
|
-
types$1.logger.debug("Permission request stale, likely timed out");
|
|
1643
|
-
return;
|
|
1686
|
+
} else if (this.allowedTools.has(toolName)) {
|
|
1687
|
+
return { behavior: "allow", updatedInput: input };
|
|
1644
1688
|
}
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1689
|
+
const descriptor = getToolDescriptor(toolName);
|
|
1690
|
+
if (this.permissionMode === "bypassPermissions") {
|
|
1691
|
+
return { behavior: "allow", updatedInput: input };
|
|
1692
|
+
}
|
|
1693
|
+
if (this.permissionMode === "acceptEdits" && descriptor.edit) {
|
|
1694
|
+
return { behavior: "allow", updatedInput: input };
|
|
1695
|
+
}
|
|
1696
|
+
let toolCallId = this.resolveToolCallId(toolName, input);
|
|
1697
|
+
if (!toolCallId) {
|
|
1698
|
+
await types$1.delay(1e3);
|
|
1699
|
+
toolCallId = this.resolveToolCallId(toolName, input);
|
|
1700
|
+
if (!toolCallId) {
|
|
1701
|
+
throw new Error(`Could not resolve tool call ID for ${toolName}`);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
return this.handlePermissionRequest(toolCallId, toolName, input, options.signal);
|
|
1705
|
+
};
|
|
1706
|
+
/**
|
|
1707
|
+
* Handles individual permission requests
|
|
1708
|
+
*/
|
|
1709
|
+
async handlePermissionRequest(id, toolName, input, signal) {
|
|
1710
|
+
return new Promise((resolve, reject) => {
|
|
1711
|
+
const abortHandler = () => {
|
|
1712
|
+
this.pendingRequests.delete(id);
|
|
1713
|
+
reject(new Error("Permission request aborted"));
|
|
1714
|
+
};
|
|
1715
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1716
|
+
this.pendingRequests.set(id, {
|
|
1717
|
+
resolve: (result) => {
|
|
1718
|
+
signal.removeEventListener("abort", abortHandler);
|
|
1719
|
+
resolve(result);
|
|
1720
|
+
},
|
|
1721
|
+
reject: (error) => {
|
|
1722
|
+
signal.removeEventListener("abort", abortHandler);
|
|
1723
|
+
reject(error);
|
|
1724
|
+
},
|
|
1725
|
+
toolName,
|
|
1726
|
+
input
|
|
1727
|
+
});
|
|
1728
|
+
this.session.api.push().sendToAllDevices(
|
|
1729
|
+
"Permission Request",
|
|
1730
|
+
`Claude wants to ${getToolName(toolName)}`,
|
|
1731
|
+
{
|
|
1732
|
+
sessionId: this.session.client.sessionId,
|
|
1733
|
+
requestId: id,
|
|
1734
|
+
tool: toolName,
|
|
1735
|
+
type: "permission_request"
|
|
1736
|
+
}
|
|
1737
|
+
);
|
|
1738
|
+
this.session.client.updateAgentState((currentState) => ({
|
|
1652
1739
|
...currentState,
|
|
1653
|
-
requests:
|
|
1654
|
-
|
|
1655
|
-
...currentState.completedRequests,
|
|
1740
|
+
requests: {
|
|
1741
|
+
...currentState.requests,
|
|
1656
1742
|
[id]: {
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
reason: isExitPlanModeSuccess ? "Plan approved" : message.reason
|
|
1743
|
+
tool: toolName,
|
|
1744
|
+
arguments: input,
|
|
1745
|
+
createdAt: Date.now()
|
|
1661
1746
|
}
|
|
1662
1747
|
}
|
|
1663
|
-
};
|
|
1748
|
+
}));
|
|
1749
|
+
types$1.logger.debug(`Permission request sent for tool call ${id}: ${toolName}`);
|
|
1664
1750
|
});
|
|
1665
|
-
}
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1751
|
+
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Parses Bash permission strings into literal and prefix sets
|
|
1754
|
+
*/
|
|
1755
|
+
parseBashPermission(permission) {
|
|
1756
|
+
if (permission === "Bash") {
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
const bashPattern = /^Bash\((.+?)\)$/;
|
|
1760
|
+
const match = permission.match(bashPattern);
|
|
1761
|
+
if (!match) {
|
|
1762
|
+
return;
|
|
1763
|
+
}
|
|
1764
|
+
const command = match[1];
|
|
1765
|
+
if (command.endsWith(":*")) {
|
|
1766
|
+
const prefix = command.slice(0, -2);
|
|
1767
|
+
this.allowedBashPrefixes.add(prefix);
|
|
1768
|
+
} else {
|
|
1769
|
+
this.allowedBashLiterals.add(command);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
/**
|
|
1773
|
+
* Resolves tool call ID based on tool name and input
|
|
1774
|
+
*/
|
|
1775
|
+
resolveToolCallId(name, args) {
|
|
1776
|
+
for (let i = this.toolCalls.length - 1; i >= 0; i--) {
|
|
1777
|
+
const call = this.toolCalls[i];
|
|
1669
1778
|
if (call.name === name && deepEqual(call.input, args)) {
|
|
1670
1779
|
if (call.used) {
|
|
1671
1780
|
return null;
|
|
@@ -1675,59 +1784,22 @@ async function startPermissionResolver(session) {
|
|
|
1675
1784
|
}
|
|
1676
1785
|
}
|
|
1677
1786
|
return null;
|
|
1678
|
-
};
|
|
1679
|
-
function reset() {
|
|
1680
|
-
toolCalls = [];
|
|
1681
|
-
requests.clear();
|
|
1682
|
-
responses.clear();
|
|
1683
|
-
for (const pending of pendingPermissionRequests) {
|
|
1684
|
-
clearTimeout(pending.timeout);
|
|
1685
|
-
}
|
|
1686
|
-
pendingPermissionRequests = [];
|
|
1687
|
-
session.client.updateAgentState((currentState) => {
|
|
1688
|
-
const pendingRequests = currentState.requests || {};
|
|
1689
|
-
const completedRequests = { ...currentState.completedRequests };
|
|
1690
|
-
for (const [id, request] of Object.entries(pendingRequests)) {
|
|
1691
|
-
completedRequests[id] = {
|
|
1692
|
-
...request,
|
|
1693
|
-
completedAt: Date.now(),
|
|
1694
|
-
status: "canceled",
|
|
1695
|
-
reason: "Session switched to local mode"
|
|
1696
|
-
};
|
|
1697
|
-
}
|
|
1698
|
-
return {
|
|
1699
|
-
...currentState,
|
|
1700
|
-
requests: {},
|
|
1701
|
-
// Clear all pending requests
|
|
1702
|
-
completedRequests
|
|
1703
|
-
};
|
|
1704
|
-
});
|
|
1705
1787
|
}
|
|
1706
|
-
|
|
1788
|
+
/**
|
|
1789
|
+
* Handles messages to track tool calls
|
|
1790
|
+
*/
|
|
1791
|
+
onMessage(message) {
|
|
1707
1792
|
if (message.type === "assistant") {
|
|
1708
1793
|
const assistantMsg = message;
|
|
1709
1794
|
if (assistantMsg.message && assistantMsg.message.content) {
|
|
1710
1795
|
for (const block of assistantMsg.message.content) {
|
|
1711
1796
|
if (block.type === "tool_use") {
|
|
1712
|
-
toolCalls.push({
|
|
1797
|
+
this.toolCalls.push({
|
|
1713
1798
|
id: block.id,
|
|
1714
1799
|
name: block.name,
|
|
1715
1800
|
input: block.input,
|
|
1716
1801
|
used: false
|
|
1717
1802
|
});
|
|
1718
|
-
for (let i = pendingPermissionRequests.length - 1; i >= 0; i--) {
|
|
1719
|
-
const pending = pendingPermissionRequests[i];
|
|
1720
|
-
if (pending.request.name === block.name && deepEqual(pending.request.arguments, block.input)) {
|
|
1721
|
-
types$1.logger.debug(`Resolving pending permission request for ${block.name} with ID ${block.id}`);
|
|
1722
|
-
clearTimeout(pending.timeout);
|
|
1723
|
-
pendingPermissionRequests.splice(i, 1);
|
|
1724
|
-
handlePermissionRequest(block.id, pending.request).then(
|
|
1725
|
-
pending.resolve,
|
|
1726
|
-
pending.reject
|
|
1727
|
-
);
|
|
1728
|
-
break;
|
|
1729
|
-
}
|
|
1730
|
-
}
|
|
1731
1803
|
}
|
|
1732
1804
|
}
|
|
1733
1805
|
}
|
|
@@ -1737,7 +1809,7 @@ async function startPermissionResolver(session) {
|
|
|
1737
1809
|
if (userMsg.message && userMsg.message.content && Array.isArray(userMsg.message.content)) {
|
|
1738
1810
|
for (const block of userMsg.message.content) {
|
|
1739
1811
|
if (block.type === "tool_result" && block.tool_use_id) {
|
|
1740
|
-
const toolCall = toolCalls.find((tc) => tc.id === block.tool_use_id);
|
|
1812
|
+
const toolCall = this.toolCalls.find((tc) => tc.id === block.tool_use_id);
|
|
1741
1813
|
if (toolCall && !toolCall.used) {
|
|
1742
1814
|
toolCall.used = true;
|
|
1743
1815
|
}
|
|
@@ -1746,12 +1818,95 @@ async function startPermissionResolver(session) {
|
|
|
1746
1818
|
}
|
|
1747
1819
|
}
|
|
1748
1820
|
}
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
responses
|
|
1754
|
-
|
|
1821
|
+
/**
|
|
1822
|
+
* Checks if a tool call is rejected
|
|
1823
|
+
*/
|
|
1824
|
+
isAborted(toolCallId) {
|
|
1825
|
+
if (this.responses.get(toolCallId)?.approved === false) {
|
|
1826
|
+
return true;
|
|
1827
|
+
}
|
|
1828
|
+
const toolCall = this.toolCalls.find((tc) => tc.id === toolCallId);
|
|
1829
|
+
if (toolCall && (toolCall.name === "exit_plan_mode" || toolCall.name === "ExitPlanMode")) {
|
|
1830
|
+
return true;
|
|
1831
|
+
}
|
|
1832
|
+
return false;
|
|
1833
|
+
}
|
|
1834
|
+
/**
|
|
1835
|
+
* Resets all state for new sessions
|
|
1836
|
+
*/
|
|
1837
|
+
reset() {
|
|
1838
|
+
this.toolCalls = [];
|
|
1839
|
+
this.responses.clear();
|
|
1840
|
+
this.allowedTools.clear();
|
|
1841
|
+
this.allowedBashLiterals.clear();
|
|
1842
|
+
this.allowedBashPrefixes.clear();
|
|
1843
|
+
for (const [, pending] of this.pendingRequests.entries()) {
|
|
1844
|
+
pending.reject(new Error("Session reset"));
|
|
1845
|
+
}
|
|
1846
|
+
this.pendingRequests.clear();
|
|
1847
|
+
this.session.client.updateAgentState((currentState) => {
|
|
1848
|
+
const pendingRequests = currentState.requests || {};
|
|
1849
|
+
const completedRequests = { ...currentState.completedRequests };
|
|
1850
|
+
for (const [id, request] of Object.entries(pendingRequests)) {
|
|
1851
|
+
completedRequests[id] = {
|
|
1852
|
+
...request,
|
|
1853
|
+
completedAt: Date.now(),
|
|
1854
|
+
status: "canceled",
|
|
1855
|
+
reason: "Session switched to local mode"
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
return {
|
|
1859
|
+
...currentState,
|
|
1860
|
+
requests: {},
|
|
1861
|
+
// Clear all pending requests
|
|
1862
|
+
completedRequests
|
|
1863
|
+
};
|
|
1864
|
+
});
|
|
1865
|
+
}
|
|
1866
|
+
/**
|
|
1867
|
+
* Sets up the client handler for permission responses
|
|
1868
|
+
*/
|
|
1869
|
+
setupClientHandler() {
|
|
1870
|
+
this.session.client.setHandler("permission", async (message) => {
|
|
1871
|
+
types$1.logger.debug(`Permission response: ${JSON.stringify(message)}`);
|
|
1872
|
+
const id = message.id;
|
|
1873
|
+
const pending = this.pendingRequests.get(id);
|
|
1874
|
+
if (!pending) {
|
|
1875
|
+
types$1.logger.debug("Permission request not found or already resolved");
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
this.responses.set(id, { ...message, receivedAt: Date.now() });
|
|
1879
|
+
this.pendingRequests.delete(id);
|
|
1880
|
+
this.handlePermissionResponse(message, pending);
|
|
1881
|
+
this.session.client.updateAgentState((currentState) => {
|
|
1882
|
+
const request = currentState.requests?.[id];
|
|
1883
|
+
if (!request) return currentState;
|
|
1884
|
+
let r = { ...currentState.requests };
|
|
1885
|
+
delete r[id];
|
|
1886
|
+
return {
|
|
1887
|
+
...currentState,
|
|
1888
|
+
requests: r,
|
|
1889
|
+
completedRequests: {
|
|
1890
|
+
...currentState.completedRequests,
|
|
1891
|
+
[id]: {
|
|
1892
|
+
...request,
|
|
1893
|
+
completedAt: Date.now(),
|
|
1894
|
+
status: message.approved ? "approved" : "denied",
|
|
1895
|
+
reason: message.reason,
|
|
1896
|
+
mode: message.mode,
|
|
1897
|
+
allowTools: message.allowTools
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
};
|
|
1901
|
+
});
|
|
1902
|
+
});
|
|
1903
|
+
}
|
|
1904
|
+
/**
|
|
1905
|
+
* Gets the responses map (for compatibility with existing code)
|
|
1906
|
+
*/
|
|
1907
|
+
getResponses() {
|
|
1908
|
+
return this.responses;
|
|
1909
|
+
}
|
|
1755
1910
|
}
|
|
1756
1911
|
|
|
1757
1912
|
function formatClaudeMessageForInk(message, messageBuffer, onAssistantResult) {
|
|
@@ -2125,12 +2280,6 @@ async function claudeRemoteLauncher(session) {
|
|
|
2125
2280
|
}
|
|
2126
2281
|
process.stdin.setEncoding("utf8");
|
|
2127
2282
|
}
|
|
2128
|
-
const scanner = await createSessionScanner({
|
|
2129
|
-
sessionId: session.sessionId,
|
|
2130
|
-
workingDirectory: session.path,
|
|
2131
|
-
onMessage: (message) => {
|
|
2132
|
-
}
|
|
2133
|
-
});
|
|
2134
2283
|
let exitReason = null;
|
|
2135
2284
|
let abortController = null;
|
|
2136
2285
|
let abortFuture = null;
|
|
@@ -2153,17 +2302,17 @@ async function claudeRemoteLauncher(session) {
|
|
|
2153
2302
|
}
|
|
2154
2303
|
session.client.setHandler("abort", doAbort);
|
|
2155
2304
|
session.client.setHandler("switch", doSwitch);
|
|
2156
|
-
const
|
|
2305
|
+
const permissionHandler = new PermissionHandler(session);
|
|
2157
2306
|
const sdkToLogConverter = new SDKToLogConverter({
|
|
2158
2307
|
sessionId: session.sessionId || "unknown",
|
|
2159
2308
|
cwd: session.path,
|
|
2160
2309
|
version: process.env.npm_package_version
|
|
2161
|
-
},
|
|
2310
|
+
}, permissionHandler.getResponses());
|
|
2162
2311
|
let planModeToolCalls = /* @__PURE__ */ new Set();
|
|
2163
2312
|
let ongoingToolCalls = /* @__PURE__ */ new Map();
|
|
2164
2313
|
function onMessage(message) {
|
|
2165
2314
|
formatClaudeMessageForInk(message, messageBuffer);
|
|
2166
|
-
|
|
2315
|
+
permissionHandler.onMessage(message);
|
|
2167
2316
|
if (message.type === "assistant") {
|
|
2168
2317
|
let umessage = message;
|
|
2169
2318
|
if (umessage.message.content && Array.isArray(umessage.message.content)) {
|
|
@@ -2227,6 +2376,32 @@ async function claudeRemoteLauncher(session) {
|
|
|
2227
2376
|
}
|
|
2228
2377
|
const logMessage = sdkToLogConverter.convert(msg);
|
|
2229
2378
|
if (logMessage) {
|
|
2379
|
+
if (logMessage.type === "user" && logMessage.message?.content) {
|
|
2380
|
+
const content = Array.isArray(logMessage.message.content) ? logMessage.message.content : [];
|
|
2381
|
+
for (let i = 0; i < content.length; i++) {
|
|
2382
|
+
const c = content[i];
|
|
2383
|
+
if (c.type === "tool_result" && c.tool_use_id) {
|
|
2384
|
+
const responses = permissionHandler.getResponses();
|
|
2385
|
+
const response = responses.get(c.tool_use_id);
|
|
2386
|
+
if (response) {
|
|
2387
|
+
const permissions = {
|
|
2388
|
+
date: response.receivedAt || Date.now(),
|
|
2389
|
+
result: response.approved ? "approved" : "denied"
|
|
2390
|
+
};
|
|
2391
|
+
if (response.mode) {
|
|
2392
|
+
permissions.mode = response.mode;
|
|
2393
|
+
}
|
|
2394
|
+
if (response.allowTools && response.allowTools.length > 0) {
|
|
2395
|
+
permissions.allowedTools = response.allowTools;
|
|
2396
|
+
}
|
|
2397
|
+
content[i] = {
|
|
2398
|
+
...c,
|
|
2399
|
+
permissions
|
|
2400
|
+
};
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2230
2405
|
if (logMessage.type !== "system") {
|
|
2231
2406
|
session.client.sendClaudeSessionMessage(logMessage);
|
|
2232
2407
|
}
|
|
@@ -2246,58 +2421,57 @@ async function claudeRemoteLauncher(session) {
|
|
|
2246
2421
|
}
|
|
2247
2422
|
}
|
|
2248
2423
|
try {
|
|
2424
|
+
let pending = null;
|
|
2249
2425
|
while (!exitReason) {
|
|
2250
|
-
types$1.logger.debug("[remote]: fetch next message");
|
|
2251
|
-
abortController = new AbortController();
|
|
2252
|
-
abortFuture = new Future();
|
|
2253
|
-
const messageData = await session.queue.waitForMessagesAndGetAsString(abortController.signal);
|
|
2254
|
-
if (!messageData || abortController.signal.aborted) {
|
|
2255
|
-
types$1.logger.debug("[remote]: fetch next message done: no message or aborted");
|
|
2256
|
-
abortFuture?.resolve(void 0);
|
|
2257
|
-
if (exitReason) {
|
|
2258
|
-
return exitReason;
|
|
2259
|
-
} else {
|
|
2260
|
-
continue;
|
|
2261
|
-
}
|
|
2262
|
-
}
|
|
2263
|
-
types$1.logger.debug("[remote]: fetch next message done: message received");
|
|
2264
|
-
abortFuture?.resolve(void 0);
|
|
2265
|
-
abortFuture = null;
|
|
2266
|
-
abortController = null;
|
|
2267
2426
|
types$1.logger.debug("[remote]: launch");
|
|
2268
2427
|
messageBuffer.addMessage("\u2550".repeat(40), "status");
|
|
2269
2428
|
messageBuffer.addMessage("Starting new Claude session...", "status");
|
|
2270
|
-
|
|
2429
|
+
const controller = new AbortController();
|
|
2430
|
+
abortController = controller;
|
|
2271
2431
|
abortFuture = new Future();
|
|
2272
|
-
|
|
2432
|
+
permissionHandler.reset();
|
|
2273
2433
|
sdkToLogConverter.resetParentChain();
|
|
2434
|
+
let modeHash = null;
|
|
2435
|
+
let mode = null;
|
|
2274
2436
|
try {
|
|
2275
2437
|
await claudeRemote({
|
|
2276
2438
|
sessionId: session.sessionId,
|
|
2277
2439
|
path: session.path,
|
|
2278
|
-
|
|
2279
|
-
mcpServers:
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2440
|
+
allowedTools: session.allowedTools ?? [],
|
|
2441
|
+
mcpServers: session.mcpServers,
|
|
2442
|
+
canCallTool: permissionHandler.handleToolCall,
|
|
2443
|
+
isAborted: (toolCallId) => {
|
|
2444
|
+
return permissionHandler.isAborted(toolCallId);
|
|
2445
|
+
},
|
|
2446
|
+
nextMessage: async () => {
|
|
2447
|
+
if (pending) {
|
|
2448
|
+
let p = pending;
|
|
2449
|
+
pending = null;
|
|
2450
|
+
permissionHandler.handleModeChange(p.mode.permissionMode);
|
|
2451
|
+
return p;
|
|
2284
2452
|
}
|
|
2453
|
+
let msg = await session.queue.waitForMessagesAndGetAsString(controller.signal);
|
|
2454
|
+
if (msg) {
|
|
2455
|
+
if (modeHash && msg.hash !== modeHash || msg.isolate) {
|
|
2456
|
+
types$1.logger.debug("[remote]: mode has changed, pending message");
|
|
2457
|
+
pending = msg;
|
|
2458
|
+
return null;
|
|
2459
|
+
}
|
|
2460
|
+
modeHash = msg.hash;
|
|
2461
|
+
mode = msg.mode;
|
|
2462
|
+
permissionHandler.handleModeChange(mode.permissionMode);
|
|
2463
|
+
return {
|
|
2464
|
+
message: msg.message,
|
|
2465
|
+
mode: msg.mode
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
return null;
|
|
2285
2469
|
},
|
|
2286
|
-
permissionPromptToolName: "mcp__permission__" + permissions.server.toolName,
|
|
2287
|
-
permissionMode: messageData.mode.permissionMode,
|
|
2288
|
-
model: messageData.mode.model,
|
|
2289
|
-
fallbackModel: messageData.mode.fallbackModel,
|
|
2290
|
-
customSystemPrompt: messageData.mode.customSystemPrompt,
|
|
2291
|
-
appendSystemPrompt: messageData.mode.appendSystemPrompt ? messageData.mode.appendSystemPrompt + "\n" + systemPrompt : systemPrompt,
|
|
2292
|
-
allowedTools: messageData.mode.allowedTools ? [...messageData.mode.allowedTools, ...session.allowedTools ? session.allowedTools : []] : session.allowedTools ? [...session.allowedTools] : void 0,
|
|
2293
|
-
disallowedTools: messageData.mode.disallowedTools,
|
|
2294
2470
|
onSessionFound: (sessionId) => {
|
|
2295
2471
|
sdkToLogConverter.updateSessionId(sessionId);
|
|
2296
2472
|
session.onSessionFound(sessionId);
|
|
2297
|
-
scanner.onNewSession(sessionId);
|
|
2298
2473
|
},
|
|
2299
2474
|
onThinkingChange: session.onThinkingChange,
|
|
2300
|
-
message: messageData.message,
|
|
2301
2475
|
claudeEnvVars: session.claudeEnvVars,
|
|
2302
2476
|
claudeArgs: session.claudeArgs,
|
|
2303
2477
|
onMessage,
|
|
@@ -2315,11 +2489,13 @@ async function claudeRemoteLauncher(session) {
|
|
|
2315
2489
|
session.client.sendSessionEvent({ type: "message", message: "Aborted by user" });
|
|
2316
2490
|
}
|
|
2317
2491
|
} catch (e) {
|
|
2492
|
+
types$1.logger.debug("[remote]: launch error", e);
|
|
2318
2493
|
if (!exitReason) {
|
|
2319
2494
|
session.client.sendSessionEvent({ type: "message", message: "Process exited unexpectedly" });
|
|
2320
2495
|
continue;
|
|
2321
2496
|
}
|
|
2322
2497
|
} finally {
|
|
2498
|
+
types$1.logger.debug("[remote]: launch finally");
|
|
2323
2499
|
for (let [toolCallId, { parentToolCallId }] of ongoingToolCalls) {
|
|
2324
2500
|
const converted = sdkToLogConverter.generateInterruptedToolResult(toolCallId, parentToolCallId);
|
|
2325
2501
|
if (converted) {
|
|
@@ -2332,11 +2508,13 @@ async function claudeRemoteLauncher(session) {
|
|
|
2332
2508
|
abortFuture?.resolve(void 0);
|
|
2333
2509
|
abortFuture = null;
|
|
2334
2510
|
types$1.logger.debug("[remote]: launch done");
|
|
2335
|
-
|
|
2511
|
+
permissionHandler.reset();
|
|
2512
|
+
modeHash = null;
|
|
2513
|
+
mode = null;
|
|
2336
2514
|
}
|
|
2337
2515
|
}
|
|
2338
2516
|
} finally {
|
|
2339
|
-
|
|
2517
|
+
permissionHandler.reset();
|
|
2340
2518
|
process.stdin.off("data", abort);
|
|
2341
2519
|
if (process.stdin.isTTY) {
|
|
2342
2520
|
process.stdin.setRawMode(false);
|
|
@@ -2348,7 +2526,6 @@ async function claudeRemoteLauncher(session) {
|
|
|
2348
2526
|
if (abortFuture) {
|
|
2349
2527
|
abortFuture.resolve(void 0);
|
|
2350
2528
|
}
|
|
2351
|
-
await scanner.cleanup();
|
|
2352
2529
|
}
|
|
2353
2530
|
return exitReason || "exit";
|
|
2354
2531
|
}
|
|
@@ -2400,7 +2577,7 @@ async function loop(opts) {
|
|
|
2400
2577
|
}
|
|
2401
2578
|
|
|
2402
2579
|
var name = "happy-coder";
|
|
2403
|
-
var version = "0.
|
|
2580
|
+
var version = "0.9.0-1";
|
|
2404
2581
|
var description = "Claude Code session sharing CLI";
|
|
2405
2582
|
var author = "Kirill Dubovitskiy";
|
|
2406
2583
|
var license = "MIT";
|
|
@@ -2450,18 +2627,14 @@ var scripts = {
|
|
|
2450
2627
|
test: "yarn build && vitest run",
|
|
2451
2628
|
"test:watch": "vitest",
|
|
2452
2629
|
"test:integration-test-env": "yarn build && tsx --env-file .env.integration-test node_modules/.bin/vitest run",
|
|
2453
|
-
dev: "
|
|
2630
|
+
dev: "yarn build && DEBUG=1 npx tsx src/index.ts",
|
|
2454
2631
|
"dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
|
|
2455
2632
|
"dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
|
|
2456
2633
|
prepublishOnly: "yarn build && yarn test",
|
|
2457
|
-
|
|
2458
|
-
"patch:publish": "yarn build && npm version patch && npm publish",
|
|
2459
|
-
"version:prerelease": "yarn build && npm version prerelease --preid=beta",
|
|
2460
|
-
"publish:prerelease": "npm publish --tag beta",
|
|
2461
|
-
"beta:publish": "yarn version:prerelease && yarn publish:prerelease"
|
|
2634
|
+
release: "release-it"
|
|
2462
2635
|
};
|
|
2463
2636
|
var dependencies = {
|
|
2464
|
-
"@anthropic-ai/claude-code": "^1.0.
|
|
2637
|
+
"@anthropic-ai/claude-code": "^1.0.89",
|
|
2465
2638
|
"@anthropic-ai/sdk": "^0.56.0",
|
|
2466
2639
|
"@modelcontextprotocol/sdk": "^1.15.1",
|
|
2467
2640
|
"@stablelib/base64": "^2.0.1",
|
|
@@ -2490,6 +2663,7 @@ var devDependencies = {
|
|
|
2490
2663
|
eslint: "^9",
|
|
2491
2664
|
"eslint-config-prettier": "^10",
|
|
2492
2665
|
pkgroll: "^2.14.2",
|
|
2666
|
+
"release-it": "^19.0.4",
|
|
2493
2667
|
shx: "^0.3.3",
|
|
2494
2668
|
"ts-node": "^10",
|
|
2495
2669
|
tsx: "^4.20.3",
|
|
@@ -2497,7 +2671,12 @@ var devDependencies = {
|
|
|
2497
2671
|
vitest: "^3.2.4"
|
|
2498
2672
|
};
|
|
2499
2673
|
var resolutions = {
|
|
2500
|
-
"whatwg-url": "14.2.0"
|
|
2674
|
+
"whatwg-url": "14.2.0",
|
|
2675
|
+
"parse-path": "7.0.3",
|
|
2676
|
+
"@types/parse-path": "7.0.3"
|
|
2677
|
+
};
|
|
2678
|
+
var publishConfig = {
|
|
2679
|
+
registry: "https://registry.npmjs.org"
|
|
2501
2680
|
};
|
|
2502
2681
|
var packageManager = "yarn@1.22.22";
|
|
2503
2682
|
var packageJson = {
|
|
@@ -2520,6 +2699,7 @@ var packageJson = {
|
|
|
2520
2699
|
dependencies: dependencies,
|
|
2521
2700
|
devDependencies: devDependencies,
|
|
2522
2701
|
resolutions: resolutions,
|
|
2702
|
+
publishConfig: publishConfig,
|
|
2523
2703
|
packageManager: packageManager
|
|
2524
2704
|
};
|
|
2525
2705
|
|
|
@@ -2906,15 +3086,17 @@ async function clearDaemonState() {
|
|
|
2906
3086
|
}
|
|
2907
3087
|
|
|
2908
3088
|
class MessageQueue2 {
|
|
2909
|
-
constructor(modeHasher) {
|
|
2910
|
-
this.modeHasher = modeHasher;
|
|
2911
|
-
types$1.logger.debug(`[MessageQueue2] Initialized`);
|
|
2912
|
-
}
|
|
2913
3089
|
queue = [];
|
|
2914
3090
|
// Made public for testing
|
|
2915
3091
|
waiter = null;
|
|
2916
3092
|
closed = false;
|
|
2917
3093
|
onMessageHandler = null;
|
|
3094
|
+
modeHasher;
|
|
3095
|
+
constructor(modeHasher, onMessageHandler = null) {
|
|
3096
|
+
this.modeHasher = modeHasher;
|
|
3097
|
+
this.onMessageHandler = onMessageHandler;
|
|
3098
|
+
types$1.logger.debug(`[MessageQueue2] Initialized`);
|
|
3099
|
+
}
|
|
2918
3100
|
/**
|
|
2919
3101
|
* Set a handler that will be called when a message arrives
|
|
2920
3102
|
*/
|
|
@@ -3089,6 +3271,7 @@ class MessageQueue2 {
|
|
|
3089
3271
|
const firstItem = this.queue[0];
|
|
3090
3272
|
const sameModeMessages = [];
|
|
3091
3273
|
let mode = firstItem.mode;
|
|
3274
|
+
let isolate = firstItem.isolate ?? false;
|
|
3092
3275
|
const targetModeHash = firstItem.modeHash;
|
|
3093
3276
|
if (firstItem.isolate) {
|
|
3094
3277
|
const item = this.queue.shift();
|
|
@@ -3104,7 +3287,9 @@ class MessageQueue2 {
|
|
|
3104
3287
|
const combinedMessage = sameModeMessages.join("\n");
|
|
3105
3288
|
return {
|
|
3106
3289
|
message: combinedMessage,
|
|
3107
|
-
mode
|
|
3290
|
+
mode,
|
|
3291
|
+
hash: targetModeHash,
|
|
3292
|
+
isolate
|
|
3108
3293
|
};
|
|
3109
3294
|
}
|
|
3110
3295
|
/**
|
|
@@ -3999,10 +4184,10 @@ async function doWebAuth(keypair) {
|
|
|
3999
4184
|
console.log("\u2713 Browser opened\n");
|
|
4000
4185
|
console.log("Complete authentication in your browser window.");
|
|
4001
4186
|
} else {
|
|
4002
|
-
console.log("Could not open browser automatically
|
|
4003
|
-
console.log("Please open this URL manually:");
|
|
4004
|
-
console.log(webUrl);
|
|
4187
|
+
console.log("Could not open browser automatically.");
|
|
4005
4188
|
}
|
|
4189
|
+
console.log("\nIf the browser did not open, please copy and paste this URL:");
|
|
4190
|
+
console.log(webUrl);
|
|
4006
4191
|
console.log("");
|
|
4007
4192
|
return await waitForAuthentication(keypair);
|
|
4008
4193
|
}
|
|
@@ -4513,7 +4698,15 @@ async function start(credentials, options = {}) {
|
|
|
4513
4698
|
if (caffeinateStarted) {
|
|
4514
4699
|
types$1.logger.infoDeveloper("Sleep prevention enabled (macOS)");
|
|
4515
4700
|
}
|
|
4516
|
-
const messageQueue = new MessageQueue2((mode) => hashObject(
|
|
4701
|
+
const messageQueue = new MessageQueue2((mode) => hashObject({
|
|
4702
|
+
isPlan: mode.permissionMode === "plan",
|
|
4703
|
+
model: mode.model,
|
|
4704
|
+
fallbackModel: mode.fallbackModel,
|
|
4705
|
+
customSystemPrompt: mode.customSystemPrompt,
|
|
4706
|
+
appendSystemPrompt: mode.appendSystemPrompt,
|
|
4707
|
+
allowedTools: mode.allowedTools,
|
|
4708
|
+
disallowedTools: mode.disallowedTools
|
|
4709
|
+
}));
|
|
4517
4710
|
registerHandlers(session);
|
|
4518
4711
|
let currentPermissionMode = options.permissionMode;
|
|
4519
4712
|
let currentModel = options.model;
|