happy-imou-cloud 2.0.11 → 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.
Files changed (35) hide show
  1. package/bin/happy-cloud.mjs +1 -1
  2. package/dist/ConversationHistory-V3VLmjJf.cjs +868 -0
  3. package/dist/ConversationHistory-_ciJNIgH.mjs +856 -0
  4. package/dist/{api-BcWf5v4i.mjs → api-D1meoL-9.mjs} +2 -2
  5. package/dist/{api-Ye-rPX6s.cjs → api-DH5-IqeM.cjs} +2 -2
  6. package/dist/{command-nOI80Mnm.mjs → command-CMvWClny.mjs} +3 -3
  7. package/dist/{command-BK93nizl.cjs → command-Ch8Dgidj.cjs} +3 -3
  8. package/dist/createKeepAliveController-C5cQlDRr.mjs +51 -0
  9. package/dist/createKeepAliveController-DO8H6d5E.cjs +54 -0
  10. package/dist/{index-J7QKJ8lc.cjs → index-CryJfCh5.cjs} +54 -23
  11. package/dist/{index-DnsqY6I_.mjs → index-Cxrx9m5D.mjs} +53 -21
  12. package/dist/index.cjs +3 -3
  13. package/dist/index.mjs +3 -3
  14. package/dist/lib.cjs +1 -1
  15. package/dist/lib.mjs +1 -1
  16. package/dist/{persistence-BMa6cyw9.mjs → persistence-9Iu0wGNM.mjs} +1 -1
  17. package/dist/{persistence-xK5CKhbn.cjs → persistence-Bl3FYvwd.cjs} +1 -1
  18. package/dist/{registerKillSessionHandler-Dxwg4L4J.mjs → registerKillSessionHandler-BElGmD1E.mjs} +5 -541
  19. package/dist/{registerKillSessionHandler-CH6yN0eG.cjs → registerKillSessionHandler-BjkY-oUn.cjs} +4 -549
  20. package/dist/{runClaude-BMHlBny_.cjs → runClaude-CDZxAF3l.cjs} +129 -630
  21. package/dist/{runClaude-cQ-UT0Ke.mjs → runClaude-D7dF4RDM.mjs} +126 -627
  22. package/dist/{runCodex-roOSOWWL.cjs → runCodex-Cik8VzFs.cjs} +224 -17
  23. package/dist/{runCodex-AnJUPIhX.mjs → runCodex-DnGz1XES.mjs} +213 -6
  24. package/dist/{runGemini-BGo_0mzA.mjs → runGemini-B8tXMHeL.mjs} +5 -5
  25. package/dist/{runGemini-Cg6Zbqlz.cjs → runGemini-BM2BQ4I7.cjs} +13 -13
  26. package/package.json +9 -9
  27. package/scripts/build.mjs +66 -66
  28. package/scripts/devtools/README.md +9 -9
  29. package/scripts/e2e/fake-codex-acp-agent.mjs +139 -139
  30. package/scripts/e2e/local-server-session-roundtrip.mjs +1063 -1063
  31. package/scripts/release-smoke.mjs +202 -202
  32. package/dist/BaseReasoningProcessor-5ACv9gKu.mjs +0 -320
  33. package/dist/BaseReasoningProcessor-COrRWyn0.cjs +0 -323
  34. package/dist/ProviderSelectionHandler-BljOLMQn.mjs +0 -261
  35. package/dist/ProviderSelectionHandler-DBGobhGZ.cjs +0 -265
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  var node_crypto = require('node:crypto');
4
- var api = require('./api-Ye-rPX6s.cjs');
4
+ var api = require('./api-DH5-IqeM.cjs');
5
5
  require('cross-spawn');
6
6
  require('@agentclientprotocol/sdk');
7
- var index = require('./index-J7QKJ8lc.cjs');
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-xK5CKhbn.cjs');
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 ProviderSelectionHandler = require('./ProviderSelectionHandler-DBGobhGZ.cjs');
28
+ var createKeepAliveController = require('./createKeepAliveController-DO8H6d5E.cjs');
29
29
  var types = require('./types-DVk3crez.cjs');
30
- var registerKillSessionHandler = require('./registerKillSessionHandler-CH6yN0eG.cjs');
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 = ProviderSelectionHandler.createKeepAliveController({
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-BMHlBny_.cjs', document.baseURI).href)));
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 ProviderSelectionHandler.runModeLoop({
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 = opts.startedBy === "daemon" ? await claudeAcpRemoteLauncher(session) : await claudeRemoteLauncher(session);
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
  }