happy-imou-cloud 2.0.12 → 2.0.13
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-cloud.mjs +1 -1
- package/dist/ConversationHistory-V3VLmjJf.cjs +868 -0
- package/dist/ConversationHistory-_ciJNIgH.mjs +856 -0
- package/dist/{api-BxXBKBUy.mjs → api-D1meoL-9.mjs} +2 -2
- package/dist/{api-B4g8VLUn.cjs → api-DH5-IqeM.cjs} +2 -2
- package/dist/{command-CHiLfBa4.mjs → command-CMvWClny.mjs} +3 -3
- package/dist/{command-DVt_YmE6.cjs → command-Ch8Dgidj.cjs} +3 -3
- package/dist/createKeepAliveController-C5cQlDRr.mjs +51 -0
- package/dist/createKeepAliveController-DO8H6d5E.cjs +54 -0
- package/dist/{index-CWom7mSf.cjs → index-CryJfCh5.cjs} +10 -11
- package/dist/{index-DaAkW0VN.mjs → index-Cxrx9m5D.mjs} +9 -9
- package/dist/index.cjs +3 -3
- package/dist/index.mjs +3 -3
- package/dist/lib.cjs +1 -1
- package/dist/lib.mjs +1 -1
- package/dist/{persistence-8pNEvzaq.mjs → persistence-9Iu0wGNM.mjs} +1 -1
- package/dist/{persistence-DScOANDE.cjs → persistence-Bl3FYvwd.cjs} +1 -1
- package/dist/{registerKillSessionHandler-CNNguWyD.mjs → registerKillSessionHandler-BElGmD1E.mjs} +5 -541
- package/dist/{registerKillSessionHandler-Dr1inhTc.cjs → registerKillSessionHandler-BjkY-oUn.cjs} +4 -549
- package/dist/{runClaude-h-8llTrI.cjs → runClaude-CDZxAF3l.cjs} +129 -630
- package/dist/{runClaude-BcvOkIwh.mjs → runClaude-D7dF4RDM.mjs} +126 -627
- package/dist/{runCodex-CA58KUHf.cjs → runCodex-Cik8VzFs.cjs} +224 -17
- package/dist/{runCodex-ClJUgipy.mjs → runCodex-DnGz1XES.mjs} +213 -6
- package/dist/{runGemini-dAr7Gcn8.mjs → runGemini-B8tXMHeL.mjs} +5 -5
- package/dist/{runGemini-IFHhFMSU.cjs → runGemini-BM2BQ4I7.cjs} +13 -13
- package/package.json +9 -9
- package/scripts/build.mjs +66 -66
- package/scripts/devtools/README.md +9 -9
- package/scripts/e2e/fake-codex-acp-agent.mjs +139 -139
- package/scripts/e2e/local-server-session-roundtrip.mjs +1063 -1063
- package/scripts/release-smoke.mjs +202 -202
- package/dist/BaseReasoningProcessor-BrKUKAOr.cjs +0 -323
- package/dist/BaseReasoningProcessor-DrHf5B98.mjs +0 -320
- package/dist/ProviderSelectionHandler-BCDvmifJ.cjs +0 -265
- package/dist/ProviderSelectionHandler-BuZarTDc.mjs +0 -261
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var node_crypto = require('node:crypto');
|
|
4
|
-
var api = require('./api-
|
|
4
|
+
var api = require('./api-DH5-IqeM.cjs');
|
|
5
5
|
require('cross-spawn');
|
|
6
6
|
require('@agentclientprotocol/sdk');
|
|
7
|
-
var index = require('./index-
|
|
7
|
+
var index = require('./index-CryJfCh5.cjs');
|
|
8
8
|
require('ps-list');
|
|
9
9
|
require('fs');
|
|
10
10
|
require('path');
|
|
@@ -13,7 +13,7 @@ require('child_process');
|
|
|
13
13
|
var fs$1 = require('node:fs');
|
|
14
14
|
var path = require('node:path');
|
|
15
15
|
var os = require('node:os');
|
|
16
|
-
require('./persistence-
|
|
16
|
+
require('./persistence-Bl3FYvwd.cjs');
|
|
17
17
|
var promises = require('node:fs/promises');
|
|
18
18
|
var fs = require('fs/promises');
|
|
19
19
|
require('crypto');
|
|
@@ -25,9 +25,9 @@ require('tweetnacl');
|
|
|
25
25
|
require('open');
|
|
26
26
|
var React = require('react');
|
|
27
27
|
var ink = require('ink');
|
|
28
|
-
var
|
|
28
|
+
var createKeepAliveController = require('./createKeepAliveController-DO8H6d5E.cjs');
|
|
29
29
|
var types = require('./types-DVk3crez.cjs');
|
|
30
|
-
var registerKillSessionHandler = require('./registerKillSessionHandler-
|
|
30
|
+
var registerKillSessionHandler = require('./registerKillSessionHandler-BjkY-oUn.cjs');
|
|
31
31
|
var node_readline = require('node:readline');
|
|
32
32
|
var node_url = require('node:url');
|
|
33
33
|
require('socket.io-client');
|
|
@@ -82,7 +82,7 @@ class Session {
|
|
|
82
82
|
this._onModeChange = opts.onModeChange;
|
|
83
83
|
this.hookSettingsPath = opts.hookSettingsPath;
|
|
84
84
|
this.jsRuntime = opts.jsRuntime ?? "node";
|
|
85
|
-
this.keepAliveController =
|
|
85
|
+
this.keepAliveController = createKeepAliveController.createKeepAliveController({
|
|
86
86
|
initialMode: this.mode,
|
|
87
87
|
initialThinking: this.thinking,
|
|
88
88
|
send: (thinking, mode) => {
|
|
@@ -718,50 +718,6 @@ async function claudeLocalLauncher(session) {
|
|
|
718
718
|
return exitReason || { type: "exit", code: 0 };
|
|
719
719
|
}
|
|
720
720
|
|
|
721
|
-
function parseCompact(message) {
|
|
722
|
-
const trimmed = message.trim();
|
|
723
|
-
if (trimmed === "/compact") {
|
|
724
|
-
return {
|
|
725
|
-
isCompact: true,
|
|
726
|
-
originalMessage: trimmed
|
|
727
|
-
};
|
|
728
|
-
}
|
|
729
|
-
if (trimmed.startsWith("/compact ")) {
|
|
730
|
-
return {
|
|
731
|
-
isCompact: true,
|
|
732
|
-
originalMessage: trimmed
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
return {
|
|
736
|
-
isCompact: false,
|
|
737
|
-
originalMessage: message
|
|
738
|
-
};
|
|
739
|
-
}
|
|
740
|
-
function parseClear(message) {
|
|
741
|
-
const trimmed = message.trim();
|
|
742
|
-
return {
|
|
743
|
-
isClear: trimmed === "/clear"
|
|
744
|
-
};
|
|
745
|
-
}
|
|
746
|
-
function parseSpecialCommand(message) {
|
|
747
|
-
const compactResult = parseCompact(message);
|
|
748
|
-
if (compactResult.isCompact) {
|
|
749
|
-
return {
|
|
750
|
-
type: "compact",
|
|
751
|
-
originalMessage: compactResult.originalMessage
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
const clearResult = parseClear(message);
|
|
755
|
-
if (clearResult.isClear) {
|
|
756
|
-
return {
|
|
757
|
-
type: "clear"
|
|
758
|
-
};
|
|
759
|
-
}
|
|
760
|
-
return {
|
|
761
|
-
type: null
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
|
|
765
721
|
const RemoteModeDisplay = ({ messageBuffer, logPath, onExit, onSwitchToLocal }) => {
|
|
766
722
|
const [messages, setMessages] = React.useState([]);
|
|
767
723
|
const [confirmationMode, setConfirmationMode] = React.useState(null);
|
|
@@ -887,582 +843,6 @@ const RemoteModeDisplay = ({ messageBuffer, logPath, onExit, onSwitchToLocal })
|
|
|
887
843
|
));
|
|
888
844
|
};
|
|
889
845
|
|
|
890
|
-
function getClaudeSettingsPath() {
|
|
891
|
-
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), ".claude");
|
|
892
|
-
return path.join(claudeConfigDir, "settings.json");
|
|
893
|
-
}
|
|
894
|
-
function readClaudeSettings() {
|
|
895
|
-
try {
|
|
896
|
-
const settingsPath = getClaudeSettingsPath();
|
|
897
|
-
if (!fs$1.existsSync(settingsPath)) {
|
|
898
|
-
api.logger.debug(`[ClaudeSettings] No Claude settings file found at ${settingsPath}`);
|
|
899
|
-
return null;
|
|
900
|
-
}
|
|
901
|
-
const settingsContent = fs$1.readFileSync(settingsPath, "utf-8");
|
|
902
|
-
const settings = JSON.parse(settingsContent);
|
|
903
|
-
api.logger.debug(`[ClaudeSettings] Successfully read Claude settings from ${settingsPath}`);
|
|
904
|
-
api.logger.debug(`[ClaudeSettings] includeCoAuthoredBy: ${settings.includeCoAuthoredBy}`);
|
|
905
|
-
return settings;
|
|
906
|
-
} catch (error) {
|
|
907
|
-
api.logger.debug(`[ClaudeSettings] Error reading Claude settings: ${error}`);
|
|
908
|
-
return null;
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
function shouldIncludeCoAuthoredBy() {
|
|
912
|
-
const settings = readClaudeSettings();
|
|
913
|
-
if (!settings || settings.includeCoAuthoredBy === void 0) {
|
|
914
|
-
return true;
|
|
915
|
-
}
|
|
916
|
-
return settings.includeCoAuthoredBy;
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
const BASE_SYSTEM_PROMPT = (() => index.trimIdent(`
|
|
920
|
-
Help human.
|
|
921
|
-
`))();
|
|
922
|
-
const CO_AUTHORED_CREDITS = (() => index.trimIdent(`
|
|
923
|
-
When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
|
|
924
|
-
|
|
925
|
-
<main commit message>
|
|
926
|
-
|
|
927
|
-
Generated with [Claude Code](https://claude.ai/code)
|
|
928
|
-
via [Happy](https://happy.engineering)
|
|
929
|
-
|
|
930
|
-
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
931
|
-
Co-Authored-By: Happy <yesreply@happy.engineering>
|
|
932
|
-
`))();
|
|
933
|
-
const systemPrompt = (() => {
|
|
934
|
-
const includeCoAuthored = shouldIncludeCoAuthoredBy();
|
|
935
|
-
if (includeCoAuthored) {
|
|
936
|
-
return BASE_SYSTEM_PROMPT + "\n\n" + CO_AUTHORED_CREDITS;
|
|
937
|
-
} else {
|
|
938
|
-
return BASE_SYSTEM_PROMPT;
|
|
939
|
-
}
|
|
940
|
-
})();
|
|
941
|
-
|
|
942
|
-
function getToolDescriptor(toolName) {
|
|
943
|
-
if (toolName === "exit_plan_mode" || toolName === "ExitPlanMode") {
|
|
944
|
-
return { edit: false, exitPlan: true };
|
|
945
|
-
}
|
|
946
|
-
if (toolName === "Edit" || toolName === "MultiEdit" || toolName === "Write" || toolName === "NotebookEdit") {
|
|
947
|
-
return { edit: true, exitPlan: false };
|
|
948
|
-
}
|
|
949
|
-
return { edit: false, exitPlan: false };
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
class ClaudeAcpPermissionHandler extends registerKillSessionHandler.BasePermissionHandler {
|
|
953
|
-
currentPermissionMode = "default";
|
|
954
|
-
constructor(session) {
|
|
955
|
-
super(session);
|
|
956
|
-
}
|
|
957
|
-
getLogPrefix() {
|
|
958
|
-
return "[ClaudeACP]";
|
|
959
|
-
}
|
|
960
|
-
setPermissionMode(mode) {
|
|
961
|
-
this.currentPermissionMode = mode;
|
|
962
|
-
api.logger.debug(`${this.getLogPrefix()} Permission mode set to: ${mode}`);
|
|
963
|
-
}
|
|
964
|
-
shouldAutoApprove(toolName) {
|
|
965
|
-
const descriptor = getToolDescriptor(toolName);
|
|
966
|
-
switch (this.currentPermissionMode) {
|
|
967
|
-
case "bypassPermissions":
|
|
968
|
-
case "yolo":
|
|
969
|
-
return true;
|
|
970
|
-
case "acceptEdits":
|
|
971
|
-
return descriptor.edit;
|
|
972
|
-
default:
|
|
973
|
-
return false;
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
async handleToolCall(toolCallId, toolName, input) {
|
|
977
|
-
if (this.shouldAutoApprove(toolName)) {
|
|
978
|
-
const decision = this.currentPermissionMode === "bypassPermissions" || this.currentPermissionMode === "yolo" ? "approved_for_session" : "approved";
|
|
979
|
-
this.session.updateAgentState((currentState) => ({
|
|
980
|
-
...currentState,
|
|
981
|
-
completedRequests: {
|
|
982
|
-
...currentState.completedRequests,
|
|
983
|
-
[toolCallId]: {
|
|
984
|
-
tool: toolName,
|
|
985
|
-
arguments: input,
|
|
986
|
-
createdAt: Date.now(),
|
|
987
|
-
completedAt: Date.now(),
|
|
988
|
-
status: "approved",
|
|
989
|
-
decision
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
}));
|
|
993
|
-
api.logger.debug(`${this.getLogPrefix()} Auto-approved tool ${toolName} (${toolCallId}) in ${this.currentPermissionMode} mode`);
|
|
994
|
-
return { decision };
|
|
995
|
-
}
|
|
996
|
-
return this.registerPendingRequest(
|
|
997
|
-
toolCallId,
|
|
998
|
-
toolName,
|
|
999
|
-
input,
|
|
1000
|
-
` in ${this.currentPermissionMode} mode`
|
|
1001
|
-
);
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
function normalizeClaudeBackendError(error) {
|
|
1006
|
-
const record = typeof error === "object" && error !== null ? error : null;
|
|
1007
|
-
const text = index.formatDisplayMessage(error).trim();
|
|
1008
|
-
const stderrText = record ? index.formatDisplayMessage(record.stderr).trim() : "";
|
|
1009
|
-
const detailText = record ? index.formatDisplayMessage(record.detail).trim() : "";
|
|
1010
|
-
const searchable = [text, stderrText, detailText].filter(Boolean).join("\n").trim();
|
|
1011
|
-
return searchable || "Claude ACP backend exited unexpectedly";
|
|
1012
|
-
}
|
|
1013
|
-
async function claudeAcpRemoteLauncher(session) {
|
|
1014
|
-
const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
1015
|
-
const messageBuffer = new registerKillSessionHandler.MessageBuffer({ enabled: hasTTY });
|
|
1016
|
-
let inkInstance = null;
|
|
1017
|
-
let shouldExit = false;
|
|
1018
|
-
let abortController = new AbortController();
|
|
1019
|
-
let runtimeHandle = null;
|
|
1020
|
-
let unsubscribeRuntimeMessages = null;
|
|
1021
|
-
let currentModeHash = null;
|
|
1022
|
-
let pending = null;
|
|
1023
|
-
let accumulatedResponse = "";
|
|
1024
|
-
let isResponseInProgress = false;
|
|
1025
|
-
let taskStartedSent = false;
|
|
1026
|
-
let currentAssistantMessageId = null;
|
|
1027
|
-
let currentThinkingMessageId = null;
|
|
1028
|
-
let shouldInjectHistoryOnNextSession = false;
|
|
1029
|
-
let readyAlreadySent = false;
|
|
1030
|
-
const permissionHandler = new ClaudeAcpPermissionHandler(session.client);
|
|
1031
|
-
const selectionHandler = new ProviderSelectionHandler.ProviderSelectionHandler(session.client, "Claude");
|
|
1032
|
-
const conversationHistory = new registerKillSessionHandler.ConversationHistory({
|
|
1033
|
-
maxMessages: 20,
|
|
1034
|
-
maxCharacters: 5e4
|
|
1035
|
-
});
|
|
1036
|
-
const rotateAbortController = () => {
|
|
1037
|
-
const activeController = abortController;
|
|
1038
|
-
abortController = new AbortController();
|
|
1039
|
-
return activeController;
|
|
1040
|
-
};
|
|
1041
|
-
const sendReady = () => {
|
|
1042
|
-
session.client.sendSessionEvent({ type: "ready" });
|
|
1043
|
-
try {
|
|
1044
|
-
session.api.push().sendToAllDevices(
|
|
1045
|
-
"It's ready!",
|
|
1046
|
-
"Claude is waiting for your command",
|
|
1047
|
-
{ sessionId: session.client.sessionId }
|
|
1048
|
-
);
|
|
1049
|
-
} catch (pushError) {
|
|
1050
|
-
api.logger.debug("[ClaudeACP] Failed to send ready push", pushError);
|
|
1051
|
-
}
|
|
1052
|
-
};
|
|
1053
|
-
const emitStatusMessage = (message) => {
|
|
1054
|
-
messageBuffer.addMessage(message, "status");
|
|
1055
|
-
session.client.sendSessionEvent({ type: "message", message });
|
|
1056
|
-
};
|
|
1057
|
-
const emitUserVisibleErrorMessage = (message) => {
|
|
1058
|
-
emitStatusMessage(message);
|
|
1059
|
-
session.client.sendAgentMessage("claude", {
|
|
1060
|
-
type: "message",
|
|
1061
|
-
message
|
|
1062
|
-
});
|
|
1063
|
-
};
|
|
1064
|
-
const resetTurnState = () => {
|
|
1065
|
-
accumulatedResponse = "";
|
|
1066
|
-
isResponseInProgress = false;
|
|
1067
|
-
taskStartedSent = false;
|
|
1068
|
-
currentAssistantMessageId = null;
|
|
1069
|
-
currentThinkingMessageId = null;
|
|
1070
|
-
session.onThinkingChange(false);
|
|
1071
|
-
};
|
|
1072
|
-
const emitFinalAssistantMessage = () => {
|
|
1073
|
-
const finalMessage = accumulatedResponse.trim();
|
|
1074
|
-
if (!finalMessage) {
|
|
1075
|
-
accumulatedResponse = "";
|
|
1076
|
-
isResponseInProgress = false;
|
|
1077
|
-
return;
|
|
1078
|
-
}
|
|
1079
|
-
conversationHistory.addAssistantMessage(finalMessage);
|
|
1080
|
-
session.client.sendAgentMessage("claude", {
|
|
1081
|
-
type: "message",
|
|
1082
|
-
message: finalMessage
|
|
1083
|
-
});
|
|
1084
|
-
accumulatedResponse = "";
|
|
1085
|
-
isResponseInProgress = false;
|
|
1086
|
-
};
|
|
1087
|
-
const disposeRuntimeHandle = async () => {
|
|
1088
|
-
if (!runtimeHandle) {
|
|
1089
|
-
return;
|
|
1090
|
-
}
|
|
1091
|
-
const activeHandle = runtimeHandle;
|
|
1092
|
-
runtimeHandle = null;
|
|
1093
|
-
unsubscribeRuntimeMessages?.();
|
|
1094
|
-
unsubscribeRuntimeMessages = null;
|
|
1095
|
-
try {
|
|
1096
|
-
await activeHandle.dispose();
|
|
1097
|
-
} catch (error) {
|
|
1098
|
-
api.logger.debug("[ClaudeACP] Error disposing runtime handle:", error);
|
|
1099
|
-
}
|
|
1100
|
-
};
|
|
1101
|
-
const queueHistoryInjectionForRestart = (reason) => {
|
|
1102
|
-
messageBuffer.addMessage("\u2550".repeat(40), "status");
|
|
1103
|
-
if (conversationHistory.hasHistory()) {
|
|
1104
|
-
shouldInjectHistoryOnNextSession = true;
|
|
1105
|
-
const message = `${reason} Preserving ${conversationHistory.size()} earlier messages of context.`;
|
|
1106
|
-
emitStatusMessage(message);
|
|
1107
|
-
api.logger.debug(`[ClaudeACP] Will inject conversation history after restart: ${conversationHistory.getSummary()}`);
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1110
|
-
emitStatusMessage(reason);
|
|
1111
|
-
};
|
|
1112
|
-
const setupRuntimeMessageHandler = (activeRuntimeHandle) => {
|
|
1113
|
-
const forwardAgentMessage = (agentMessage) => {
|
|
1114
|
-
registerKillSessionHandler.forwardAgentMessageToProviderSession(agentMessage, {
|
|
1115
|
-
provider: "claude",
|
|
1116
|
-
send: (body) => session.client.sendAgentMessage("claude", body)
|
|
1117
|
-
});
|
|
1118
|
-
};
|
|
1119
|
-
unsubscribeRuntimeMessages?.();
|
|
1120
|
-
unsubscribeRuntimeMessages = activeRuntimeHandle.onMessage((msg) => {
|
|
1121
|
-
switch (msg.type) {
|
|
1122
|
-
case "model-output": {
|
|
1123
|
-
const text = msg.textDelta ?? msg.fullText ?? "";
|
|
1124
|
-
if (!text) {
|
|
1125
|
-
return;
|
|
1126
|
-
}
|
|
1127
|
-
if (!isResponseInProgress) {
|
|
1128
|
-
if (currentThinkingMessageId) {
|
|
1129
|
-
messageBuffer.removeMessage(currentThinkingMessageId);
|
|
1130
|
-
currentThinkingMessageId = null;
|
|
1131
|
-
}
|
|
1132
|
-
currentAssistantMessageId = messageBuffer.addMessage(text, "assistant");
|
|
1133
|
-
isResponseInProgress = true;
|
|
1134
|
-
} else if (currentAssistantMessageId) {
|
|
1135
|
-
const updated = messageBuffer.updateMessage(currentAssistantMessageId, text);
|
|
1136
|
-
if (!updated) {
|
|
1137
|
-
currentAssistantMessageId = messageBuffer.addMessage(text, "assistant");
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
accumulatedResponse += text;
|
|
1141
|
-
return;
|
|
1142
|
-
}
|
|
1143
|
-
case "status": {
|
|
1144
|
-
if (msg.status === "running") {
|
|
1145
|
-
session.onThinkingChange(true);
|
|
1146
|
-
if (!taskStartedSent) {
|
|
1147
|
-
session.client.sendAgentMessage("claude", {
|
|
1148
|
-
type: "task_started",
|
|
1149
|
-
id: node_crypto.randomUUID()
|
|
1150
|
-
});
|
|
1151
|
-
taskStartedSent = true;
|
|
1152
|
-
}
|
|
1153
|
-
if (!isResponseInProgress && !currentThinkingMessageId) {
|
|
1154
|
-
currentThinkingMessageId = messageBuffer.addMessage("Thinking...", "system");
|
|
1155
|
-
}
|
|
1156
|
-
return;
|
|
1157
|
-
}
|
|
1158
|
-
if (msg.status === "idle" || msg.status === "stopped") {
|
|
1159
|
-
session.onThinkingChange(false);
|
|
1160
|
-
return;
|
|
1161
|
-
}
|
|
1162
|
-
if (msg.status === "error") {
|
|
1163
|
-
messageBuffer.addMessage(`Error: ${normalizeClaudeBackendError(msg.detail)}`, "status");
|
|
1164
|
-
}
|
|
1165
|
-
return;
|
|
1166
|
-
}
|
|
1167
|
-
case "tool-call": {
|
|
1168
|
-
const toolArgs = index.truncateDisplayMessage(msg.args, 100);
|
|
1169
|
-
messageBuffer.addMessage(
|
|
1170
|
-
`Executing: ${msg.toolName}${toolArgs ? ` ${toolArgs}` : ""}`,
|
|
1171
|
-
"tool"
|
|
1172
|
-
);
|
|
1173
|
-
forwardAgentMessage(msg);
|
|
1174
|
-
return;
|
|
1175
|
-
}
|
|
1176
|
-
case "tool-result": {
|
|
1177
|
-
const resultText = index.truncateDisplayMessage(msg.result, 200);
|
|
1178
|
-
messageBuffer.addMessage(resultText ? `Result: ${resultText}` : "Tool completed", "result");
|
|
1179
|
-
forwardAgentMessage(msg);
|
|
1180
|
-
return;
|
|
1181
|
-
}
|
|
1182
|
-
case "fs-edit": {
|
|
1183
|
-
messageBuffer.addMessage(`File edit: ${msg.description}`, "tool");
|
|
1184
|
-
forwardAgentMessage(msg);
|
|
1185
|
-
return;
|
|
1186
|
-
}
|
|
1187
|
-
case "terminal-output": {
|
|
1188
|
-
const output = index.formatDisplayMessage(msg.data);
|
|
1189
|
-
messageBuffer.addMessage(output, "result");
|
|
1190
|
-
forwardAgentMessage({
|
|
1191
|
-
...msg,
|
|
1192
|
-
data: output
|
|
1193
|
-
});
|
|
1194
|
-
return;
|
|
1195
|
-
}
|
|
1196
|
-
case "permission-request": {
|
|
1197
|
-
forwardAgentMessage(msg);
|
|
1198
|
-
return;
|
|
1199
|
-
}
|
|
1200
|
-
case "token-count": {
|
|
1201
|
-
forwardAgentMessage(msg);
|
|
1202
|
-
return;
|
|
1203
|
-
}
|
|
1204
|
-
case "exec-approval-request":
|
|
1205
|
-
case "patch-apply-begin":
|
|
1206
|
-
case "patch-apply-end": {
|
|
1207
|
-
forwardAgentMessage(msg);
|
|
1208
|
-
return;
|
|
1209
|
-
}
|
|
1210
|
-
case "event": {
|
|
1211
|
-
if (msg.name === "thinking") {
|
|
1212
|
-
const payload = msg.payload;
|
|
1213
|
-
const thinkingText = typeof payload?.text === "string" ? payload.text : "";
|
|
1214
|
-
if (thinkingText) {
|
|
1215
|
-
session.client.sendAgentMessage("claude", {
|
|
1216
|
-
type: "thinking",
|
|
1217
|
-
text: thinkingText
|
|
1218
|
-
});
|
|
1219
|
-
if (!isResponseInProgress) {
|
|
1220
|
-
const preview = `[Thinking] ${thinkingText.substring(0, 100)}...`;
|
|
1221
|
-
if (currentThinkingMessageId) {
|
|
1222
|
-
messageBuffer.updateMessage(currentThinkingMessageId, preview, { mode: "replace" });
|
|
1223
|
-
} else {
|
|
1224
|
-
currentThinkingMessageId = messageBuffer.addMessage(preview, "system");
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
return;
|
|
1230
|
-
}
|
|
1231
|
-
default:
|
|
1232
|
-
return;
|
|
1233
|
-
}
|
|
1234
|
-
});
|
|
1235
|
-
};
|
|
1236
|
-
const createRuntimeHandle = async (mode) => {
|
|
1237
|
-
const { session: nextRuntimeHandle, factoryResult } = await registerKillSessionHandler.launchRuntimeHandleWithFactoryResult({
|
|
1238
|
-
provider: "claude",
|
|
1239
|
-
cwd: session.path,
|
|
1240
|
-
createBackendResult: (opts) => index.createClaudeBackend({
|
|
1241
|
-
...opts,
|
|
1242
|
-
baseArgs: session.claudeArgs,
|
|
1243
|
-
mcpServers: session.mcpServers,
|
|
1244
|
-
permissionHandler,
|
|
1245
|
-
selectionHandler: {
|
|
1246
|
-
handleSelection: (request) => selectionHandler.requestSelection(request)
|
|
1247
|
-
},
|
|
1248
|
-
permissionMode: mode.permissionMode,
|
|
1249
|
-
model: mode.model,
|
|
1250
|
-
fallbackModel: mode.fallbackModel,
|
|
1251
|
-
customSystemPrompt: mode.customSystemPrompt ? `${mode.customSystemPrompt}
|
|
1252
|
-
|
|
1253
|
-
${systemPrompt}` : void 0,
|
|
1254
|
-
appendSystemPrompt: mode.appendSystemPrompt ? `${mode.appendSystemPrompt}
|
|
1255
|
-
|
|
1256
|
-
${systemPrompt}` : systemPrompt,
|
|
1257
|
-
allowedTools: mode.allowedTools ? mode.allowedTools.concat(session.allowedTools ?? []) : session.allowedTools,
|
|
1258
|
-
disallowedTools: mode.disallowedTools,
|
|
1259
|
-
settingsPath: session.hookSettingsPath
|
|
1260
|
-
})
|
|
1261
|
-
});
|
|
1262
|
-
api.logger.debug("[ClaudeACP] Started Claude ACP backend", {
|
|
1263
|
-
command: factoryResult.command,
|
|
1264
|
-
args: factoryResult.args
|
|
1265
|
-
});
|
|
1266
|
-
runtimeHandle = nextRuntimeHandle;
|
|
1267
|
-
setupRuntimeMessageHandler(nextRuntimeHandle);
|
|
1268
|
-
session.consumeOneTimeFlags();
|
|
1269
|
-
return nextRuntimeHandle;
|
|
1270
|
-
};
|
|
1271
|
-
const abortActiveTurn = async () => {
|
|
1272
|
-
const activeController = rotateAbortController();
|
|
1273
|
-
activeController.abort();
|
|
1274
|
-
session.onThinkingChange(false);
|
|
1275
|
-
if (runtimeHandle) {
|
|
1276
|
-
await runtimeHandle.cancel().catch((error) => {
|
|
1277
|
-
api.logger.debug("[ClaudeACP] Error cancelling runtime handle:", error);
|
|
1278
|
-
});
|
|
1279
|
-
}
|
|
1280
|
-
};
|
|
1281
|
-
const completeSyntheticTurn = () => {
|
|
1282
|
-
session.client.sendAgentMessage("claude", {
|
|
1283
|
-
type: "task_complete",
|
|
1284
|
-
id: node_crypto.randomUUID()
|
|
1285
|
-
});
|
|
1286
|
-
};
|
|
1287
|
-
const handleClearCommand = async () => {
|
|
1288
|
-
api.logger.debug("[ClaudeACP] /clear command received - resetting runtime and local history");
|
|
1289
|
-
conversationHistory.clear();
|
|
1290
|
-
shouldInjectHistoryOnNextSession = false;
|
|
1291
|
-
await disposeRuntimeHandle();
|
|
1292
|
-
session.clearSessionId();
|
|
1293
|
-
currentModeHash = null;
|
|
1294
|
-
permissionHandler.reset();
|
|
1295
|
-
selectionHandler.reset();
|
|
1296
|
-
resetTurnState();
|
|
1297
|
-
emitStatusMessage("Context was reset");
|
|
1298
|
-
completeSyntheticTurn();
|
|
1299
|
-
};
|
|
1300
|
-
const handleSwitchToLocal = async () => {
|
|
1301
|
-
const message = "Daemon-spawned Claude ACP sessions stay in remote mode.";
|
|
1302
|
-
api.logger.debug("[ClaudeACP] Ignoring switch request because daemon sessions are remote-only");
|
|
1303
|
-
emitStatusMessage(message);
|
|
1304
|
-
};
|
|
1305
|
-
const handleAbort = async () => {
|
|
1306
|
-
api.logger.debug("[ClaudeACP] Abort requested - stopping current task");
|
|
1307
|
-
await abortActiveTurn();
|
|
1308
|
-
};
|
|
1309
|
-
if (hasTTY) {
|
|
1310
|
-
console.clear();
|
|
1311
|
-
inkInstance = ink.render(React.createElement(RemoteModeDisplay, {
|
|
1312
|
-
messageBuffer,
|
|
1313
|
-
logPath: process.env.DEBUG ? session.logPath : void 0,
|
|
1314
|
-
onExit: async () => {
|
|
1315
|
-
shouldExit = true;
|
|
1316
|
-
await handleAbort();
|
|
1317
|
-
},
|
|
1318
|
-
onSwitchToLocal: () => {
|
|
1319
|
-
void handleSwitchToLocal();
|
|
1320
|
-
}
|
|
1321
|
-
}), {
|
|
1322
|
-
exitOnCtrlC: false,
|
|
1323
|
-
patchConsole: false
|
|
1324
|
-
});
|
|
1325
|
-
}
|
|
1326
|
-
if (hasTTY) {
|
|
1327
|
-
process.stdin.resume();
|
|
1328
|
-
if (process.stdin.isTTY) {
|
|
1329
|
-
process.stdin.setRawMode(true);
|
|
1330
|
-
}
|
|
1331
|
-
process.stdin.setEncoding("utf8");
|
|
1332
|
-
}
|
|
1333
|
-
session.client.rpcHandlerManager.registerHandler("abort", handleAbort);
|
|
1334
|
-
session.client.rpcHandlerManager.registerHandler("switch", handleSwitchToLocal);
|
|
1335
|
-
try {
|
|
1336
|
-
while (!shouldExit) {
|
|
1337
|
-
let message = pending;
|
|
1338
|
-
pending = null;
|
|
1339
|
-
if (!message) {
|
|
1340
|
-
const waitSignal = abortController.signal;
|
|
1341
|
-
const batch = await session.queue.waitForMessagesAndGetAsString(waitSignal);
|
|
1342
|
-
if (!batch) {
|
|
1343
|
-
if (waitSignal.aborted && !shouldExit) {
|
|
1344
|
-
continue;
|
|
1345
|
-
}
|
|
1346
|
-
break;
|
|
1347
|
-
}
|
|
1348
|
-
message = batch;
|
|
1349
|
-
}
|
|
1350
|
-
if (!message) {
|
|
1351
|
-
break;
|
|
1352
|
-
}
|
|
1353
|
-
if (runtimeHandle && currentModeHash && message.hash !== currentModeHash) {
|
|
1354
|
-
queueHistoryInjectionForRestart("Starting new Claude ACP session (execution settings changed)...");
|
|
1355
|
-
await disposeRuntimeHandle();
|
|
1356
|
-
session.clearSessionId();
|
|
1357
|
-
currentModeHash = null;
|
|
1358
|
-
pending = message;
|
|
1359
|
-
permissionHandler.reset();
|
|
1360
|
-
selectionHandler.reset();
|
|
1361
|
-
resetTurnState();
|
|
1362
|
-
continue;
|
|
1363
|
-
}
|
|
1364
|
-
currentModeHash = message.hash;
|
|
1365
|
-
readyAlreadySent = false;
|
|
1366
|
-
const specialCommand = parseSpecialCommand(message.message);
|
|
1367
|
-
if (specialCommand.type === "clear") {
|
|
1368
|
-
await handleClearCommand();
|
|
1369
|
-
if (!shouldExit && !pending && session.queue.size() === 0 && !readyAlreadySent) {
|
|
1370
|
-
sendReady();
|
|
1371
|
-
readyAlreadySent = true;
|
|
1372
|
-
}
|
|
1373
|
-
continue;
|
|
1374
|
-
}
|
|
1375
|
-
permissionHandler.setPermissionMode(message.mode.permissionMode);
|
|
1376
|
-
messageBuffer.addMessage(message.message, "user");
|
|
1377
|
-
let shouldClearHistoryAfterTurn = false;
|
|
1378
|
-
try {
|
|
1379
|
-
const activeRuntimeHandle = runtimeHandle ?? await createRuntimeHandle(message.mode);
|
|
1380
|
-
let promptToSend = message.message;
|
|
1381
|
-
if (shouldInjectHistoryOnNextSession && conversationHistory.hasHistory()) {
|
|
1382
|
-
const historyContext = conversationHistory.getContextForNewSession(
|
|
1383
|
-
"Continue from the prior Claude ACP session using the conversation below as context."
|
|
1384
|
-
);
|
|
1385
|
-
promptToSend = historyContext + promptToSend;
|
|
1386
|
-
api.logger.debug(`[ClaudeACP] Injected conversation history context (${historyContext.length} chars)`);
|
|
1387
|
-
}
|
|
1388
|
-
if (specialCommand.type === "compact") {
|
|
1389
|
-
emitStatusMessage("Compaction started");
|
|
1390
|
-
}
|
|
1391
|
-
conversationHistory.addUserMessage(message.message);
|
|
1392
|
-
await activeRuntimeHandle.sendPrompt(promptToSend);
|
|
1393
|
-
await registerKillSessionHandler.waitForResponseCompleteWithAbort(activeRuntimeHandle.backend, abortController.signal);
|
|
1394
|
-
shouldInjectHistoryOnNextSession = false;
|
|
1395
|
-
shouldClearHistoryAfterTurn = specialCommand.type === "compact";
|
|
1396
|
-
} catch (error) {
|
|
1397
|
-
api.logger.warn("[ClaudeACP] Error in Claude ACP session:", error);
|
|
1398
|
-
const isAbortError = error instanceof Error && error.name === "AbortError";
|
|
1399
|
-
const isExpectedInterruption = isAbortError || abortController.signal.aborted || shouldExit;
|
|
1400
|
-
if (isExpectedInterruption) {
|
|
1401
|
-
session.client.sendAgentMessage("claude", {
|
|
1402
|
-
type: "turn_aborted",
|
|
1403
|
-
id: node_crypto.randomUUID()
|
|
1404
|
-
});
|
|
1405
|
-
emitStatusMessage("Aborted by user");
|
|
1406
|
-
} else {
|
|
1407
|
-
const errorMessage = normalizeClaudeBackendError(error);
|
|
1408
|
-
emitUserVisibleErrorMessage(errorMessage);
|
|
1409
|
-
if (conversationHistory.hasHistory()) {
|
|
1410
|
-
shouldInjectHistoryOnNextSession = true;
|
|
1411
|
-
}
|
|
1412
|
-
await disposeRuntimeHandle();
|
|
1413
|
-
session.clearSessionId();
|
|
1414
|
-
currentModeHash = null;
|
|
1415
|
-
}
|
|
1416
|
-
} finally {
|
|
1417
|
-
emitFinalAssistantMessage();
|
|
1418
|
-
if (shouldClearHistoryAfterTurn) {
|
|
1419
|
-
conversationHistory.clear();
|
|
1420
|
-
emitStatusMessage("Compaction completed");
|
|
1421
|
-
}
|
|
1422
|
-
if (!shouldExit) {
|
|
1423
|
-
session.client.sendAgentMessage("claude", {
|
|
1424
|
-
type: "task_complete",
|
|
1425
|
-
id: node_crypto.randomUUID()
|
|
1426
|
-
});
|
|
1427
|
-
}
|
|
1428
|
-
permissionHandler.reset();
|
|
1429
|
-
selectionHandler.reset();
|
|
1430
|
-
resetTurnState();
|
|
1431
|
-
if (!shouldExit && !pending && session.queue.size() === 0 && !readyAlreadySent) {
|
|
1432
|
-
sendReady();
|
|
1433
|
-
readyAlreadySent = true;
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
} finally {
|
|
1438
|
-
await disposeRuntimeHandle();
|
|
1439
|
-
permissionHandler.reset();
|
|
1440
|
-
selectionHandler.reset();
|
|
1441
|
-
if (process.stdin.isTTY) {
|
|
1442
|
-
try {
|
|
1443
|
-
process.stdin.setRawMode(false);
|
|
1444
|
-
} catch {
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
if (hasTTY) {
|
|
1448
|
-
try {
|
|
1449
|
-
process.stdin.pause();
|
|
1450
|
-
} catch {
|
|
1451
|
-
}
|
|
1452
|
-
}
|
|
1453
|
-
session.client.rpcHandlerManager.registerHandler("abort", async () => {
|
|
1454
|
-
});
|
|
1455
|
-
session.client.rpcHandlerManager.registerHandler("switch", async () => {
|
|
1456
|
-
});
|
|
1457
|
-
if (inkInstance) {
|
|
1458
|
-
inkInstance.unmount();
|
|
1459
|
-
}
|
|
1460
|
-
messageBuffer.clear();
|
|
1461
|
-
}
|
|
1462
|
-
api.logger.debug("[ClaudeACP] Remote launcher returning: exit");
|
|
1463
|
-
return "exit";
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1466
846
|
class Stream {
|
|
1467
847
|
constructor(returned) {
|
|
1468
848
|
this.returned = returned;
|
|
@@ -1560,7 +940,7 @@ class AbortError extends Error {
|
|
|
1560
940
|
}
|
|
1561
941
|
}
|
|
1562
942
|
|
|
1563
|
-
const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('runClaude-
|
|
943
|
+
const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('runClaude-CDZxAF3l.cjs', document.baseURI).href)));
|
|
1564
944
|
const __dirname$1 = path.join(__filename$1, "..");
|
|
1565
945
|
function getGlobalClaudeVersion() {
|
|
1566
946
|
try {
|
|
@@ -1988,6 +1368,50 @@ function query(config) {
|
|
|
1988
1368
|
return query2;
|
|
1989
1369
|
}
|
|
1990
1370
|
|
|
1371
|
+
function parseCompact(message) {
|
|
1372
|
+
const trimmed = message.trim();
|
|
1373
|
+
if (trimmed === "/compact") {
|
|
1374
|
+
return {
|
|
1375
|
+
isCompact: true,
|
|
1376
|
+
originalMessage: trimmed
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
if (trimmed.startsWith("/compact ")) {
|
|
1380
|
+
return {
|
|
1381
|
+
isCompact: true,
|
|
1382
|
+
originalMessage: trimmed
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
return {
|
|
1386
|
+
isCompact: false,
|
|
1387
|
+
originalMessage: message
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
function parseClear(message) {
|
|
1391
|
+
const trimmed = message.trim();
|
|
1392
|
+
return {
|
|
1393
|
+
isClear: trimmed === "/clear"
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
function parseSpecialCommand(message) {
|
|
1397
|
+
const compactResult = parseCompact(message);
|
|
1398
|
+
if (compactResult.isCompact) {
|
|
1399
|
+
return {
|
|
1400
|
+
type: "compact",
|
|
1401
|
+
originalMessage: compactResult.originalMessage
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
const clearResult = parseClear(message);
|
|
1405
|
+
if (clearResult.isClear) {
|
|
1406
|
+
return {
|
|
1407
|
+
type: "clear"
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
return {
|
|
1411
|
+
type: null
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1991
1415
|
class PushableAsyncIterable {
|
|
1992
1416
|
queue = [];
|
|
1993
1417
|
waiters = [];
|
|
@@ -2127,6 +1551,58 @@ async function awaitFileExist(file, timeout = 1e4) {
|
|
|
2127
1551
|
return false;
|
|
2128
1552
|
}
|
|
2129
1553
|
|
|
1554
|
+
function getClaudeSettingsPath() {
|
|
1555
|
+
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), ".claude");
|
|
1556
|
+
return path.join(claudeConfigDir, "settings.json");
|
|
1557
|
+
}
|
|
1558
|
+
function readClaudeSettings() {
|
|
1559
|
+
try {
|
|
1560
|
+
const settingsPath = getClaudeSettingsPath();
|
|
1561
|
+
if (!fs$1.existsSync(settingsPath)) {
|
|
1562
|
+
api.logger.debug(`[ClaudeSettings] No Claude settings file found at ${settingsPath}`);
|
|
1563
|
+
return null;
|
|
1564
|
+
}
|
|
1565
|
+
const settingsContent = fs$1.readFileSync(settingsPath, "utf-8");
|
|
1566
|
+
const settings = JSON.parse(settingsContent);
|
|
1567
|
+
api.logger.debug(`[ClaudeSettings] Successfully read Claude settings from ${settingsPath}`);
|
|
1568
|
+
api.logger.debug(`[ClaudeSettings] includeCoAuthoredBy: ${settings.includeCoAuthoredBy}`);
|
|
1569
|
+
return settings;
|
|
1570
|
+
} catch (error) {
|
|
1571
|
+
api.logger.debug(`[ClaudeSettings] Error reading Claude settings: ${error}`);
|
|
1572
|
+
return null;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
function shouldIncludeCoAuthoredBy() {
|
|
1576
|
+
const settings = readClaudeSettings();
|
|
1577
|
+
if (!settings || settings.includeCoAuthoredBy === void 0) {
|
|
1578
|
+
return true;
|
|
1579
|
+
}
|
|
1580
|
+
return settings.includeCoAuthoredBy;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
const BASE_SYSTEM_PROMPT = (() => index.trimIdent(`
|
|
1584
|
+
Help human.
|
|
1585
|
+
`))();
|
|
1586
|
+
const CO_AUTHORED_CREDITS = (() => index.trimIdent(`
|
|
1587
|
+
When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
|
|
1588
|
+
|
|
1589
|
+
<main commit message>
|
|
1590
|
+
|
|
1591
|
+
Generated with [Claude Code](https://claude.ai/code)
|
|
1592
|
+
via [Happy](https://happy.engineering)
|
|
1593
|
+
|
|
1594
|
+
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
1595
|
+
Co-Authored-By: Happy <yesreply@happy.engineering>
|
|
1596
|
+
`))();
|
|
1597
|
+
const systemPrompt = (() => {
|
|
1598
|
+
const includeCoAuthored = shouldIncludeCoAuthoredBy();
|
|
1599
|
+
if (includeCoAuthored) {
|
|
1600
|
+
return BASE_SYSTEM_PROMPT + "\n\n" + CO_AUTHORED_CREDITS;
|
|
1601
|
+
} else {
|
|
1602
|
+
return BASE_SYSTEM_PROMPT;
|
|
1603
|
+
}
|
|
1604
|
+
})();
|
|
1605
|
+
|
|
2130
1606
|
async function claudeRemote(opts) {
|
|
2131
1607
|
let startFrom = opts.sessionId;
|
|
2132
1608
|
if (opts.sessionId && !index.claudeCheckSession(opts.sessionId, opts.path, opts.transcriptPath)) {
|
|
@@ -2330,6 +1806,16 @@ function getToolName(toolName) {
|
|
|
2330
1806
|
return toTitleCase(toolName);
|
|
2331
1807
|
}
|
|
2332
1808
|
|
|
1809
|
+
function getToolDescriptor(toolName) {
|
|
1810
|
+
if (toolName === "exit_plan_mode" || toolName === "ExitPlanMode") {
|
|
1811
|
+
return { edit: false, exitPlan: true };
|
|
1812
|
+
}
|
|
1813
|
+
if (toolName === "Edit" || toolName === "MultiEdit" || toolName === "Write" || toolName === "NotebookEdit") {
|
|
1814
|
+
return { edit: true, exitPlan: false };
|
|
1815
|
+
}
|
|
1816
|
+
return { edit: false, exitPlan: false };
|
|
1817
|
+
}
|
|
1818
|
+
|
|
2333
1819
|
class PermissionHandler {
|
|
2334
1820
|
toolCalls = [];
|
|
2335
1821
|
responses = /* @__PURE__ */ new Map();
|
|
@@ -3116,8 +2602,9 @@ class OutgoingMessageQueue {
|
|
|
3116
2602
|
}
|
|
3117
2603
|
}
|
|
3118
2604
|
|
|
3119
|
-
async function claudeRemoteLauncher(session) {
|
|
2605
|
+
async function claudeRemoteLauncher(session, options = {}) {
|
|
3120
2606
|
api.logger.debug("[claudeRemoteLauncher] Starting remote launcher");
|
|
2607
|
+
const allowSwitchToLocal = options.allowSwitchToLocal ?? true;
|
|
3121
2608
|
const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
3122
2609
|
api.logger.debug(`[claudeRemoteLauncher] TTY available: ${hasTTY}`);
|
|
3123
2610
|
let messageBuffer = new registerKillSessionHandler.MessageBuffer();
|
|
@@ -3135,6 +2622,11 @@ async function claudeRemoteLauncher(session) {
|
|
|
3135
2622
|
await abort();
|
|
3136
2623
|
},
|
|
3137
2624
|
onSwitchToLocal: () => {
|
|
2625
|
+
if (!allowSwitchToLocal) {
|
|
2626
|
+
api.logger.debug("[remote]: Ignoring switch-to-local request for remote-only session");
|
|
2627
|
+
session.client.sendSessionEvent({ type: "message", message: "Daemon-spawned Claude sessions stay in remote mode." });
|
|
2628
|
+
return;
|
|
2629
|
+
}
|
|
3138
2630
|
api.logger.debug("[remote]: Switching to local mode via double space");
|
|
3139
2631
|
doSwitch();
|
|
3140
2632
|
}
|
|
@@ -3164,6 +2656,11 @@ async function claudeRemoteLauncher(session) {
|
|
|
3164
2656
|
await abort();
|
|
3165
2657
|
}
|
|
3166
2658
|
async function doSwitch() {
|
|
2659
|
+
if (!allowSwitchToLocal) {
|
|
2660
|
+
api.logger.debug("[remote]: Ignoring RPC switch-to-local request for remote-only session");
|
|
2661
|
+
session.client.sendSessionEvent({ type: "message", message: "Daemon-spawned Claude sessions stay in remote mode." });
|
|
2662
|
+
return;
|
|
2663
|
+
}
|
|
3167
2664
|
api.logger.debug("[remote]: doSwitch");
|
|
3168
2665
|
if (!exitReason) {
|
|
3169
2666
|
exitReason = "switch";
|
|
@@ -3471,7 +2968,7 @@ async function loop(opts) {
|
|
|
3471
2968
|
jsRuntime: opts.jsRuntime
|
|
3472
2969
|
});
|
|
3473
2970
|
opts.onSessionReady?.(session);
|
|
3474
|
-
return await
|
|
2971
|
+
return await createKeepAliveController.runModeLoop({
|
|
3475
2972
|
startingMode: opts.startingMode ?? "local",
|
|
3476
2973
|
onIteration: (mode) => {
|
|
3477
2974
|
api.logger.debug(`[loop] Iteration with mode: ${mode}`);
|
|
@@ -3488,7 +2985,9 @@ async function loop(opts) {
|
|
|
3488
2985
|
return { type: "exit", value: result.code };
|
|
3489
2986
|
},
|
|
3490
2987
|
remote: async () => {
|
|
3491
|
-
const reason =
|
|
2988
|
+
const reason = await claudeRemoteLauncher(session, {
|
|
2989
|
+
allowSwitchToLocal: opts.startedBy !== "daemon"
|
|
2990
|
+
});
|
|
3492
2991
|
if (reason === "switch") {
|
|
3493
2992
|
return { type: "switch", mode: "local" };
|
|
3494
2993
|
}
|