happy-imou-cloud 2.0.13 → 2.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BaseReasoningProcessor-BqMAZlIw.cjs +323 -0
- package/dist/BaseReasoningProcessor-C04_LHjN.mjs +320 -0
- package/dist/ProviderSelectionHandler-CO9NkAt6.cjs +265 -0
- package/dist/ProviderSelectionHandler-DDWyn9Lo.mjs +261 -0
- package/dist/{api-DH5-IqeM.cjs → api-Db1SQcP_.cjs} +2 -2
- package/dist/{api-D1meoL-9.mjs → api-sRF6xXi-.mjs} +2 -2
- package/dist/{command-CMvWClny.mjs → command-WcgGTRnG.mjs} +4 -3
- package/dist/{command-Ch8Dgidj.cjs → command-eRjSBm2C.cjs} +4 -3
- package/dist/{index-CryJfCh5.cjs → index-B6ID1zDR.cjs} +1106 -50
- package/dist/{index-Cxrx9m5D.mjs → index-DpWeKfvS.mjs} +1102 -50
- package/dist/index.cjs +5 -4
- package/dist/index.mjs +5 -4
- package/dist/lib.cjs +1 -1
- package/dist/lib.mjs +1 -1
- package/dist/{persistence-9Iu0wGNM.mjs → persistence-B89V4xY5.mjs} +1 -1
- package/dist/{persistence-Bl3FYvwd.cjs → persistence-Btu2VPXI.cjs} +1 -1
- package/dist/{registerKillSessionHandler-BElGmD1E.mjs → registerKillSessionHandler-CwmYlUfS.mjs} +541 -5
- package/dist/{registerKillSessionHandler-BjkY-oUn.cjs → registerKillSessionHandler-eZ2TsHqx.cjs} +549 -4
- package/dist/{runClaude-CDZxAF3l.cjs → runClaude-C9-ylbQh.cjs} +599 -747
- package/dist/{runClaude-D7dF4RDM.mjs → runClaude-kRPXCaBj.mjs} +591 -738
- package/dist/{runCodex-DnGz1XES.mjs → runCodex-B1xN0wAU.mjs} +9 -215
- package/dist/{runCodex-Cik8VzFs.cjs → runCodex-CRNBxY5f.cjs} +20 -226
- package/dist/{runGemini-BM2BQ4I7.cjs → runGemini-BZ5hqJyl.cjs} +16 -15
- package/dist/{runGemini-B8tXMHeL.mjs → runGemini-Xn2VwS88.mjs} +8 -7
- package/package.json +1 -1
- package/scripts/release-smoke.mjs +6 -5
- package/dist/ConversationHistory-V3VLmjJf.cjs +0 -868
- package/dist/ConversationHistory-_ciJNIgH.mjs +0 -856
- package/dist/createKeepAliveController-C5cQlDRr.mjs +0 -51
- package/dist/createKeepAliveController-DO8H6d5E.cjs +0 -54
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { l as logger, d as backoff, f as delay, g as AsyncLock, c as configuration, s as startOfflineReconnection, b as connectionState, A as ApiClient, i as isAuthenticationRequiredError } from './api-
|
|
2
|
+
import { l as logger, d as backoff, f as delay, g as AsyncLock, c as configuration, s as startOfflineReconnection, b as connectionState, A as ApiClient, i as isAuthenticationRequiredError } from './api-sRF6xXi-.mjs';
|
|
3
3
|
import 'cross-spawn';
|
|
4
4
|
import '@agentclientprotocol/sdk';
|
|
5
|
-
import { j as getProjectPath, k as claudeLocal, E as ExitCodeError, l as
|
|
5
|
+
import { j as getProjectPath, F as Future, k as claudeLocal, E as ExitCodeError, l as trimIdent, m as createClaudeBackend, f as formatDisplayMessage, t as truncateDisplayMessage, n as claudeCheckSession, e as projectPath, o as mapToClaudeMode, P as PushableAsyncIterable, q as query, A as AbortError, b as stopCaffeinate, p as publishSessionRegistration, u as getEnvironmentInfo, w as startCaffeinate } from './index-DpWeKfvS.mjs';
|
|
6
6
|
import 'ps-list';
|
|
7
7
|
import 'fs';
|
|
8
8
|
import 'path';
|
|
@@ -11,11 +11,12 @@ import 'child_process';
|
|
|
11
11
|
import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
12
12
|
import { dirname, basename, join, resolve } from 'node:path';
|
|
13
13
|
import { homedir } from 'node:os';
|
|
14
|
-
import '
|
|
14
|
+
import { execSync } from 'node:child_process';
|
|
15
|
+
import 'node:readline';
|
|
16
|
+
import './persistence-B89V4xY5.mjs';
|
|
15
17
|
import { readFile } from 'node:fs/promises';
|
|
16
18
|
import { stat, watch, access } from 'fs/promises';
|
|
17
19
|
import 'crypto';
|
|
18
|
-
import { execSync, spawn } from 'node:child_process';
|
|
19
20
|
import 'chalk';
|
|
20
21
|
import 'node:events';
|
|
21
22
|
import 'axios';
|
|
@@ -23,11 +24,9 @@ import 'tweetnacl';
|
|
|
23
24
|
import 'open';
|
|
24
25
|
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
|
25
26
|
import { useStdout, useInput, Box, Text, render } from 'ink';
|
|
26
|
-
import { c as createKeepAliveController, r as runModeLoop } from './
|
|
27
|
+
import { c as createKeepAliveController, P as ProviderSelectionHandler, r as runModeLoop } from './ProviderSelectionHandler-DDWyn9Lo.mjs';
|
|
27
28
|
import { R as RawJSONLinesSchema } from './types-CiliQpqS.mjs';
|
|
28
|
-
import {
|
|
29
|
-
import { createInterface } from 'node:readline';
|
|
30
|
-
import { fileURLToPath } from 'node:url';
|
|
29
|
+
import { B as BasePermissionHandler, d as MessageBuffer, C as ConversationHistory$1, w as waitForResponseCompleteWithAbort, l as launchRuntimeHandleWithFactoryResult, j as forwardAgentMessageToProviderSession, s as syncControlledByUserState, e as ensureManagedProviderMachine, M as MissingMachineIdError, c as createSessionMetadata, b as MessageQueue2, h as hashObject, r as registerKillSessionHandler, f as closeProviderSession } from './registerKillSessionHandler-CwmYlUfS.mjs';
|
|
31
30
|
import 'socket.io-client';
|
|
32
31
|
import 'expo-server-sdk';
|
|
33
32
|
import { isDeepStrictEqual } from 'node:util';
|
|
@@ -41,6 +40,7 @@ import 'fastify';
|
|
|
41
40
|
import 'fastify-type-provider-zod';
|
|
42
41
|
import 'http';
|
|
43
42
|
import 'util';
|
|
43
|
+
import 'node:url';
|
|
44
44
|
|
|
45
45
|
class Session {
|
|
46
46
|
path;
|
|
@@ -201,27 +201,6 @@ class Session {
|
|
|
201
201
|
};
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
class Future {
|
|
205
|
-
_resolve;
|
|
206
|
-
_reject;
|
|
207
|
-
_promise;
|
|
208
|
-
constructor() {
|
|
209
|
-
this._promise = new Promise((resolve, reject) => {
|
|
210
|
-
this._resolve = resolve;
|
|
211
|
-
this._reject = reject;
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
resolve(value) {
|
|
215
|
-
this._resolve(value);
|
|
216
|
-
}
|
|
217
|
-
reject(reason) {
|
|
218
|
-
this._reject(reason);
|
|
219
|
-
}
|
|
220
|
-
get promise() {
|
|
221
|
-
return this._promise;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
204
|
class InvalidateSync {
|
|
226
205
|
_invalidated = false;
|
|
227
206
|
_invalidatedDouble = false;
|
|
@@ -715,6 +694,50 @@ async function claudeLocalLauncher(session) {
|
|
|
715
694
|
return exitReason || { type: "exit", code: 0 };
|
|
716
695
|
}
|
|
717
696
|
|
|
697
|
+
function parseCompact(message) {
|
|
698
|
+
const trimmed = message.trim();
|
|
699
|
+
if (trimmed === "/compact") {
|
|
700
|
+
return {
|
|
701
|
+
isCompact: true,
|
|
702
|
+
originalMessage: trimmed
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
if (trimmed.startsWith("/compact ")) {
|
|
706
|
+
return {
|
|
707
|
+
isCompact: true,
|
|
708
|
+
originalMessage: trimmed
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
return {
|
|
712
|
+
isCompact: false,
|
|
713
|
+
originalMessage: message
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
function parseClear(message) {
|
|
717
|
+
const trimmed = message.trim();
|
|
718
|
+
return {
|
|
719
|
+
isClear: trimmed === "/clear"
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function parseSpecialCommand(message) {
|
|
723
|
+
const compactResult = parseCompact(message);
|
|
724
|
+
if (compactResult.isCompact) {
|
|
725
|
+
return {
|
|
726
|
+
type: "compact",
|
|
727
|
+
originalMessage: compactResult.originalMessage
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
const clearResult = parseClear(message);
|
|
731
|
+
if (clearResult.isClear) {
|
|
732
|
+
return {
|
|
733
|
+
type: "clear"
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
return {
|
|
737
|
+
type: null
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
|
|
718
741
|
const RemoteModeDisplay = ({ messageBuffer, logPath, onExit, onSwitchToLocal }) => {
|
|
719
742
|
const [messages, setMessages] = useState([]);
|
|
720
743
|
const [confirmationMode, setConfirmationMode] = useState(null);
|
|
@@ -840,699 +863,593 @@ const RemoteModeDisplay = ({ messageBuffer, logPath, onExit, onSwitchToLocal })
|
|
|
840
863
|
));
|
|
841
864
|
};
|
|
842
865
|
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
}
|
|
847
|
-
queue = [];
|
|
848
|
-
readResolve;
|
|
849
|
-
readReject;
|
|
850
|
-
isDone = false;
|
|
851
|
-
hasError;
|
|
852
|
-
started = false;
|
|
853
|
-
/**
|
|
854
|
-
* Implements async iterable protocol
|
|
855
|
-
*/
|
|
856
|
-
[Symbol.asyncIterator]() {
|
|
857
|
-
if (this.started) {
|
|
858
|
-
throw new Error("Stream can only be iterated once");
|
|
859
|
-
}
|
|
860
|
-
this.started = true;
|
|
861
|
-
return this;
|
|
862
|
-
}
|
|
863
|
-
/**
|
|
864
|
-
* Gets the next value from the stream
|
|
865
|
-
*/
|
|
866
|
-
async next() {
|
|
867
|
-
if (this.queue.length > 0) {
|
|
868
|
-
return Promise.resolve({
|
|
869
|
-
done: false,
|
|
870
|
-
value: this.queue.shift()
|
|
871
|
-
});
|
|
872
|
-
}
|
|
873
|
-
if (this.isDone) {
|
|
874
|
-
return Promise.resolve({ done: true, value: void 0 });
|
|
875
|
-
}
|
|
876
|
-
if (this.hasError) {
|
|
877
|
-
return Promise.reject(this.hasError);
|
|
878
|
-
}
|
|
879
|
-
return new Promise((resolve, reject) => {
|
|
880
|
-
this.readResolve = resolve;
|
|
881
|
-
this.readReject = reject;
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
/**
|
|
885
|
-
* Adds a value to the stream
|
|
886
|
-
*/
|
|
887
|
-
enqueue(value) {
|
|
888
|
-
if (this.readResolve) {
|
|
889
|
-
const resolve = this.readResolve;
|
|
890
|
-
this.readResolve = void 0;
|
|
891
|
-
this.readReject = void 0;
|
|
892
|
-
resolve({ done: false, value });
|
|
893
|
-
} else {
|
|
894
|
-
this.queue.push(value);
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
/**
|
|
898
|
-
* Marks the stream as complete
|
|
899
|
-
*/
|
|
900
|
-
done() {
|
|
901
|
-
this.isDone = true;
|
|
902
|
-
if (this.readResolve) {
|
|
903
|
-
const resolve = this.readResolve;
|
|
904
|
-
this.readResolve = void 0;
|
|
905
|
-
this.readReject = void 0;
|
|
906
|
-
resolve({ done: true, value: void 0 });
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
/**
|
|
910
|
-
* Propagates an error through the stream
|
|
911
|
-
*/
|
|
912
|
-
error(error) {
|
|
913
|
-
this.hasError = error;
|
|
914
|
-
if (this.readReject) {
|
|
915
|
-
const reject = this.readReject;
|
|
916
|
-
this.readResolve = void 0;
|
|
917
|
-
this.readReject = void 0;
|
|
918
|
-
reject(error);
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
/**
|
|
922
|
-
* Implements async iterator cleanup
|
|
923
|
-
*/
|
|
924
|
-
async return() {
|
|
925
|
-
this.isDone = true;
|
|
926
|
-
if (this.returned) {
|
|
927
|
-
this.returned();
|
|
928
|
-
}
|
|
929
|
-
return Promise.resolve({ done: true, value: void 0 });
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
class AbortError extends Error {
|
|
934
|
-
constructor(message) {
|
|
935
|
-
super(message);
|
|
936
|
-
this.name = "AbortError";
|
|
937
|
-
}
|
|
866
|
+
function getClaudeSettingsPath() {
|
|
867
|
+
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
|
|
868
|
+
return join(claudeConfigDir, "settings.json");
|
|
938
869
|
}
|
|
939
|
-
|
|
940
|
-
const __filename$1 = fileURLToPath(import.meta.url);
|
|
941
|
-
const __dirname$1 = join(__filename$1, "..");
|
|
942
|
-
function getGlobalClaudeVersion() {
|
|
870
|
+
function readClaudeSettings() {
|
|
943
871
|
try {
|
|
944
|
-
const
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
logger.debug(`[
|
|
953
|
-
return
|
|
954
|
-
} catch {
|
|
872
|
+
const settingsPath = getClaudeSettingsPath();
|
|
873
|
+
if (!existsSync(settingsPath)) {
|
|
874
|
+
logger.debug(`[ClaudeSettings] No Claude settings file found at ${settingsPath}`);
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
const settingsContent = readFileSync(settingsPath, "utf-8");
|
|
878
|
+
const settings = JSON.parse(settingsContent);
|
|
879
|
+
logger.debug(`[ClaudeSettings] Successfully read Claude settings from ${settingsPath}`);
|
|
880
|
+
logger.debug(`[ClaudeSettings] includeCoAuthoredBy: ${settings.includeCoAuthoredBy}`);
|
|
881
|
+
return settings;
|
|
882
|
+
} catch (error) {
|
|
883
|
+
logger.debug(`[ClaudeSettings] Error reading Claude settings: ${error}`);
|
|
955
884
|
return null;
|
|
956
885
|
}
|
|
957
886
|
}
|
|
958
|
-
function
|
|
959
|
-
const
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
const pathKey = process.platform === "win32" ? "Path" : "PATH";
|
|
963
|
-
const actualPathKey = Object.keys(env).find((k) => k.toLowerCase() === "path") || pathKey;
|
|
964
|
-
if (env[actualPathKey]) {
|
|
965
|
-
const cleanPath = env[actualPathKey].split(pathSep).filter((p) => {
|
|
966
|
-
const normalizedP = p.replace(/\\/g, "/").toLowerCase();
|
|
967
|
-
const normalizedCwd = cwd.replace(/\\/g, "/").toLowerCase();
|
|
968
|
-
return !normalizedP.startsWith(normalizedCwd);
|
|
969
|
-
}).join(pathSep);
|
|
970
|
-
env[actualPathKey] = cleanPath;
|
|
971
|
-
logger.debug(`[Claude SDK] Cleaned PATH, removed local paths from: ${cwd}`);
|
|
972
|
-
}
|
|
973
|
-
if (isBun()) {
|
|
974
|
-
Object.keys(env).forEach((key) => {
|
|
975
|
-
if (key.startsWith("BUN_")) {
|
|
976
|
-
delete env[key];
|
|
977
|
-
}
|
|
978
|
-
});
|
|
979
|
-
logger.debug("[Claude SDK] Removed Bun-specific environment variables for Node.js compatibility");
|
|
887
|
+
function shouldIncludeCoAuthoredBy() {
|
|
888
|
+
const settings = readClaudeSettings();
|
|
889
|
+
if (!settings || settings.includeCoAuthoredBy === void 0) {
|
|
890
|
+
return true;
|
|
980
891
|
}
|
|
981
|
-
return
|
|
892
|
+
return settings.includeCoAuthoredBy;
|
|
982
893
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
}).trim();
|
|
1005
|
-
if (result && existsSync(result)) {
|
|
1006
|
-
logger.debug(`[Claude SDK] Found global claude path via which: ${result}`);
|
|
1007
|
-
return result;
|
|
1008
|
-
}
|
|
1009
|
-
} catch {
|
|
1010
|
-
}
|
|
894
|
+
|
|
895
|
+
const BASE_SYSTEM_PROMPT = (() => trimIdent(`
|
|
896
|
+
Help human.
|
|
897
|
+
`))();
|
|
898
|
+
const CO_AUTHORED_CREDITS = (() => trimIdent(`
|
|
899
|
+
When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
|
|
900
|
+
|
|
901
|
+
<main commit message>
|
|
902
|
+
|
|
903
|
+
Generated with [Claude Code](https://claude.ai/code)
|
|
904
|
+
via [Happy](https://happy.engineering)
|
|
905
|
+
|
|
906
|
+
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
907
|
+
Co-Authored-By: Happy <yesreply@happy.engineering>
|
|
908
|
+
`))();
|
|
909
|
+
const systemPrompt = (() => {
|
|
910
|
+
const includeCoAuthored = shouldIncludeCoAuthoredBy();
|
|
911
|
+
if (includeCoAuthored) {
|
|
912
|
+
return BASE_SYSTEM_PROMPT + "\n\n" + CO_AUTHORED_CREDITS;
|
|
913
|
+
} else {
|
|
914
|
+
return BASE_SYSTEM_PROMPT;
|
|
1011
915
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
function
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
logger.debug(`[Claude SDK] Using HAPPY_CLAUDE_PATH: ${process.env.HAPPY_CLAUDE_PATH}`);
|
|
1018
|
-
return process.env.HAPPY_CLAUDE_PATH;
|
|
1019
|
-
}
|
|
1020
|
-
if (process.env.HAPPY_USE_BUNDLED_CLAUDE === "1") {
|
|
1021
|
-
logger.debug(`[Claude SDK] Forced bundled version: ${nodeModulesPath}`);
|
|
1022
|
-
return nodeModulesPath;
|
|
1023
|
-
}
|
|
1024
|
-
const globalPath = findGlobalClaudePath();
|
|
1025
|
-
if (!globalPath) {
|
|
1026
|
-
logger.debug(`[Claude SDK] No global claude found, using bundled: ${nodeModulesPath}`);
|
|
1027
|
-
return nodeModulesPath;
|
|
1028
|
-
}
|
|
1029
|
-
const globalVersion = getGlobalClaudeVersion();
|
|
1030
|
-
logger.debug(`[Claude SDK] Global version: ${globalVersion || "unknown"}`);
|
|
1031
|
-
if (!globalVersion) {
|
|
1032
|
-
logger.debug(`[Claude SDK] Cannot compare versions, using global: ${globalPath}`);
|
|
1033
|
-
return globalPath;
|
|
1034
|
-
}
|
|
1035
|
-
return globalPath;
|
|
1036
|
-
}
|
|
1037
|
-
function logDebug(message) {
|
|
1038
|
-
if (process.env.DEBUG) {
|
|
1039
|
-
logger.debug(message);
|
|
1040
|
-
console.log(message);
|
|
916
|
+
})();
|
|
917
|
+
|
|
918
|
+
function getToolDescriptor(toolName) {
|
|
919
|
+
if (toolName === "exit_plan_mode" || toolName === "ExitPlanMode") {
|
|
920
|
+
return { edit: false, exitPlan: true };
|
|
1041
921
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
for await (const message of stream) {
|
|
1045
|
-
if (abort?.aborted) break;
|
|
1046
|
-
stdin.write(JSON.stringify(message) + "\n");
|
|
922
|
+
if (toolName === "Edit" || toolName === "MultiEdit" || toolName === "Write" || toolName === "NotebookEdit") {
|
|
923
|
+
return { edit: true, exitPlan: false };
|
|
1047
924
|
}
|
|
1048
|
-
|
|
925
|
+
return { edit: false, exitPlan: false };
|
|
1049
926
|
}
|
|
1050
927
|
|
|
1051
|
-
class
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
this.processExitPromise = processExitPromise;
|
|
1056
|
-
this.canCallTool = canCallTool;
|
|
1057
|
-
this.readMessages();
|
|
1058
|
-
this.sdkMessages = this.readSdkMessages();
|
|
1059
|
-
}
|
|
1060
|
-
pendingControlResponses = /* @__PURE__ */ new Map();
|
|
1061
|
-
cancelControllers = /* @__PURE__ */ new Map();
|
|
1062
|
-
sdkMessages;
|
|
1063
|
-
inputStream = new Stream();
|
|
1064
|
-
canCallTool;
|
|
1065
|
-
/**
|
|
1066
|
-
* Set an error on the stream
|
|
1067
|
-
*/
|
|
1068
|
-
setError(error) {
|
|
1069
|
-
this.inputStream.error(error);
|
|
928
|
+
class ClaudeAcpPermissionHandler extends BasePermissionHandler {
|
|
929
|
+
currentPermissionMode = "default";
|
|
930
|
+
constructor(session) {
|
|
931
|
+
super(session);
|
|
1070
932
|
}
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
*/
|
|
1074
|
-
next(...args) {
|
|
1075
|
-
return this.sdkMessages.next(...args);
|
|
933
|
+
getLogPrefix() {
|
|
934
|
+
return "[ClaudeACP]";
|
|
1076
935
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
}
|
|
1081
|
-
return Promise.resolve({ done: true, value: void 0 });
|
|
936
|
+
setPermissionMode(mode) {
|
|
937
|
+
this.currentPermissionMode = mode;
|
|
938
|
+
logger.debug(`${this.getLogPrefix()} Permission mode set to: ${mode}`);
|
|
1082
939
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
940
|
+
shouldAutoApprove(toolName) {
|
|
941
|
+
const descriptor = getToolDescriptor(toolName);
|
|
942
|
+
switch (this.currentPermissionMode) {
|
|
943
|
+
case "bypassPermissions":
|
|
944
|
+
case "yolo":
|
|
945
|
+
return true;
|
|
946
|
+
case "acceptEdits":
|
|
947
|
+
return descriptor.edit;
|
|
948
|
+
default:
|
|
949
|
+
return false;
|
|
1086
950
|
}
|
|
1087
|
-
return Promise.reject(e);
|
|
1088
|
-
}
|
|
1089
|
-
[Symbol.asyncIterator]() {
|
|
1090
|
-
return this.sdkMessages;
|
|
1091
951
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
handler(controlResponse.response);
|
|
1107
|
-
}
|
|
1108
|
-
continue;
|
|
1109
|
-
} else if (message.type === "control_request") {
|
|
1110
|
-
await this.handleControlRequest(message);
|
|
1111
|
-
continue;
|
|
1112
|
-
} else if (message.type === "control_cancel_request") {
|
|
1113
|
-
this.handleControlCancelRequest(message);
|
|
1114
|
-
continue;
|
|
1115
|
-
}
|
|
1116
|
-
this.inputStream.enqueue(message);
|
|
1117
|
-
} catch (e) {
|
|
1118
|
-
logger.debug(line);
|
|
952
|
+
async handleToolCall(toolCallId, toolName, input) {
|
|
953
|
+
if (this.shouldAutoApprove(toolName)) {
|
|
954
|
+
const decision = this.currentPermissionMode === "bypassPermissions" || this.currentPermissionMode === "yolo" ? "approved_for_session" : "approved";
|
|
955
|
+
this.session.updateAgentState((currentState) => ({
|
|
956
|
+
...currentState,
|
|
957
|
+
completedRequests: {
|
|
958
|
+
...currentState.completedRequests,
|
|
959
|
+
[toolCallId]: {
|
|
960
|
+
tool: toolName,
|
|
961
|
+
arguments: input,
|
|
962
|
+
createdAt: Date.now(),
|
|
963
|
+
completedAt: Date.now(),
|
|
964
|
+
status: "approved",
|
|
965
|
+
decision
|
|
1119
966
|
}
|
|
1120
967
|
}
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
this.inputStream.error(error);
|
|
1125
|
-
} finally {
|
|
1126
|
-
this.inputStream.done();
|
|
1127
|
-
this.cleanupControllers();
|
|
1128
|
-
rl.close();
|
|
968
|
+
}));
|
|
969
|
+
logger.debug(`${this.getLogPrefix()} Auto-approved tool ${toolName} (${toolCallId}) in ${this.currentPermissionMode} mode`);
|
|
970
|
+
return { decision };
|
|
1129
971
|
}
|
|
972
|
+
return this.registerPendingRequest(
|
|
973
|
+
toolCallId,
|
|
974
|
+
toolName,
|
|
975
|
+
input,
|
|
976
|
+
` in ${this.currentPermissionMode} mode`
|
|
977
|
+
);
|
|
1130
978
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
function normalizeClaudeBackendError(error) {
|
|
982
|
+
const record = typeof error === "object" && error !== null ? error : null;
|
|
983
|
+
const text = formatDisplayMessage(error).trim();
|
|
984
|
+
const stderrText = record ? formatDisplayMessage(record.stderr).trim() : "";
|
|
985
|
+
const detailText = record ? formatDisplayMessage(record.detail).trim() : "";
|
|
986
|
+
const searchable = [text, stderrText, detailText].filter(Boolean).join("\n").trim();
|
|
987
|
+
return searchable || "Claude ACP backend exited unexpectedly";
|
|
988
|
+
}
|
|
989
|
+
const CLAUDE_ACP_LEGACY_FINAL_MESSAGE_SHADOW = "claude-acp-final-message-shadow";
|
|
990
|
+
async function claudeAcpRemoteLauncher(session) {
|
|
991
|
+
const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
992
|
+
const messageBuffer = new MessageBuffer({ enabled: hasTTY });
|
|
993
|
+
let inkInstance = null;
|
|
994
|
+
let shouldExit = false;
|
|
995
|
+
let abortController = new AbortController();
|
|
996
|
+
let runtimeHandle = null;
|
|
997
|
+
let unsubscribeRuntimeMessages = null;
|
|
998
|
+
let currentModeHash = null;
|
|
999
|
+
let pending = null;
|
|
1000
|
+
let accumulatedResponse = "";
|
|
1001
|
+
let isResponseInProgress = false;
|
|
1002
|
+
let taskStartedSent = false;
|
|
1003
|
+
let currentAssistantMessageId = null;
|
|
1004
|
+
let currentThinkingMessageId = null;
|
|
1005
|
+
let shouldInjectHistoryOnNextSession = false;
|
|
1006
|
+
let readyAlreadySent = false;
|
|
1007
|
+
const permissionHandler = new ClaudeAcpPermissionHandler(session.client);
|
|
1008
|
+
const selectionHandler = new ProviderSelectionHandler(session.client, "Claude");
|
|
1009
|
+
const conversationHistory = new ConversationHistory$1({
|
|
1010
|
+
maxMessages: 20,
|
|
1011
|
+
maxCharacters: 5e4
|
|
1012
|
+
});
|
|
1013
|
+
const rotateAbortController = () => {
|
|
1014
|
+
const activeController = abortController;
|
|
1015
|
+
abortController = new AbortController();
|
|
1016
|
+
return activeController;
|
|
1017
|
+
};
|
|
1018
|
+
const sendReady = () => {
|
|
1019
|
+
session.client.sendSessionEvent({ type: "ready" });
|
|
1020
|
+
try {
|
|
1021
|
+
session.api.push().sendToAllDevices(
|
|
1022
|
+
"It's ready!",
|
|
1023
|
+
"Claude is waiting for your command",
|
|
1024
|
+
{ sessionId: session.client.sessionId }
|
|
1025
|
+
);
|
|
1026
|
+
} catch (pushError) {
|
|
1027
|
+
logger.debug("[ClaudeACP] Failed to send ready push", pushError);
|
|
1137
1028
|
}
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1029
|
+
};
|
|
1030
|
+
const emitStatusMessage = (message) => {
|
|
1031
|
+
messageBuffer.addMessage(message, "status");
|
|
1032
|
+
session.client.sendSessionEvent({ type: "message", message });
|
|
1033
|
+
};
|
|
1034
|
+
const emitUserVisibleErrorMessage = (message) => {
|
|
1035
|
+
emitStatusMessage(message);
|
|
1036
|
+
session.client.sendAgentMessage("claude", {
|
|
1037
|
+
type: "message",
|
|
1038
|
+
message
|
|
1039
|
+
});
|
|
1040
|
+
};
|
|
1041
|
+
const resetTurnState = () => {
|
|
1042
|
+
accumulatedResponse = "";
|
|
1043
|
+
isResponseInProgress = false;
|
|
1044
|
+
taskStartedSent = false;
|
|
1045
|
+
currentAssistantMessageId = null;
|
|
1046
|
+
currentThinkingMessageId = null;
|
|
1047
|
+
session.onThinkingChange(false);
|
|
1048
|
+
};
|
|
1049
|
+
const emitFinalAssistantMessage = () => {
|
|
1050
|
+
const finalMessage = accumulatedResponse.trim();
|
|
1051
|
+
if (!finalMessage) {
|
|
1052
|
+
accumulatedResponse = "";
|
|
1053
|
+
isResponseInProgress = false;
|
|
1054
|
+
return;
|
|
1145
1055
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
/**
|
|
1151
|
-
* Send control request to Claude process
|
|
1152
|
-
*/
|
|
1153
|
-
request(request, childStdin) {
|
|
1154
|
-
const requestId = Math.random().toString(36).substring(2, 15);
|
|
1155
|
-
const sdkRequest = {
|
|
1156
|
-
request_id: requestId,
|
|
1157
|
-
type: "control_request",
|
|
1158
|
-
request
|
|
1159
|
-
};
|
|
1160
|
-
return new Promise((resolve, reject) => {
|
|
1161
|
-
this.pendingControlResponses.set(requestId, (response) => {
|
|
1162
|
-
if (response.subtype === "success") {
|
|
1163
|
-
resolve(response);
|
|
1164
|
-
} else {
|
|
1165
|
-
reject(new Error(response.error));
|
|
1166
|
-
}
|
|
1167
|
-
});
|
|
1168
|
-
childStdin.write(JSON.stringify(sdkRequest) + "\n");
|
|
1056
|
+
conversationHistory.addAssistantMessage(finalMessage);
|
|
1057
|
+
session.client.sendAgentMessage("claude", {
|
|
1058
|
+
type: "message",
|
|
1059
|
+
message: finalMessage
|
|
1169
1060
|
});
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1061
|
+
session.client.sendClaudeSessionMessage({
|
|
1062
|
+
type: "assistant",
|
|
1063
|
+
uuid: randomUUID(),
|
|
1064
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1065
|
+
sessionId: session.sessionId ?? void 0,
|
|
1066
|
+
happyCompatibilityShadow: CLAUDE_ACP_LEGACY_FINAL_MESSAGE_SHADOW,
|
|
1067
|
+
message: {
|
|
1068
|
+
role: "assistant",
|
|
1069
|
+
content: [{
|
|
1070
|
+
type: "text",
|
|
1071
|
+
text: finalMessage,
|
|
1072
|
+
happyCompatibilityShadow: CLAUDE_ACP_LEGACY_FINAL_MESSAGE_SHADOW
|
|
1073
|
+
}]
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
accumulatedResponse = "";
|
|
1077
|
+
isResponseInProgress = false;
|
|
1078
|
+
};
|
|
1079
|
+
const disposeRuntimeHandle = async () => {
|
|
1080
|
+
if (!runtimeHandle) {
|
|
1178
1081
|
return;
|
|
1179
1082
|
}
|
|
1180
|
-
const
|
|
1181
|
-
|
|
1083
|
+
const activeHandle = runtimeHandle;
|
|
1084
|
+
runtimeHandle = null;
|
|
1085
|
+
unsubscribeRuntimeMessages?.();
|
|
1086
|
+
unsubscribeRuntimeMessages = null;
|
|
1182
1087
|
try {
|
|
1183
|
-
|
|
1184
|
-
const controlResponse = {
|
|
1185
|
-
type: "control_response",
|
|
1186
|
-
response: {
|
|
1187
|
-
subtype: "success",
|
|
1188
|
-
request_id: request.request_id,
|
|
1189
|
-
response
|
|
1190
|
-
}
|
|
1191
|
-
};
|
|
1192
|
-
this.childStdin.write(JSON.stringify(controlResponse) + "\n");
|
|
1088
|
+
await activeHandle.dispose();
|
|
1193
1089
|
} catch (error) {
|
|
1194
|
-
|
|
1195
|
-
type: "control_response",
|
|
1196
|
-
response: {
|
|
1197
|
-
subtype: "error",
|
|
1198
|
-
request_id: request.request_id,
|
|
1199
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1200
|
-
}
|
|
1201
|
-
};
|
|
1202
|
-
this.childStdin.write(JSON.stringify(controlErrorResponse) + "\n");
|
|
1203
|
-
} finally {
|
|
1204
|
-
this.cancelControllers.delete(request.request_id);
|
|
1090
|
+
logger.debug("[ClaudeACP] Error disposing runtime handle:", error);
|
|
1205
1091
|
}
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
this.cancelControllers.delete(request.request_id);
|
|
1092
|
+
};
|
|
1093
|
+
const queueHistoryInjectionForRestart = (reason) => {
|
|
1094
|
+
messageBuffer.addMessage("\u2550".repeat(40), "status");
|
|
1095
|
+
if (conversationHistory.hasHistory()) {
|
|
1096
|
+
shouldInjectHistoryOnNextSession = true;
|
|
1097
|
+
const message = `${reason} Preserving ${conversationHistory.size()} earlier messages of context.`;
|
|
1098
|
+
emitStatusMessage(message);
|
|
1099
|
+
logger.debug(`[ClaudeACP] Will inject conversation history after restart: ${conversationHistory.getSummary()}`);
|
|
1100
|
+
return;
|
|
1216
1101
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
if (!this.canCallTool) {
|
|
1225
|
-
throw new Error("canCallTool callback is not provided.");
|
|
1226
|
-
}
|
|
1227
|
-
return this.canCallTool(request.request.tool_name, request.request.input, {
|
|
1228
|
-
signal
|
|
1102
|
+
emitStatusMessage(reason);
|
|
1103
|
+
};
|
|
1104
|
+
const setupRuntimeMessageHandler = (activeRuntimeHandle) => {
|
|
1105
|
+
const forwardAgentMessage = (agentMessage) => {
|
|
1106
|
+
forwardAgentMessageToProviderSession(agentMessage, {
|
|
1107
|
+
provider: "claude",
|
|
1108
|
+
send: (body) => session.client.sendAgentMessage("claude", body)
|
|
1229
1109
|
});
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1110
|
+
};
|
|
1111
|
+
unsubscribeRuntimeMessages?.();
|
|
1112
|
+
unsubscribeRuntimeMessages = activeRuntimeHandle.onMessage((msg) => {
|
|
1113
|
+
switch (msg.type) {
|
|
1114
|
+
case "model-output": {
|
|
1115
|
+
const text = msg.textDelta ?? msg.fullText ?? "";
|
|
1116
|
+
if (!text) {
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
if (!isResponseInProgress) {
|
|
1120
|
+
if (currentThinkingMessageId) {
|
|
1121
|
+
messageBuffer.removeMessage(currentThinkingMessageId);
|
|
1122
|
+
currentThinkingMessageId = null;
|
|
1123
|
+
}
|
|
1124
|
+
currentAssistantMessageId = messageBuffer.addMessage(text, "assistant");
|
|
1125
|
+
isResponseInProgress = true;
|
|
1126
|
+
} else if (currentAssistantMessageId) {
|
|
1127
|
+
const updated = messageBuffer.updateMessage(currentAssistantMessageId, text);
|
|
1128
|
+
if (!updated) {
|
|
1129
|
+
currentAssistantMessageId = messageBuffer.addMessage(text, "assistant");
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
accumulatedResponse += text;
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
case "status": {
|
|
1136
|
+
if (msg.status === "running") {
|
|
1137
|
+
session.onThinkingChange(true);
|
|
1138
|
+
if (!taskStartedSent) {
|
|
1139
|
+
session.client.sendAgentMessage("claude", {
|
|
1140
|
+
type: "task_started",
|
|
1141
|
+
id: randomUUID()
|
|
1142
|
+
});
|
|
1143
|
+
taskStartedSent = true;
|
|
1144
|
+
}
|
|
1145
|
+
if (!isResponseInProgress && !currentThinkingMessageId) {
|
|
1146
|
+
currentThinkingMessageId = messageBuffer.addMessage("Thinking...", "system");
|
|
1147
|
+
}
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
if (msg.status === "idle" || msg.status === "stopped") {
|
|
1151
|
+
session.onThinkingChange(false);
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
if (msg.status === "error") {
|
|
1155
|
+
messageBuffer.addMessage(`Error: ${normalizeClaudeBackendError(msg.detail)}`, "status");
|
|
1156
|
+
}
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
case "tool-call": {
|
|
1160
|
+
const toolArgs = truncateDisplayMessage(msg.args, 100);
|
|
1161
|
+
messageBuffer.addMessage(
|
|
1162
|
+
`Executing: ${msg.toolName}${toolArgs ? ` ${toolArgs}` : ""}`,
|
|
1163
|
+
"tool"
|
|
1164
|
+
);
|
|
1165
|
+
forwardAgentMessage(msg);
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
case "tool-result": {
|
|
1169
|
+
const resultText = truncateDisplayMessage(msg.result, 200);
|
|
1170
|
+
messageBuffer.addMessage(resultText ? `Result: ${resultText}` : "Tool completed", "result");
|
|
1171
|
+
forwardAgentMessage(msg);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
case "fs-edit": {
|
|
1175
|
+
messageBuffer.addMessage(`File edit: ${msg.description}`, "tool");
|
|
1176
|
+
forwardAgentMessage(msg);
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
case "terminal-output": {
|
|
1180
|
+
const output = formatDisplayMessage(msg.data);
|
|
1181
|
+
messageBuffer.addMessage(output, "result");
|
|
1182
|
+
forwardAgentMessage({
|
|
1183
|
+
...msg,
|
|
1184
|
+
data: output
|
|
1185
|
+
});
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
case "permission-request": {
|
|
1189
|
+
forwardAgentMessage(msg);
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
case "token-count": {
|
|
1193
|
+
forwardAgentMessage(msg);
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
case "exec-approval-request":
|
|
1197
|
+
case "patch-apply-begin":
|
|
1198
|
+
case "patch-apply-end": {
|
|
1199
|
+
forwardAgentMessage(msg);
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
case "event": {
|
|
1203
|
+
if (msg.name === "thinking") {
|
|
1204
|
+
const payload = msg.payload;
|
|
1205
|
+
const thinkingText = typeof payload?.text === "string" ? payload.text : "";
|
|
1206
|
+
if (thinkingText) {
|
|
1207
|
+
session.client.sendAgentMessage("claude", {
|
|
1208
|
+
type: "thinking",
|
|
1209
|
+
text: thinkingText
|
|
1210
|
+
});
|
|
1211
|
+
if (!isResponseInProgress) {
|
|
1212
|
+
const preview = `[Thinking] ${thinkingText.substring(0, 100)}...`;
|
|
1213
|
+
if (currentThinkingMessageId) {
|
|
1214
|
+
messageBuffer.updateMessage(currentThinkingMessageId, preview, { mode: "replace" });
|
|
1215
|
+
} else {
|
|
1216
|
+
currentThinkingMessageId = messageBuffer.addMessage(preview, "system");
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
default:
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1329
1226
|
});
|
|
1330
|
-
}
|
|
1331
|
-
const cleanup = () => {
|
|
1332
|
-
if (!child.killed) {
|
|
1333
|
-
child.kill("SIGTERM");
|
|
1334
|
-
}
|
|
1335
1227
|
};
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1228
|
+
const createRuntimeHandle = async (mode) => {
|
|
1229
|
+
const { session: nextRuntimeHandle, factoryResult } = await launchRuntimeHandleWithFactoryResult({
|
|
1230
|
+
provider: "claude",
|
|
1231
|
+
cwd: session.path,
|
|
1232
|
+
createBackendResult: (opts) => createClaudeBackend({
|
|
1233
|
+
...opts,
|
|
1234
|
+
baseArgs: session.claudeArgs,
|
|
1235
|
+
mcpServers: session.mcpServers,
|
|
1236
|
+
permissionHandler,
|
|
1237
|
+
permissionMode: mode.permissionMode,
|
|
1238
|
+
model: mode.model,
|
|
1239
|
+
fallbackModel: mode.fallbackModel,
|
|
1240
|
+
customSystemPrompt: mode.customSystemPrompt ? `${mode.customSystemPrompt}
|
|
1241
|
+
|
|
1242
|
+
${systemPrompt}` : void 0,
|
|
1243
|
+
appendSystemPrompt: mode.appendSystemPrompt ? `${mode.appendSystemPrompt}
|
|
1244
|
+
|
|
1245
|
+
${systemPrompt}` : systemPrompt,
|
|
1246
|
+
allowedTools: mode.allowedTools ? mode.allowedTools.concat(session.allowedTools ?? []) : session.allowedTools,
|
|
1247
|
+
disallowedTools: mode.disallowedTools,
|
|
1248
|
+
settingsPath: session.hookSettingsPath
|
|
1249
|
+
})
|
|
1348
1250
|
});
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1251
|
+
logger.debug("[ClaudeACP] Started Claude ACP backend", {
|
|
1252
|
+
command: factoryResult.command,
|
|
1253
|
+
args: factoryResult.args
|
|
1254
|
+
});
|
|
1255
|
+
runtimeHandle = nextRuntimeHandle;
|
|
1256
|
+
setupRuntimeMessageHandler(nextRuntimeHandle);
|
|
1257
|
+
session.consumeOneTimeFlags();
|
|
1258
|
+
return nextRuntimeHandle;
|
|
1259
|
+
};
|
|
1260
|
+
const abortActiveTurn = async () => {
|
|
1261
|
+
const activeController = rotateAbortController();
|
|
1262
|
+
activeController.abort();
|
|
1263
|
+
session.onThinkingChange(false);
|
|
1264
|
+
if (runtimeHandle) {
|
|
1265
|
+
await runtimeHandle.cancel().catch((error) => {
|
|
1266
|
+
logger.debug("[ClaudeACP] Error cancelling runtime handle:", error);
|
|
1267
|
+
});
|
|
1363
1268
|
}
|
|
1364
|
-
});
|
|
1365
|
-
return query2;
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
function parseCompact(message) {
|
|
1369
|
-
const trimmed = message.trim();
|
|
1370
|
-
if (trimmed === "/compact") {
|
|
1371
|
-
return {
|
|
1372
|
-
isCompact: true,
|
|
1373
|
-
originalMessage: trimmed
|
|
1374
|
-
};
|
|
1375
|
-
}
|
|
1376
|
-
if (trimmed.startsWith("/compact ")) {
|
|
1377
|
-
return {
|
|
1378
|
-
isCompact: true,
|
|
1379
|
-
originalMessage: trimmed
|
|
1380
|
-
};
|
|
1381
|
-
}
|
|
1382
|
-
return {
|
|
1383
|
-
isCompact: false,
|
|
1384
|
-
originalMessage: message
|
|
1385
1269
|
};
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1270
|
+
const completeSyntheticTurn = () => {
|
|
1271
|
+
session.client.sendAgentMessage("claude", {
|
|
1272
|
+
type: "task_complete",
|
|
1273
|
+
id: randomUUID()
|
|
1274
|
+
});
|
|
1391
1275
|
};
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
type: "clear"
|
|
1405
|
-
};
|
|
1406
|
-
}
|
|
1407
|
-
return {
|
|
1408
|
-
type: null
|
|
1276
|
+
const handleClearCommand = async () => {
|
|
1277
|
+
logger.debug("[ClaudeACP] /clear command received - resetting runtime and local history");
|
|
1278
|
+
conversationHistory.clear();
|
|
1279
|
+
shouldInjectHistoryOnNextSession = false;
|
|
1280
|
+
await disposeRuntimeHandle();
|
|
1281
|
+
session.clearSessionId();
|
|
1282
|
+
currentModeHash = null;
|
|
1283
|
+
permissionHandler.reset();
|
|
1284
|
+
selectionHandler.reset();
|
|
1285
|
+
resetTurnState();
|
|
1286
|
+
emitStatusMessage("Context was reset");
|
|
1287
|
+
completeSyntheticTurn();
|
|
1409
1288
|
};
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
/**
|
|
1438
|
-
* Mark the iterable as complete
|
|
1439
|
-
*/
|
|
1440
|
-
end() {
|
|
1441
|
-
if (this.isDone) {
|
|
1442
|
-
return;
|
|
1443
|
-
}
|
|
1444
|
-
this.isDone = true;
|
|
1445
|
-
this.cleanup();
|
|
1289
|
+
const handleSwitchToLocal = async () => {
|
|
1290
|
+
const message = "Daemon-spawned Claude ACP sessions stay in remote mode.";
|
|
1291
|
+
logger.debug("[ClaudeACP] Ignoring switch request because daemon sessions are remote-only");
|
|
1292
|
+
emitStatusMessage(message);
|
|
1293
|
+
};
|
|
1294
|
+
const handleAbort = async () => {
|
|
1295
|
+
logger.debug("[ClaudeACP] Abort requested - stopping current task");
|
|
1296
|
+
await abortActiveTurn();
|
|
1297
|
+
};
|
|
1298
|
+
if (hasTTY) {
|
|
1299
|
+
console.clear();
|
|
1300
|
+
inkInstance = render(React.createElement(RemoteModeDisplay, {
|
|
1301
|
+
messageBuffer,
|
|
1302
|
+
logPath: process.env.DEBUG ? session.logPath : void 0,
|
|
1303
|
+
onExit: async () => {
|
|
1304
|
+
shouldExit = true;
|
|
1305
|
+
await handleAbort();
|
|
1306
|
+
},
|
|
1307
|
+
onSwitchToLocal: () => {
|
|
1308
|
+
void handleSwitchToLocal();
|
|
1309
|
+
}
|
|
1310
|
+
}), {
|
|
1311
|
+
exitOnCtrlC: false,
|
|
1312
|
+
patchConsole: false
|
|
1313
|
+
});
|
|
1446
1314
|
}
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
if (this.isDone) {
|
|
1452
|
-
return;
|
|
1315
|
+
if (hasTTY) {
|
|
1316
|
+
process.stdin.resume();
|
|
1317
|
+
if (process.stdin.isTTY) {
|
|
1318
|
+
process.stdin.setRawMode(true);
|
|
1453
1319
|
}
|
|
1454
|
-
|
|
1455
|
-
this.isDone = true;
|
|
1456
|
-
this.cleanup();
|
|
1320
|
+
process.stdin.setEncoding("utf8");
|
|
1457
1321
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
if (
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1322
|
+
session.client.rpcHandlerManager.registerHandler("abort", handleAbort);
|
|
1323
|
+
session.client.rpcHandlerManager.registerHandler("switch", handleSwitchToLocal);
|
|
1324
|
+
try {
|
|
1325
|
+
while (!shouldExit) {
|
|
1326
|
+
let message = pending;
|
|
1327
|
+
pending = null;
|
|
1328
|
+
if (!message) {
|
|
1329
|
+
const waitSignal = abortController.signal;
|
|
1330
|
+
const batch = await session.queue.waitForMessagesAndGetAsString(waitSignal);
|
|
1331
|
+
if (!batch) {
|
|
1332
|
+
if (waitSignal.aborted && !shouldExit) {
|
|
1333
|
+
continue;
|
|
1334
|
+
}
|
|
1335
|
+
break;
|
|
1336
|
+
}
|
|
1337
|
+
message = batch;
|
|
1338
|
+
}
|
|
1339
|
+
if (!message) {
|
|
1340
|
+
break;
|
|
1341
|
+
}
|
|
1342
|
+
if (runtimeHandle && currentModeHash && message.hash !== currentModeHash) {
|
|
1343
|
+
queueHistoryInjectionForRestart("Starting new Claude ACP session (execution settings changed)...");
|
|
1344
|
+
await disposeRuntimeHandle();
|
|
1345
|
+
session.clearSessionId();
|
|
1346
|
+
currentModeHash = null;
|
|
1347
|
+
pending = message;
|
|
1348
|
+
permissionHandler.reset();
|
|
1349
|
+
selectionHandler.reset();
|
|
1350
|
+
resetTurnState();
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
currentModeHash = message.hash;
|
|
1354
|
+
readyAlreadySent = false;
|
|
1355
|
+
const specialCommand = parseSpecialCommand(message.message);
|
|
1356
|
+
if (specialCommand.type === "clear") {
|
|
1357
|
+
await handleClearCommand();
|
|
1358
|
+
if (!shouldExit && !pending && session.queue.size() === 0 && !readyAlreadySent) {
|
|
1359
|
+
sendReady();
|
|
1360
|
+
readyAlreadySent = true;
|
|
1361
|
+
}
|
|
1362
|
+
continue;
|
|
1363
|
+
}
|
|
1364
|
+
permissionHandler.setPermissionMode(message.mode.permissionMode);
|
|
1365
|
+
messageBuffer.addMessage(message.message, "user");
|
|
1366
|
+
let shouldClearHistoryAfterTurn = false;
|
|
1367
|
+
try {
|
|
1368
|
+
const activeRuntimeHandle = runtimeHandle ?? await createRuntimeHandle(message.mode);
|
|
1369
|
+
let promptToSend = message.message;
|
|
1370
|
+
if (shouldInjectHistoryOnNextSession && conversationHistory.hasHistory()) {
|
|
1371
|
+
const historyContext = conversationHistory.getContextForNewSession(
|
|
1372
|
+
"Continue from the prior Claude ACP session using the conversation below as context."
|
|
1373
|
+
);
|
|
1374
|
+
promptToSend = historyContext + promptToSend;
|
|
1375
|
+
logger.debug(`[ClaudeACP] Injected conversation history context (${historyContext.length} chars)`);
|
|
1376
|
+
}
|
|
1377
|
+
if (specialCommand.type === "compact") {
|
|
1378
|
+
emitStatusMessage("Compaction started");
|
|
1379
|
+
}
|
|
1380
|
+
conversationHistory.addUserMessage(message.message);
|
|
1381
|
+
await activeRuntimeHandle.sendPrompt(promptToSend);
|
|
1382
|
+
await waitForResponseCompleteWithAbort(activeRuntimeHandle.backend, abortController.signal);
|
|
1383
|
+
shouldInjectHistoryOnNextSession = false;
|
|
1384
|
+
shouldClearHistoryAfterTurn = specialCommand.type === "compact";
|
|
1385
|
+
} catch (error) {
|
|
1386
|
+
logger.warn("[ClaudeACP] Error in Claude ACP session:", error);
|
|
1387
|
+
const isAbortError = error instanceof Error && error.name === "AbortError";
|
|
1388
|
+
const isExpectedInterruption = isAbortError || abortController.signal.aborted || shouldExit;
|
|
1389
|
+
if (isExpectedInterruption) {
|
|
1390
|
+
session.client.sendAgentMessage("claude", {
|
|
1391
|
+
type: "turn_aborted",
|
|
1392
|
+
id: randomUUID()
|
|
1393
|
+
});
|
|
1394
|
+
emitStatusMessage("Aborted by user");
|
|
1395
|
+
} else {
|
|
1396
|
+
const errorMessage = normalizeClaudeBackendError(error);
|
|
1397
|
+
emitUserVisibleErrorMessage(errorMessage);
|
|
1398
|
+
if (conversationHistory.hasHistory()) {
|
|
1399
|
+
shouldInjectHistoryOnNextSession = true;
|
|
1400
|
+
}
|
|
1401
|
+
await disposeRuntimeHandle();
|
|
1402
|
+
session.clearSessionId();
|
|
1403
|
+
currentModeHash = null;
|
|
1404
|
+
}
|
|
1405
|
+
} finally {
|
|
1406
|
+
emitFinalAssistantMessage();
|
|
1407
|
+
if (shouldClearHistoryAfterTurn) {
|
|
1408
|
+
conversationHistory.clear();
|
|
1409
|
+
emitStatusMessage("Compaction completed");
|
|
1410
|
+
}
|
|
1411
|
+
if (!shouldExit) {
|
|
1412
|
+
session.client.sendAgentMessage("claude", {
|
|
1413
|
+
type: "task_complete",
|
|
1414
|
+
id: randomUUID()
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
permissionHandler.reset();
|
|
1418
|
+
selectionHandler.reset();
|
|
1419
|
+
resetTurnState();
|
|
1420
|
+
if (!shouldExit && !pending && session.queue.size() === 0 && !readyAlreadySent) {
|
|
1421
|
+
sendReady();
|
|
1422
|
+
readyAlreadySent = true;
|
|
1423
|
+
}
|
|
1468
1424
|
}
|
|
1469
1425
|
}
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1426
|
+
} finally {
|
|
1427
|
+
await disposeRuntimeHandle();
|
|
1428
|
+
permissionHandler.reset();
|
|
1429
|
+
selectionHandler.reset();
|
|
1430
|
+
if (process.stdin.isTTY) {
|
|
1431
|
+
try {
|
|
1432
|
+
process.stdin.setRawMode(false);
|
|
1433
|
+
} catch {
|
|
1434
|
+
}
|
|
1477
1435
|
}
|
|
1478
|
-
if (
|
|
1479
|
-
|
|
1480
|
-
|
|
1436
|
+
if (hasTTY) {
|
|
1437
|
+
try {
|
|
1438
|
+
process.stdin.pause();
|
|
1439
|
+
} catch {
|
|
1481
1440
|
}
|
|
1482
|
-
return { done: true, value: void 0 };
|
|
1483
1441
|
}
|
|
1484
|
-
|
|
1485
|
-
this.waiters.push({ resolve, reject });
|
|
1442
|
+
session.client.rpcHandlerManager.registerHandler("abort", async () => {
|
|
1486
1443
|
});
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
async return(_value) {
|
|
1492
|
-
this.end();
|
|
1493
|
-
return { done: true, value: void 0 };
|
|
1494
|
-
}
|
|
1495
|
-
/**
|
|
1496
|
-
* AsyncIterableIterator throw implementation
|
|
1497
|
-
*/
|
|
1498
|
-
async throw(e) {
|
|
1499
|
-
this.setError(e instanceof Error ? e : new Error(String(e)));
|
|
1500
|
-
throw this.error;
|
|
1501
|
-
}
|
|
1502
|
-
/**
|
|
1503
|
-
* Make this iterable
|
|
1504
|
-
*/
|
|
1505
|
-
[Symbol.asyncIterator]() {
|
|
1506
|
-
if (this.started) {
|
|
1507
|
-
throw new Error("PushableAsyncIterable can only be iterated once");
|
|
1444
|
+
session.client.rpcHandlerManager.registerHandler("switch", async () => {
|
|
1445
|
+
});
|
|
1446
|
+
if (inkInstance) {
|
|
1447
|
+
inkInstance.unmount();
|
|
1508
1448
|
}
|
|
1509
|
-
|
|
1510
|
-
return this;
|
|
1511
|
-
}
|
|
1512
|
-
/**
|
|
1513
|
-
* Check if the iterable is done
|
|
1514
|
-
*/
|
|
1515
|
-
get done() {
|
|
1516
|
-
return this.isDone;
|
|
1517
|
-
}
|
|
1518
|
-
/**
|
|
1519
|
-
* Check if the iterable has an error
|
|
1520
|
-
*/
|
|
1521
|
-
get hasError() {
|
|
1522
|
-
return this.error !== null;
|
|
1523
|
-
}
|
|
1524
|
-
/**
|
|
1525
|
-
* Get the current queue size
|
|
1526
|
-
*/
|
|
1527
|
-
get queueSize() {
|
|
1528
|
-
return this.queue.length;
|
|
1529
|
-
}
|
|
1530
|
-
/**
|
|
1531
|
-
* Get the number of waiting consumers
|
|
1532
|
-
*/
|
|
1533
|
-
get waiterCount() {
|
|
1534
|
-
return this.waiters.length;
|
|
1449
|
+
messageBuffer.clear();
|
|
1535
1450
|
}
|
|
1451
|
+
logger.debug("[ClaudeACP] Remote launcher returning: exit");
|
|
1452
|
+
return "exit";
|
|
1536
1453
|
}
|
|
1537
1454
|
|
|
1538
1455
|
async function awaitFileExist(file, timeout = 1e4) {
|
|
@@ -1548,58 +1465,6 @@ async function awaitFileExist(file, timeout = 1e4) {
|
|
|
1548
1465
|
return false;
|
|
1549
1466
|
}
|
|
1550
1467
|
|
|
1551
|
-
function getClaudeSettingsPath() {
|
|
1552
|
-
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
|
|
1553
|
-
return join(claudeConfigDir, "settings.json");
|
|
1554
|
-
}
|
|
1555
|
-
function readClaudeSettings() {
|
|
1556
|
-
try {
|
|
1557
|
-
const settingsPath = getClaudeSettingsPath();
|
|
1558
|
-
if (!existsSync(settingsPath)) {
|
|
1559
|
-
logger.debug(`[ClaudeSettings] No Claude settings file found at ${settingsPath}`);
|
|
1560
|
-
return null;
|
|
1561
|
-
}
|
|
1562
|
-
const settingsContent = readFileSync(settingsPath, "utf-8");
|
|
1563
|
-
const settings = JSON.parse(settingsContent);
|
|
1564
|
-
logger.debug(`[ClaudeSettings] Successfully read Claude settings from ${settingsPath}`);
|
|
1565
|
-
logger.debug(`[ClaudeSettings] includeCoAuthoredBy: ${settings.includeCoAuthoredBy}`);
|
|
1566
|
-
return settings;
|
|
1567
|
-
} catch (error) {
|
|
1568
|
-
logger.debug(`[ClaudeSettings] Error reading Claude settings: ${error}`);
|
|
1569
|
-
return null;
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
function shouldIncludeCoAuthoredBy() {
|
|
1573
|
-
const settings = readClaudeSettings();
|
|
1574
|
-
if (!settings || settings.includeCoAuthoredBy === void 0) {
|
|
1575
|
-
return true;
|
|
1576
|
-
}
|
|
1577
|
-
return settings.includeCoAuthoredBy;
|
|
1578
|
-
}
|
|
1579
|
-
|
|
1580
|
-
const BASE_SYSTEM_PROMPT = (() => trimIdent(`
|
|
1581
|
-
Help human.
|
|
1582
|
-
`))();
|
|
1583
|
-
const CO_AUTHORED_CREDITS = (() => trimIdent(`
|
|
1584
|
-
When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
|
|
1585
|
-
|
|
1586
|
-
<main commit message>
|
|
1587
|
-
|
|
1588
|
-
Generated with [Claude Code](https://claude.ai/code)
|
|
1589
|
-
via [Happy](https://happy.engineering)
|
|
1590
|
-
|
|
1591
|
-
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
1592
|
-
Co-Authored-By: Happy <yesreply@happy.engineering>
|
|
1593
|
-
`))();
|
|
1594
|
-
const systemPrompt = (() => {
|
|
1595
|
-
const includeCoAuthored = shouldIncludeCoAuthoredBy();
|
|
1596
|
-
if (includeCoAuthored) {
|
|
1597
|
-
return BASE_SYSTEM_PROMPT + "\n\n" + CO_AUTHORED_CREDITS;
|
|
1598
|
-
} else {
|
|
1599
|
-
return BASE_SYSTEM_PROMPT;
|
|
1600
|
-
}
|
|
1601
|
-
})();
|
|
1602
|
-
|
|
1603
1468
|
async function claudeRemote(opts) {
|
|
1604
1469
|
let startFrom = opts.sessionId;
|
|
1605
1470
|
if (opts.sessionId && !claudeCheckSession(opts.sessionId, opts.path, opts.transcriptPath)) {
|
|
@@ -1803,16 +1668,6 @@ function getToolName(toolName) {
|
|
|
1803
1668
|
return toTitleCase(toolName);
|
|
1804
1669
|
}
|
|
1805
1670
|
|
|
1806
|
-
function getToolDescriptor(toolName) {
|
|
1807
|
-
if (toolName === "exit_plan_mode" || toolName === "ExitPlanMode") {
|
|
1808
|
-
return { edit: false, exitPlan: true };
|
|
1809
|
-
}
|
|
1810
|
-
if (toolName === "Edit" || toolName === "MultiEdit" || toolName === "Write" || toolName === "NotebookEdit") {
|
|
1811
|
-
return { edit: true, exitPlan: false };
|
|
1812
|
-
}
|
|
1813
|
-
return { edit: false, exitPlan: false };
|
|
1814
|
-
}
|
|
1815
|
-
|
|
1816
1671
|
class PermissionHandler {
|
|
1817
1672
|
toolCalls = [];
|
|
1818
1673
|
responses = /* @__PURE__ */ new Map();
|
|
@@ -2982,9 +2837,7 @@ async function loop(opts) {
|
|
|
2982
2837
|
return { type: "exit", value: result.code };
|
|
2983
2838
|
},
|
|
2984
2839
|
remote: async () => {
|
|
2985
|
-
const reason = await claudeRemoteLauncher(session
|
|
2986
|
-
allowSwitchToLocal: opts.startedBy !== "daemon"
|
|
2987
|
-
});
|
|
2840
|
+
const reason = opts.startedBy === "daemon" ? await claudeAcpRemoteLauncher(session) : await claudeRemoteLauncher(session);
|
|
2988
2841
|
if (reason === "switch") {
|
|
2989
2842
|
return { type: "switch", mode: "local" };
|
|
2990
2843
|
}
|