happy-coder 0.1.11 → 0.1.12
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/bin/happy +1 -0
- package/bin/happy.cmd +1 -0
- package/dist/index.cjs +200 -56
- package/dist/index.mjs +200 -56
- package/dist/lib.d.cts +14 -0
- package/dist/lib.d.mts +14 -0
- package/package.json +2 -2
package/bin/happy
CHANGED
package/bin/happy.cmd
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -264,7 +264,7 @@ async function claudeRemote(opts) {
|
|
|
264
264
|
try {
|
|
265
265
|
types.logger.debug(`[claudeRemote] Starting to iterate over response`);
|
|
266
266
|
for await (const message of response) {
|
|
267
|
-
types.logger.
|
|
267
|
+
types.logger.debugLargeJson(`[claudeRemote] Message ${message.type}`, message);
|
|
268
268
|
formatClaudeMessage(message, opts.onAssistantResult);
|
|
269
269
|
if (message.type === "system" && message.subtype === "init") {
|
|
270
270
|
types.logger.debug(`[claudeRemote] Waiting for session file to be written to disk: ${message.session_id}`);
|
|
@@ -600,8 +600,8 @@ function createSessionScanner(opts) {
|
|
|
600
600
|
let pendingSessions = /* @__PURE__ */ new Set();
|
|
601
601
|
let currentSessionId = null;
|
|
602
602
|
let watchers = /* @__PURE__ */ new Map();
|
|
603
|
-
let
|
|
604
|
-
let
|
|
603
|
+
let processedMessageKeys = /* @__PURE__ */ new Set();
|
|
604
|
+
let unmatchedServerMessageContents = /* @__PURE__ */ new Set();
|
|
605
605
|
const sync = new InvalidateSync(async () => {
|
|
606
606
|
types.logger.debug(`[SESSION_SCANNER] Syncing...`);
|
|
607
607
|
let sessions = [];
|
|
@@ -633,16 +633,16 @@ function createSessionScanner(opts) {
|
|
|
633
633
|
continue;
|
|
634
634
|
}
|
|
635
635
|
let key = getMessageKey(parsed.data);
|
|
636
|
-
if (
|
|
636
|
+
if (processedMessageKeys.has(key)) {
|
|
637
637
|
continue;
|
|
638
638
|
}
|
|
639
|
-
|
|
639
|
+
processedMessageKeys.add(key);
|
|
640
640
|
types.logger.debugLargeJson(`[SESSION_SCANNER] Processing message`, parsed.data);
|
|
641
641
|
types.logger.debug(`[SESSION_SCANNER] Message key (new): ${key}`);
|
|
642
642
|
if (parsed.data.type === "user" && typeof parsed.data.message.content === "string" && parsed.data.isSidechain !== true && parsed.data.isMeta !== true) {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
643
|
+
if (unmatchedServerMessageContents.has(parsed.data.message.content)) {
|
|
644
|
+
types.logger.debug(`[SESSION_SCANNER] Matched server message echo: ${parsed.data.uuid}`);
|
|
645
|
+
unmatchedServerMessageContents.delete(parsed.data.message.content);
|
|
646
646
|
continue;
|
|
647
647
|
}
|
|
648
648
|
}
|
|
@@ -703,13 +703,18 @@ function createSessionScanner(opts) {
|
|
|
703
703
|
sync.invalidate();
|
|
704
704
|
},
|
|
705
705
|
onRemoteUserMessageForDeduplication: (messageContent) => {
|
|
706
|
-
|
|
706
|
+
types.logger.debug(`[SESSION_SCANNER] Adding unmatched server message content: ${messageContent.substring(0, 50)}...`);
|
|
707
|
+
unmatchedServerMessageContents.add(messageContent);
|
|
707
708
|
}
|
|
708
709
|
};
|
|
709
710
|
}
|
|
710
711
|
function getMessageKey(message) {
|
|
711
712
|
if (message.type === "user") {
|
|
712
|
-
|
|
713
|
+
if (Array.isArray(message.message.content) && message.message.content.length > 0 && typeof message.message.content[0] === "object" && "text" in message.message.content[0]) {
|
|
714
|
+
return `user-message-content:${stableStringify(message.message.content[0].text)}`;
|
|
715
|
+
} else {
|
|
716
|
+
return `user-message-content:${stableStringify(message.message.content)}`;
|
|
717
|
+
}
|
|
713
718
|
} else if (message.type === "assistant") {
|
|
714
719
|
const { usage, ...messageWithoutUsage } = message.message;
|
|
715
720
|
return stableStringify(messageWithoutUsage);
|
|
@@ -721,6 +726,9 @@ function getMessageKey(message) {
|
|
|
721
726
|
return `unknown:<error, this should be unreachable>`;
|
|
722
727
|
}
|
|
723
728
|
function stableStringify(obj) {
|
|
729
|
+
if (!obj) {
|
|
730
|
+
return "null";
|
|
731
|
+
}
|
|
724
732
|
return JSON.stringify(sortKeys(obj), null, 2);
|
|
725
733
|
}
|
|
726
734
|
function sortKeys(value) {
|
|
@@ -784,6 +792,11 @@ async function loop(opts) {
|
|
|
784
792
|
interactiveAbortController.abort();
|
|
785
793
|
}
|
|
786
794
|
});
|
|
795
|
+
opts.session.setHandler("abort", () => {
|
|
796
|
+
if (onMessage) {
|
|
797
|
+
onMessage();
|
|
798
|
+
}
|
|
799
|
+
});
|
|
787
800
|
onMessage = () => {
|
|
788
801
|
if (!interactiveAbortController.signal.aborted) {
|
|
789
802
|
abortedOutside = true;
|
|
@@ -793,18 +806,32 @@ async function loop(opts) {
|
|
|
793
806
|
opts.onModeChange(mode);
|
|
794
807
|
}
|
|
795
808
|
}
|
|
809
|
+
opts.session.sendSessionEvent({ type: "message", message: "Inference aborted" });
|
|
796
810
|
interactiveAbortController.abort();
|
|
797
811
|
}
|
|
798
812
|
onMessage = null;
|
|
799
813
|
};
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
814
|
+
try {
|
|
815
|
+
if (opts.onProcessStart) {
|
|
816
|
+
opts.onProcessStart("local");
|
|
817
|
+
}
|
|
818
|
+
await claudeLocal({
|
|
819
|
+
path: opts.path,
|
|
820
|
+
sessionId,
|
|
821
|
+
onSessionFound,
|
|
822
|
+
abort: interactiveAbortController.signal,
|
|
823
|
+
claudeEnvVars: opts.claudeEnvVars,
|
|
824
|
+
claudeArgs: opts.claudeArgs
|
|
825
|
+
});
|
|
826
|
+
} catch (e) {
|
|
827
|
+
if (!interactiveAbortController.signal.aborted) {
|
|
828
|
+
opts.session.sendSessionEvent({ type: "message", message: "Process exited unexpectedly" });
|
|
829
|
+
}
|
|
830
|
+
} finally {
|
|
831
|
+
if (opts.onProcessStop) {
|
|
832
|
+
opts.onProcessStop("local");
|
|
833
|
+
}
|
|
834
|
+
}
|
|
808
835
|
onMessage = null;
|
|
809
836
|
if (!abortedOutside) {
|
|
810
837
|
return;
|
|
@@ -829,6 +856,7 @@ async function loop(opts) {
|
|
|
829
856
|
opts.onModeChange(mode);
|
|
830
857
|
}
|
|
831
858
|
}
|
|
859
|
+
opts.session.sendSessionEvent({ type: "message", message: "Inference aborted" });
|
|
832
860
|
remoteAbortController.abort();
|
|
833
861
|
}
|
|
834
862
|
if (process.stdin.isTTY) {
|
|
@@ -843,6 +871,9 @@ async function loop(opts) {
|
|
|
843
871
|
process.stdin.on("data", abortHandler);
|
|
844
872
|
try {
|
|
845
873
|
types.logger.debug(`Starting claudeRemote with messages: ${currentMessageQueue.size()}`);
|
|
874
|
+
if (opts.onProcessStart) {
|
|
875
|
+
opts.onProcessStart("remote");
|
|
876
|
+
}
|
|
846
877
|
await claudeRemote({
|
|
847
878
|
abort: remoteAbortController.signal,
|
|
848
879
|
sessionId,
|
|
@@ -856,7 +887,14 @@ async function loop(opts) {
|
|
|
856
887
|
claudeEnvVars: opts.claudeEnvVars,
|
|
857
888
|
claudeArgs: opts.claudeArgs
|
|
858
889
|
});
|
|
890
|
+
} catch (e) {
|
|
891
|
+
if (!remoteAbortController.signal.aborted) {
|
|
892
|
+
opts.session.sendSessionEvent({ type: "message", message: "Process exited unexpectedly" });
|
|
893
|
+
}
|
|
859
894
|
} finally {
|
|
895
|
+
if (opts.onProcessStop) {
|
|
896
|
+
opts.onProcessStop("remote");
|
|
897
|
+
}
|
|
860
898
|
process.stdin.off("data", abortHandler);
|
|
861
899
|
if (process.stdin.isTTY) {
|
|
862
900
|
process.stdin.setRawMode(false);
|
|
@@ -971,7 +1009,7 @@ class InterruptController {
|
|
|
971
1009
|
}
|
|
972
1010
|
}
|
|
973
1011
|
|
|
974
|
-
var version = "0.1.
|
|
1012
|
+
var version = "0.1.12";
|
|
975
1013
|
var packageJson = {
|
|
976
1014
|
version: version};
|
|
977
1015
|
|
|
@@ -1027,11 +1065,22 @@ function registerHandlers(session, interruptController, permissionCallbacks, onS
|
|
|
1027
1065
|
return;
|
|
1028
1066
|
}
|
|
1029
1067
|
session.updateAgentState((currentState) => {
|
|
1068
|
+
const request = currentState.requests?.[id];
|
|
1069
|
+
if (!request) return currentState;
|
|
1030
1070
|
let r = { ...currentState.requests };
|
|
1031
1071
|
delete r[id];
|
|
1032
1072
|
return {
|
|
1033
1073
|
...currentState,
|
|
1034
|
-
requests: r
|
|
1074
|
+
requests: r,
|
|
1075
|
+
completedRequests: {
|
|
1076
|
+
...currentState.completedRequests,
|
|
1077
|
+
[id]: {
|
|
1078
|
+
...request,
|
|
1079
|
+
completedAt: Date.now(),
|
|
1080
|
+
status: message.approved ? "approved" : "denied",
|
|
1081
|
+
reason: message.reason
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1035
1084
|
};
|
|
1036
1085
|
});
|
|
1037
1086
|
});
|
|
@@ -1287,59 +1336,96 @@ async function startHTTPDirectProxy(options) {
|
|
|
1287
1336
|
}
|
|
1288
1337
|
|
|
1289
1338
|
async function startClaudeActivityTracker(onThinking) {
|
|
1339
|
+
types.logger.debug(`[ClaudeActivityTracker] Starting activity tracker`);
|
|
1290
1340
|
let requestCounter = 0;
|
|
1291
|
-
const activeRequests = /* @__PURE__ */ new
|
|
1341
|
+
const activeRequests = /* @__PURE__ */ new Map();
|
|
1292
1342
|
let stopThinkingTimeout = null;
|
|
1293
1343
|
let isThinking = false;
|
|
1344
|
+
const REQUEST_TIMEOUT = 5 * 60 * 1e3;
|
|
1345
|
+
const checkAndStopThinking = () => {
|
|
1346
|
+
if (activeRequests.size === 0 && isThinking && !stopThinkingTimeout) {
|
|
1347
|
+
stopThinkingTimeout = setTimeout(() => {
|
|
1348
|
+
if (isThinking && activeRequests.size === 0) {
|
|
1349
|
+
isThinking = false;
|
|
1350
|
+
onThinking(false);
|
|
1351
|
+
}
|
|
1352
|
+
stopThinkingTimeout = null;
|
|
1353
|
+
}, 500);
|
|
1354
|
+
}
|
|
1355
|
+
};
|
|
1294
1356
|
const proxyUrl = await startHTTPDirectProxy({
|
|
1295
1357
|
target: process.env.ANTHROPIC_BASE_URL || "https://api.anthropic.com",
|
|
1296
1358
|
onRequest: (req, proxyReq) => {
|
|
1297
1359
|
if (req.method === "POST" && req.url?.startsWith("/v1/messages")) {
|
|
1298
1360
|
const requestId = ++requestCounter;
|
|
1299
|
-
activeRequests.add(requestId);
|
|
1300
1361
|
req._requestId = requestId;
|
|
1301
1362
|
if (stopThinkingTimeout) {
|
|
1302
1363
|
clearTimeout(stopThinkingTimeout);
|
|
1303
1364
|
stopThinkingTimeout = null;
|
|
1304
1365
|
}
|
|
1366
|
+
const timeout = setTimeout(() => {
|
|
1367
|
+
activeRequests.delete(requestId);
|
|
1368
|
+
checkAndStopThinking();
|
|
1369
|
+
}, REQUEST_TIMEOUT);
|
|
1370
|
+
activeRequests.set(requestId, timeout);
|
|
1305
1371
|
if (!isThinking) {
|
|
1306
|
-
types.logger.debug(`[ClaudeActivityTracker] Thinking started`);
|
|
1307
1372
|
isThinking = true;
|
|
1308
1373
|
onThinking(true);
|
|
1309
1374
|
}
|
|
1310
1375
|
}
|
|
1311
1376
|
},
|
|
1312
1377
|
onResponse: (req, proxyRes) => {
|
|
1378
|
+
proxyRes.headers["connection"] = "close";
|
|
1313
1379
|
if (req.method === "POST" && req.url?.startsWith("/v1/messages")) {
|
|
1314
1380
|
const requestId = req._requestId;
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1381
|
+
const timeout = activeRequests.get(requestId);
|
|
1382
|
+
if (timeout) {
|
|
1383
|
+
clearTimeout(timeout);
|
|
1384
|
+
}
|
|
1385
|
+
let cleaned = false;
|
|
1386
|
+
const cleanupRequest = () => {
|
|
1387
|
+
if (!cleaned) {
|
|
1388
|
+
cleaned = true;
|
|
1389
|
+
activeRequests.delete(requestId);
|
|
1390
|
+
checkAndStopThinking();
|
|
1325
1391
|
}
|
|
1392
|
+
};
|
|
1393
|
+
proxyRes.on("end", () => {
|
|
1394
|
+
cleanupRequest();
|
|
1326
1395
|
});
|
|
1327
|
-
proxyRes.on("error", () => {
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1396
|
+
proxyRes.on("error", (err) => {
|
|
1397
|
+
cleanupRequest();
|
|
1398
|
+
});
|
|
1399
|
+
proxyRes.on("aborted", () => {
|
|
1400
|
+
cleanupRequest();
|
|
1401
|
+
});
|
|
1402
|
+
proxyRes.on("close", () => {
|
|
1403
|
+
cleanupRequest();
|
|
1404
|
+
});
|
|
1405
|
+
req.on("close", () => {
|
|
1406
|
+
cleanupRequest();
|
|
1338
1407
|
});
|
|
1339
1408
|
}
|
|
1340
1409
|
}
|
|
1341
1410
|
});
|
|
1342
|
-
|
|
1411
|
+
const reset = () => {
|
|
1412
|
+
for (const [requestId, timeout] of activeRequests) {
|
|
1413
|
+
clearTimeout(timeout);
|
|
1414
|
+
}
|
|
1415
|
+
activeRequests.clear();
|
|
1416
|
+
if (stopThinkingTimeout) {
|
|
1417
|
+
clearTimeout(stopThinkingTimeout);
|
|
1418
|
+
stopThinkingTimeout = null;
|
|
1419
|
+
}
|
|
1420
|
+
if (isThinking) {
|
|
1421
|
+
isThinking = false;
|
|
1422
|
+
onThinking(false);
|
|
1423
|
+
}
|
|
1424
|
+
};
|
|
1425
|
+
return {
|
|
1426
|
+
proxyUrl,
|
|
1427
|
+
reset
|
|
1428
|
+
};
|
|
1343
1429
|
}
|
|
1344
1430
|
|
|
1345
1431
|
async function start(credentials, options = {}) {
|
|
@@ -1357,11 +1443,11 @@ async function start(credentials, options = {}) {
|
|
|
1357
1443
|
let pingInterval = setInterval(() => {
|
|
1358
1444
|
session.keepAlive(thinking, mode);
|
|
1359
1445
|
}, 2e3);
|
|
1360
|
-
const
|
|
1446
|
+
const activityTracker = await startClaudeActivityTracker((newThinking) => {
|
|
1361
1447
|
thinking = newThinking;
|
|
1362
1448
|
session.keepAlive(thinking, mode);
|
|
1363
1449
|
});
|
|
1364
|
-
process.env.ANTHROPIC_BASE_URL = proxyUrl;
|
|
1450
|
+
process.env.ANTHROPIC_BASE_URL = activityTracker.proxyUrl;
|
|
1365
1451
|
const logPath = await types.logger.logFilePathPromise;
|
|
1366
1452
|
types.logger.infoDeveloper(`Session: ${response.id}`);
|
|
1367
1453
|
types.logger.infoDeveloper(`Logs: ${logPath}`);
|
|
@@ -1380,11 +1466,22 @@ async function start(credentials, options = {}) {
|
|
|
1380
1466
|
}
|
|
1381
1467
|
requests.delete(id);
|
|
1382
1468
|
session.updateAgentState((currentState) => {
|
|
1469
|
+
const request2 = currentState.requests?.[id];
|
|
1470
|
+
if (!request2) return currentState;
|
|
1383
1471
|
let r = { ...currentState.requests };
|
|
1384
1472
|
delete r[id];
|
|
1385
1473
|
return {
|
|
1386
1474
|
...currentState,
|
|
1387
|
-
requests: r
|
|
1475
|
+
requests: r,
|
|
1476
|
+
completedRequests: {
|
|
1477
|
+
...currentState.completedRequests,
|
|
1478
|
+
[id]: {
|
|
1479
|
+
...request2,
|
|
1480
|
+
completedAt: Date.now(),
|
|
1481
|
+
status: "canceled",
|
|
1482
|
+
reason: "Timeout"
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1388
1485
|
};
|
|
1389
1486
|
});
|
|
1390
1487
|
}, 1e3 * 60 * 4.5);
|
|
@@ -1410,7 +1507,8 @@ async function start(credentials, options = {}) {
|
|
|
1410
1507
|
...currentState.requests,
|
|
1411
1508
|
[id]: {
|
|
1412
1509
|
tool: request.name,
|
|
1413
|
-
arguments: request.arguments
|
|
1510
|
+
arguments: request.arguments,
|
|
1511
|
+
createdAt: Date.now()
|
|
1414
1512
|
}
|
|
1415
1513
|
}
|
|
1416
1514
|
}));
|
|
@@ -1446,10 +1544,58 @@ async function start(credentials, options = {}) {
|
|
|
1446
1544
|
mode = newMode;
|
|
1447
1545
|
session.sendSessionEvent({ type: "switch", mode: newMode });
|
|
1448
1546
|
session.keepAlive(thinking, mode);
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1547
|
+
if (newMode === "local") {
|
|
1548
|
+
types.logger.debug("Switching to local mode - clearing pending permission requests");
|
|
1549
|
+
for (const [id, resolve] of requests) {
|
|
1550
|
+
types.logger.debug(`Rejecting pending permission request: ${id}`);
|
|
1551
|
+
resolve({ approved: false, reason: "Session switched to local mode" });
|
|
1552
|
+
}
|
|
1553
|
+
requests.clear();
|
|
1554
|
+
session.updateAgentState((currentState) => {
|
|
1555
|
+
const pendingRequests = currentState.requests || {};
|
|
1556
|
+
const completedRequests = { ...currentState.completedRequests };
|
|
1557
|
+
for (const [id, request] of Object.entries(pendingRequests)) {
|
|
1558
|
+
completedRequests[id] = {
|
|
1559
|
+
...request,
|
|
1560
|
+
completedAt: Date.now(),
|
|
1561
|
+
status: "canceled",
|
|
1562
|
+
reason: "Session switched to local mode"
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
return {
|
|
1566
|
+
...currentState,
|
|
1567
|
+
controlledByUser: true,
|
|
1568
|
+
requests: {},
|
|
1569
|
+
// Clear all pending requests
|
|
1570
|
+
completedRequests
|
|
1571
|
+
};
|
|
1572
|
+
});
|
|
1573
|
+
} else {
|
|
1574
|
+
session.updateAgentState((currentState) => ({
|
|
1575
|
+
...currentState,
|
|
1576
|
+
controlledByUser: false
|
|
1577
|
+
}));
|
|
1578
|
+
}
|
|
1579
|
+
},
|
|
1580
|
+
onProcessStart: (processMode) => {
|
|
1581
|
+
types.logger.debug(`[Process Lifecycle] Starting ${processMode} mode`);
|
|
1582
|
+
activityTracker.reset();
|
|
1583
|
+
types.logger.debug("Starting process - clearing any stale permission requests");
|
|
1584
|
+
for (const [id, resolve] of requests) {
|
|
1585
|
+
types.logger.debug(`Rejecting stale permission request: ${id}`);
|
|
1586
|
+
resolve({ approved: false, reason: "Process restarted" });
|
|
1587
|
+
}
|
|
1588
|
+
requests.clear();
|
|
1589
|
+
},
|
|
1590
|
+
onProcessStop: (processMode) => {
|
|
1591
|
+
types.logger.debug(`[Process Lifecycle] Stopped ${processMode} mode`);
|
|
1592
|
+
activityTracker.reset();
|
|
1593
|
+
types.logger.debug("Stopping process - clearing any stale permission requests");
|
|
1594
|
+
for (const [id, resolve] of requests) {
|
|
1595
|
+
types.logger.debug(`Rejecting stale permission request: ${id}`);
|
|
1596
|
+
resolve({ approved: false, reason: "Process restarted" });
|
|
1597
|
+
}
|
|
1598
|
+
requests.clear();
|
|
1453
1599
|
},
|
|
1454
1600
|
mcpServers: {
|
|
1455
1601
|
"permission": {
|
|
@@ -2068,9 +2214,7 @@ Currently only supported on macOS.
|
|
|
2068
2214
|
options.model = args[++i];
|
|
2069
2215
|
} else if (arg === "-p" || arg === "--permission-mode") {
|
|
2070
2216
|
options.permissionMode = z.z.enum(["auto", "default", "plan"]).parse(args[++i]);
|
|
2071
|
-
} else if (arg === "--local") {
|
|
2072
|
-
i++;
|
|
2073
|
-
} else if (arg === "--happy-starting-mode") {
|
|
2217
|
+
} else if (arg === "--local") ; else if (arg === "--happy-starting-mode") {
|
|
2074
2218
|
options.startingMode = z.z.enum(["local", "remote"]).parse(args[++i]);
|
|
2075
2219
|
} else if (arg === "--claude-env") {
|
|
2076
2220
|
const envVar = args[++i];
|
package/dist/index.mjs
CHANGED
|
@@ -243,7 +243,7 @@ async function claudeRemote(opts) {
|
|
|
243
243
|
try {
|
|
244
244
|
logger.debug(`[claudeRemote] Starting to iterate over response`);
|
|
245
245
|
for await (const message of response) {
|
|
246
|
-
logger.
|
|
246
|
+
logger.debugLargeJson(`[claudeRemote] Message ${message.type}`, message);
|
|
247
247
|
formatClaudeMessage(message, opts.onAssistantResult);
|
|
248
248
|
if (message.type === "system" && message.subtype === "init") {
|
|
249
249
|
logger.debug(`[claudeRemote] Waiting for session file to be written to disk: ${message.session_id}`);
|
|
@@ -579,8 +579,8 @@ function createSessionScanner(opts) {
|
|
|
579
579
|
let pendingSessions = /* @__PURE__ */ new Set();
|
|
580
580
|
let currentSessionId = null;
|
|
581
581
|
let watchers = /* @__PURE__ */ new Map();
|
|
582
|
-
let
|
|
583
|
-
let
|
|
582
|
+
let processedMessageKeys = /* @__PURE__ */ new Set();
|
|
583
|
+
let unmatchedServerMessageContents = /* @__PURE__ */ new Set();
|
|
584
584
|
const sync = new InvalidateSync(async () => {
|
|
585
585
|
logger.debug(`[SESSION_SCANNER] Syncing...`);
|
|
586
586
|
let sessions = [];
|
|
@@ -612,16 +612,16 @@ function createSessionScanner(opts) {
|
|
|
612
612
|
continue;
|
|
613
613
|
}
|
|
614
614
|
let key = getMessageKey(parsed.data);
|
|
615
|
-
if (
|
|
615
|
+
if (processedMessageKeys.has(key)) {
|
|
616
616
|
continue;
|
|
617
617
|
}
|
|
618
|
-
|
|
618
|
+
processedMessageKeys.add(key);
|
|
619
619
|
logger.debugLargeJson(`[SESSION_SCANNER] Processing message`, parsed.data);
|
|
620
620
|
logger.debug(`[SESSION_SCANNER] Message key (new): ${key}`);
|
|
621
621
|
if (parsed.data.type === "user" && typeof parsed.data.message.content === "string" && parsed.data.isSidechain !== true && parsed.data.isMeta !== true) {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
622
|
+
if (unmatchedServerMessageContents.has(parsed.data.message.content)) {
|
|
623
|
+
logger.debug(`[SESSION_SCANNER] Matched server message echo: ${parsed.data.uuid}`);
|
|
624
|
+
unmatchedServerMessageContents.delete(parsed.data.message.content);
|
|
625
625
|
continue;
|
|
626
626
|
}
|
|
627
627
|
}
|
|
@@ -682,13 +682,18 @@ function createSessionScanner(opts) {
|
|
|
682
682
|
sync.invalidate();
|
|
683
683
|
},
|
|
684
684
|
onRemoteUserMessageForDeduplication: (messageContent) => {
|
|
685
|
-
|
|
685
|
+
logger.debug(`[SESSION_SCANNER] Adding unmatched server message content: ${messageContent.substring(0, 50)}...`);
|
|
686
|
+
unmatchedServerMessageContents.add(messageContent);
|
|
686
687
|
}
|
|
687
688
|
};
|
|
688
689
|
}
|
|
689
690
|
function getMessageKey(message) {
|
|
690
691
|
if (message.type === "user") {
|
|
691
|
-
|
|
692
|
+
if (Array.isArray(message.message.content) && message.message.content.length > 0 && typeof message.message.content[0] === "object" && "text" in message.message.content[0]) {
|
|
693
|
+
return `user-message-content:${stableStringify(message.message.content[0].text)}`;
|
|
694
|
+
} else {
|
|
695
|
+
return `user-message-content:${stableStringify(message.message.content)}`;
|
|
696
|
+
}
|
|
692
697
|
} else if (message.type === "assistant") {
|
|
693
698
|
const { usage, ...messageWithoutUsage } = message.message;
|
|
694
699
|
return stableStringify(messageWithoutUsage);
|
|
@@ -700,6 +705,9 @@ function getMessageKey(message) {
|
|
|
700
705
|
return `unknown:<error, this should be unreachable>`;
|
|
701
706
|
}
|
|
702
707
|
function stableStringify(obj) {
|
|
708
|
+
if (!obj) {
|
|
709
|
+
return "null";
|
|
710
|
+
}
|
|
703
711
|
return JSON.stringify(sortKeys(obj), null, 2);
|
|
704
712
|
}
|
|
705
713
|
function sortKeys(value) {
|
|
@@ -763,6 +771,11 @@ async function loop(opts) {
|
|
|
763
771
|
interactiveAbortController.abort();
|
|
764
772
|
}
|
|
765
773
|
});
|
|
774
|
+
opts.session.setHandler("abort", () => {
|
|
775
|
+
if (onMessage) {
|
|
776
|
+
onMessage();
|
|
777
|
+
}
|
|
778
|
+
});
|
|
766
779
|
onMessage = () => {
|
|
767
780
|
if (!interactiveAbortController.signal.aborted) {
|
|
768
781
|
abortedOutside = true;
|
|
@@ -772,18 +785,32 @@ async function loop(opts) {
|
|
|
772
785
|
opts.onModeChange(mode);
|
|
773
786
|
}
|
|
774
787
|
}
|
|
788
|
+
opts.session.sendSessionEvent({ type: "message", message: "Inference aborted" });
|
|
775
789
|
interactiveAbortController.abort();
|
|
776
790
|
}
|
|
777
791
|
onMessage = null;
|
|
778
792
|
};
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
793
|
+
try {
|
|
794
|
+
if (opts.onProcessStart) {
|
|
795
|
+
opts.onProcessStart("local");
|
|
796
|
+
}
|
|
797
|
+
await claudeLocal({
|
|
798
|
+
path: opts.path,
|
|
799
|
+
sessionId,
|
|
800
|
+
onSessionFound,
|
|
801
|
+
abort: interactiveAbortController.signal,
|
|
802
|
+
claudeEnvVars: opts.claudeEnvVars,
|
|
803
|
+
claudeArgs: opts.claudeArgs
|
|
804
|
+
});
|
|
805
|
+
} catch (e) {
|
|
806
|
+
if (!interactiveAbortController.signal.aborted) {
|
|
807
|
+
opts.session.sendSessionEvent({ type: "message", message: "Process exited unexpectedly" });
|
|
808
|
+
}
|
|
809
|
+
} finally {
|
|
810
|
+
if (opts.onProcessStop) {
|
|
811
|
+
opts.onProcessStop("local");
|
|
812
|
+
}
|
|
813
|
+
}
|
|
787
814
|
onMessage = null;
|
|
788
815
|
if (!abortedOutside) {
|
|
789
816
|
return;
|
|
@@ -808,6 +835,7 @@ async function loop(opts) {
|
|
|
808
835
|
opts.onModeChange(mode);
|
|
809
836
|
}
|
|
810
837
|
}
|
|
838
|
+
opts.session.sendSessionEvent({ type: "message", message: "Inference aborted" });
|
|
811
839
|
remoteAbortController.abort();
|
|
812
840
|
}
|
|
813
841
|
if (process.stdin.isTTY) {
|
|
@@ -822,6 +850,9 @@ async function loop(opts) {
|
|
|
822
850
|
process.stdin.on("data", abortHandler);
|
|
823
851
|
try {
|
|
824
852
|
logger.debug(`Starting claudeRemote with messages: ${currentMessageQueue.size()}`);
|
|
853
|
+
if (opts.onProcessStart) {
|
|
854
|
+
opts.onProcessStart("remote");
|
|
855
|
+
}
|
|
825
856
|
await claudeRemote({
|
|
826
857
|
abort: remoteAbortController.signal,
|
|
827
858
|
sessionId,
|
|
@@ -835,7 +866,14 @@ async function loop(opts) {
|
|
|
835
866
|
claudeEnvVars: opts.claudeEnvVars,
|
|
836
867
|
claudeArgs: opts.claudeArgs
|
|
837
868
|
});
|
|
869
|
+
} catch (e) {
|
|
870
|
+
if (!remoteAbortController.signal.aborted) {
|
|
871
|
+
opts.session.sendSessionEvent({ type: "message", message: "Process exited unexpectedly" });
|
|
872
|
+
}
|
|
838
873
|
} finally {
|
|
874
|
+
if (opts.onProcessStop) {
|
|
875
|
+
opts.onProcessStop("remote");
|
|
876
|
+
}
|
|
839
877
|
process.stdin.off("data", abortHandler);
|
|
840
878
|
if (process.stdin.isTTY) {
|
|
841
879
|
process.stdin.setRawMode(false);
|
|
@@ -950,7 +988,7 @@ class InterruptController {
|
|
|
950
988
|
}
|
|
951
989
|
}
|
|
952
990
|
|
|
953
|
-
var version = "0.1.
|
|
991
|
+
var version = "0.1.12";
|
|
954
992
|
var packageJson = {
|
|
955
993
|
version: version};
|
|
956
994
|
|
|
@@ -1006,11 +1044,22 @@ function registerHandlers(session, interruptController, permissionCallbacks, onS
|
|
|
1006
1044
|
return;
|
|
1007
1045
|
}
|
|
1008
1046
|
session.updateAgentState((currentState) => {
|
|
1047
|
+
const request = currentState.requests?.[id];
|
|
1048
|
+
if (!request) return currentState;
|
|
1009
1049
|
let r = { ...currentState.requests };
|
|
1010
1050
|
delete r[id];
|
|
1011
1051
|
return {
|
|
1012
1052
|
...currentState,
|
|
1013
|
-
requests: r
|
|
1053
|
+
requests: r,
|
|
1054
|
+
completedRequests: {
|
|
1055
|
+
...currentState.completedRequests,
|
|
1056
|
+
[id]: {
|
|
1057
|
+
...request,
|
|
1058
|
+
completedAt: Date.now(),
|
|
1059
|
+
status: message.approved ? "approved" : "denied",
|
|
1060
|
+
reason: message.reason
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1014
1063
|
};
|
|
1015
1064
|
});
|
|
1016
1065
|
});
|
|
@@ -1266,59 +1315,96 @@ async function startHTTPDirectProxy(options) {
|
|
|
1266
1315
|
}
|
|
1267
1316
|
|
|
1268
1317
|
async function startClaudeActivityTracker(onThinking) {
|
|
1318
|
+
logger.debug(`[ClaudeActivityTracker] Starting activity tracker`);
|
|
1269
1319
|
let requestCounter = 0;
|
|
1270
|
-
const activeRequests = /* @__PURE__ */ new
|
|
1320
|
+
const activeRequests = /* @__PURE__ */ new Map();
|
|
1271
1321
|
let stopThinkingTimeout = null;
|
|
1272
1322
|
let isThinking = false;
|
|
1323
|
+
const REQUEST_TIMEOUT = 5 * 60 * 1e3;
|
|
1324
|
+
const checkAndStopThinking = () => {
|
|
1325
|
+
if (activeRequests.size === 0 && isThinking && !stopThinkingTimeout) {
|
|
1326
|
+
stopThinkingTimeout = setTimeout(() => {
|
|
1327
|
+
if (isThinking && activeRequests.size === 0) {
|
|
1328
|
+
isThinking = false;
|
|
1329
|
+
onThinking(false);
|
|
1330
|
+
}
|
|
1331
|
+
stopThinkingTimeout = null;
|
|
1332
|
+
}, 500);
|
|
1333
|
+
}
|
|
1334
|
+
};
|
|
1273
1335
|
const proxyUrl = await startHTTPDirectProxy({
|
|
1274
1336
|
target: process.env.ANTHROPIC_BASE_URL || "https://api.anthropic.com",
|
|
1275
1337
|
onRequest: (req, proxyReq) => {
|
|
1276
1338
|
if (req.method === "POST" && req.url?.startsWith("/v1/messages")) {
|
|
1277
1339
|
const requestId = ++requestCounter;
|
|
1278
|
-
activeRequests.add(requestId);
|
|
1279
1340
|
req._requestId = requestId;
|
|
1280
1341
|
if (stopThinkingTimeout) {
|
|
1281
1342
|
clearTimeout(stopThinkingTimeout);
|
|
1282
1343
|
stopThinkingTimeout = null;
|
|
1283
1344
|
}
|
|
1345
|
+
const timeout = setTimeout(() => {
|
|
1346
|
+
activeRequests.delete(requestId);
|
|
1347
|
+
checkAndStopThinking();
|
|
1348
|
+
}, REQUEST_TIMEOUT);
|
|
1349
|
+
activeRequests.set(requestId, timeout);
|
|
1284
1350
|
if (!isThinking) {
|
|
1285
|
-
logger.debug(`[ClaudeActivityTracker] Thinking started`);
|
|
1286
1351
|
isThinking = true;
|
|
1287
1352
|
onThinking(true);
|
|
1288
1353
|
}
|
|
1289
1354
|
}
|
|
1290
1355
|
},
|
|
1291
1356
|
onResponse: (req, proxyRes) => {
|
|
1357
|
+
proxyRes.headers["connection"] = "close";
|
|
1292
1358
|
if (req.method === "POST" && req.url?.startsWith("/v1/messages")) {
|
|
1293
1359
|
const requestId = req._requestId;
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1360
|
+
const timeout = activeRequests.get(requestId);
|
|
1361
|
+
if (timeout) {
|
|
1362
|
+
clearTimeout(timeout);
|
|
1363
|
+
}
|
|
1364
|
+
let cleaned = false;
|
|
1365
|
+
const cleanupRequest = () => {
|
|
1366
|
+
if (!cleaned) {
|
|
1367
|
+
cleaned = true;
|
|
1368
|
+
activeRequests.delete(requestId);
|
|
1369
|
+
checkAndStopThinking();
|
|
1304
1370
|
}
|
|
1371
|
+
};
|
|
1372
|
+
proxyRes.on("end", () => {
|
|
1373
|
+
cleanupRequest();
|
|
1305
1374
|
});
|
|
1306
|
-
proxyRes.on("error", () => {
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1375
|
+
proxyRes.on("error", (err) => {
|
|
1376
|
+
cleanupRequest();
|
|
1377
|
+
});
|
|
1378
|
+
proxyRes.on("aborted", () => {
|
|
1379
|
+
cleanupRequest();
|
|
1380
|
+
});
|
|
1381
|
+
proxyRes.on("close", () => {
|
|
1382
|
+
cleanupRequest();
|
|
1383
|
+
});
|
|
1384
|
+
req.on("close", () => {
|
|
1385
|
+
cleanupRequest();
|
|
1317
1386
|
});
|
|
1318
1387
|
}
|
|
1319
1388
|
}
|
|
1320
1389
|
});
|
|
1321
|
-
|
|
1390
|
+
const reset = () => {
|
|
1391
|
+
for (const [requestId, timeout] of activeRequests) {
|
|
1392
|
+
clearTimeout(timeout);
|
|
1393
|
+
}
|
|
1394
|
+
activeRequests.clear();
|
|
1395
|
+
if (stopThinkingTimeout) {
|
|
1396
|
+
clearTimeout(stopThinkingTimeout);
|
|
1397
|
+
stopThinkingTimeout = null;
|
|
1398
|
+
}
|
|
1399
|
+
if (isThinking) {
|
|
1400
|
+
isThinking = false;
|
|
1401
|
+
onThinking(false);
|
|
1402
|
+
}
|
|
1403
|
+
};
|
|
1404
|
+
return {
|
|
1405
|
+
proxyUrl,
|
|
1406
|
+
reset
|
|
1407
|
+
};
|
|
1322
1408
|
}
|
|
1323
1409
|
|
|
1324
1410
|
async function start(credentials, options = {}) {
|
|
@@ -1336,11 +1422,11 @@ async function start(credentials, options = {}) {
|
|
|
1336
1422
|
let pingInterval = setInterval(() => {
|
|
1337
1423
|
session.keepAlive(thinking, mode);
|
|
1338
1424
|
}, 2e3);
|
|
1339
|
-
const
|
|
1425
|
+
const activityTracker = await startClaudeActivityTracker((newThinking) => {
|
|
1340
1426
|
thinking = newThinking;
|
|
1341
1427
|
session.keepAlive(thinking, mode);
|
|
1342
1428
|
});
|
|
1343
|
-
process.env.ANTHROPIC_BASE_URL = proxyUrl;
|
|
1429
|
+
process.env.ANTHROPIC_BASE_URL = activityTracker.proxyUrl;
|
|
1344
1430
|
const logPath = await logger.logFilePathPromise;
|
|
1345
1431
|
logger.infoDeveloper(`Session: ${response.id}`);
|
|
1346
1432
|
logger.infoDeveloper(`Logs: ${logPath}`);
|
|
@@ -1359,11 +1445,22 @@ async function start(credentials, options = {}) {
|
|
|
1359
1445
|
}
|
|
1360
1446
|
requests.delete(id);
|
|
1361
1447
|
session.updateAgentState((currentState) => {
|
|
1448
|
+
const request2 = currentState.requests?.[id];
|
|
1449
|
+
if (!request2) return currentState;
|
|
1362
1450
|
let r = { ...currentState.requests };
|
|
1363
1451
|
delete r[id];
|
|
1364
1452
|
return {
|
|
1365
1453
|
...currentState,
|
|
1366
|
-
requests: r
|
|
1454
|
+
requests: r,
|
|
1455
|
+
completedRequests: {
|
|
1456
|
+
...currentState.completedRequests,
|
|
1457
|
+
[id]: {
|
|
1458
|
+
...request2,
|
|
1459
|
+
completedAt: Date.now(),
|
|
1460
|
+
status: "canceled",
|
|
1461
|
+
reason: "Timeout"
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1367
1464
|
};
|
|
1368
1465
|
});
|
|
1369
1466
|
}, 1e3 * 60 * 4.5);
|
|
@@ -1389,7 +1486,8 @@ async function start(credentials, options = {}) {
|
|
|
1389
1486
|
...currentState.requests,
|
|
1390
1487
|
[id]: {
|
|
1391
1488
|
tool: request.name,
|
|
1392
|
-
arguments: request.arguments
|
|
1489
|
+
arguments: request.arguments,
|
|
1490
|
+
createdAt: Date.now()
|
|
1393
1491
|
}
|
|
1394
1492
|
}
|
|
1395
1493
|
}));
|
|
@@ -1425,10 +1523,58 @@ async function start(credentials, options = {}) {
|
|
|
1425
1523
|
mode = newMode;
|
|
1426
1524
|
session.sendSessionEvent({ type: "switch", mode: newMode });
|
|
1427
1525
|
session.keepAlive(thinking, mode);
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1526
|
+
if (newMode === "local") {
|
|
1527
|
+
logger.debug("Switching to local mode - clearing pending permission requests");
|
|
1528
|
+
for (const [id, resolve] of requests) {
|
|
1529
|
+
logger.debug(`Rejecting pending permission request: ${id}`);
|
|
1530
|
+
resolve({ approved: false, reason: "Session switched to local mode" });
|
|
1531
|
+
}
|
|
1532
|
+
requests.clear();
|
|
1533
|
+
session.updateAgentState((currentState) => {
|
|
1534
|
+
const pendingRequests = currentState.requests || {};
|
|
1535
|
+
const completedRequests = { ...currentState.completedRequests };
|
|
1536
|
+
for (const [id, request] of Object.entries(pendingRequests)) {
|
|
1537
|
+
completedRequests[id] = {
|
|
1538
|
+
...request,
|
|
1539
|
+
completedAt: Date.now(),
|
|
1540
|
+
status: "canceled",
|
|
1541
|
+
reason: "Session switched to local mode"
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
return {
|
|
1545
|
+
...currentState,
|
|
1546
|
+
controlledByUser: true,
|
|
1547
|
+
requests: {},
|
|
1548
|
+
// Clear all pending requests
|
|
1549
|
+
completedRequests
|
|
1550
|
+
};
|
|
1551
|
+
});
|
|
1552
|
+
} else {
|
|
1553
|
+
session.updateAgentState((currentState) => ({
|
|
1554
|
+
...currentState,
|
|
1555
|
+
controlledByUser: false
|
|
1556
|
+
}));
|
|
1557
|
+
}
|
|
1558
|
+
},
|
|
1559
|
+
onProcessStart: (processMode) => {
|
|
1560
|
+
logger.debug(`[Process Lifecycle] Starting ${processMode} mode`);
|
|
1561
|
+
activityTracker.reset();
|
|
1562
|
+
logger.debug("Starting process - clearing any stale permission requests");
|
|
1563
|
+
for (const [id, resolve] of requests) {
|
|
1564
|
+
logger.debug(`Rejecting stale permission request: ${id}`);
|
|
1565
|
+
resolve({ approved: false, reason: "Process restarted" });
|
|
1566
|
+
}
|
|
1567
|
+
requests.clear();
|
|
1568
|
+
},
|
|
1569
|
+
onProcessStop: (processMode) => {
|
|
1570
|
+
logger.debug(`[Process Lifecycle] Stopped ${processMode} mode`);
|
|
1571
|
+
activityTracker.reset();
|
|
1572
|
+
logger.debug("Stopping process - clearing any stale permission requests");
|
|
1573
|
+
for (const [id, resolve] of requests) {
|
|
1574
|
+
logger.debug(`Rejecting stale permission request: ${id}`);
|
|
1575
|
+
resolve({ approved: false, reason: "Process restarted" });
|
|
1576
|
+
}
|
|
1577
|
+
requests.clear();
|
|
1432
1578
|
},
|
|
1433
1579
|
mcpServers: {
|
|
1434
1580
|
"permission": {
|
|
@@ -2047,9 +2193,7 @@ Currently only supported on macOS.
|
|
|
2047
2193
|
options.model = args[++i];
|
|
2048
2194
|
} else if (arg === "-p" || arg === "--permission-mode") {
|
|
2049
2195
|
options.permissionMode = z$1.enum(["auto", "default", "plan"]).parse(args[++i]);
|
|
2050
|
-
} else if (arg === "--local") {
|
|
2051
|
-
i++;
|
|
2052
|
-
} else if (arg === "--happy-starting-mode") {
|
|
2196
|
+
} else if (arg === "--local") ; else if (arg === "--happy-starting-mode") {
|
|
2053
2197
|
options.startingMode = z$1.enum(["local", "remote"]).parse(args[++i]);
|
|
2054
2198
|
} else if (arg === "--claude-env") {
|
|
2055
2199
|
const envVar = args[++i];
|
package/dist/lib.d.cts
CHANGED
|
@@ -369,6 +369,17 @@ type AgentState = {
|
|
|
369
369
|
[id: string]: {
|
|
370
370
|
tool: string;
|
|
371
371
|
arguments: any;
|
|
372
|
+
createdAt: number;
|
|
373
|
+
};
|
|
374
|
+
};
|
|
375
|
+
completedRequests?: {
|
|
376
|
+
[id: string]: {
|
|
377
|
+
tool: string;
|
|
378
|
+
arguments: any;
|
|
379
|
+
createdAt: number;
|
|
380
|
+
completedAt: number;
|
|
381
|
+
status: 'canceled' | 'denied' | 'approved';
|
|
382
|
+
reason?: string;
|
|
372
383
|
};
|
|
373
384
|
};
|
|
374
385
|
};
|
|
@@ -396,6 +407,9 @@ declare class ApiSessionClient extends EventEmitter {
|
|
|
396
407
|
sendSessionEvent(event: {
|
|
397
408
|
type: 'switch';
|
|
398
409
|
mode: 'local' | 'remote';
|
|
410
|
+
} | {
|
|
411
|
+
type: 'message';
|
|
412
|
+
message: string;
|
|
399
413
|
}, id?: string): void;
|
|
400
414
|
/**
|
|
401
415
|
* Send a ping message to keep the connection alive
|
package/dist/lib.d.mts
CHANGED
|
@@ -369,6 +369,17 @@ type AgentState = {
|
|
|
369
369
|
[id: string]: {
|
|
370
370
|
tool: string;
|
|
371
371
|
arguments: any;
|
|
372
|
+
createdAt: number;
|
|
373
|
+
};
|
|
374
|
+
};
|
|
375
|
+
completedRequests?: {
|
|
376
|
+
[id: string]: {
|
|
377
|
+
tool: string;
|
|
378
|
+
arguments: any;
|
|
379
|
+
createdAt: number;
|
|
380
|
+
completedAt: number;
|
|
381
|
+
status: 'canceled' | 'denied' | 'approved';
|
|
382
|
+
reason?: string;
|
|
372
383
|
};
|
|
373
384
|
};
|
|
374
385
|
};
|
|
@@ -396,6 +407,9 @@ declare class ApiSessionClient extends EventEmitter {
|
|
|
396
407
|
sendSessionEvent(event: {
|
|
397
408
|
type: 'switch';
|
|
398
409
|
mode: 'local' | 'remote';
|
|
410
|
+
} | {
|
|
411
|
+
type: 'message';
|
|
412
|
+
message: string;
|
|
399
413
|
}, id?: string): void;
|
|
400
414
|
/**
|
|
401
415
|
* Send a ping message to keep the connection alive
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "happy-coder",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Claude Code session sharing CLI",
|
|
5
5
|
"author": "Kirill Dubovitskiy",
|
|
6
6
|
"license": "MIT",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"dev:local-server": "HANDY_SERVER_URL=http://localhost:3005 npx tsx --env-file .env.sample src/index.ts"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@anthropic-ai/claude-code": "^1.0.
|
|
55
|
+
"@anthropic-ai/claude-code": "^1.0.68",
|
|
56
56
|
"@anthropic-ai/sdk": "^0.56.0",
|
|
57
57
|
"@modelcontextprotocol/sdk": "^1.15.1",
|
|
58
58
|
"@stablelib/base64": "^2.0.1",
|