@posthog/agent 2.1.125 → 2.1.137
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/adapters/claude/conversion/tool-use-to-acp.d.ts +14 -28
- package/dist/adapters/claude/conversion/tool-use-to-acp.js +116 -164
- package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -1
- package/dist/adapters/claude/permissions/permission-options.js +33 -0
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
- package/dist/adapters/claude/tools.js +21 -11
- package/dist/adapters/claude/tools.js.map +1 -1
- package/dist/agent.js +1251 -640
- package/dist/agent.js.map +1 -1
- package/dist/posthog-api.js +2 -2
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.js +1295 -684
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +1278 -669
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +2 -2
- package/src/adapters/base-acp-agent.ts +6 -3
- package/src/adapters/claude/UPSTREAM.md +63 -0
- package/src/adapters/claude/claude-agent.ts +682 -421
- package/src/adapters/claude/conversion/sdk-to-acp.ts +249 -85
- package/src/adapters/claude/conversion/tool-use-to-acp.ts +174 -149
- package/src/adapters/claude/hooks.ts +53 -1
- package/src/adapters/claude/permissions/permission-handlers.ts +39 -21
- package/src/adapters/claude/session/commands.ts +13 -9
- package/src/adapters/claude/session/mcp-config.ts +2 -5
- package/src/adapters/claude/session/options.ts +58 -6
- package/src/adapters/claude/session/settings.ts +326 -0
- package/src/adapters/claude/tools.ts +1 -0
- package/src/adapters/claude/types.ts +38 -0
- package/src/execution-mode.ts +26 -10
- package/src/server/agent-server.test.ts +41 -1
- package/src/session-log-writer.ts +1 -36
- package/src/utils/common.ts +1 -1
package/dist/agent.js
CHANGED
|
@@ -262,13 +262,16 @@ function nodeWritableToWebWritable(nodeStream) {
|
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
// src/adapters/claude/claude-agent.ts
|
|
265
|
-
import
|
|
266
|
-
import * as
|
|
267
|
-
import * as
|
|
265
|
+
import { randomUUID } from "crypto";
|
|
266
|
+
import * as fs3 from "fs";
|
|
267
|
+
import * as os4 from "os";
|
|
268
|
+
import * as path4 from "path";
|
|
268
269
|
import {
|
|
269
270
|
RequestError as RequestError2
|
|
270
271
|
} from "@agentclientprotocol/sdk";
|
|
271
272
|
import {
|
|
273
|
+
getSessionMessages,
|
|
274
|
+
listSessions,
|
|
272
275
|
query
|
|
273
276
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
274
277
|
import { v7 as uuidv7 } from "uuid";
|
|
@@ -276,7 +279,7 @@ import { v7 as uuidv7 } from "uuid";
|
|
|
276
279
|
// package.json
|
|
277
280
|
var package_default = {
|
|
278
281
|
name: "@posthog/agent",
|
|
279
|
-
version: "2.1.
|
|
282
|
+
version: "2.1.137",
|
|
280
283
|
repository: "https://github.com/PostHog/twig",
|
|
281
284
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
282
285
|
exports: {
|
|
@@ -352,7 +355,6 @@ var package_default = {
|
|
|
352
355
|
"@twig/git": "workspace:*",
|
|
353
356
|
"@types/bun": "latest",
|
|
354
357
|
"@types/tar": "^6.1.13",
|
|
355
|
-
minimatch: "^10.0.3",
|
|
356
358
|
msw: "^2.12.7",
|
|
357
359
|
tsup: "^8.5.1",
|
|
358
360
|
tsx: "^4.20.6",
|
|
@@ -373,6 +375,7 @@ var package_default = {
|
|
|
373
375
|
commander: "^14.0.2",
|
|
374
376
|
hono: "^4.11.7",
|
|
375
377
|
jsonwebtoken: "^9.0.2",
|
|
378
|
+
minimatch: "^10.0.3",
|
|
376
379
|
tar: "^7.5.0",
|
|
377
380
|
uuid: "13.0.0",
|
|
378
381
|
"yoga-wasm-web": "^0.3.3",
|
|
@@ -406,7 +409,7 @@ function unreachable(value, logger) {
|
|
|
406
409
|
try {
|
|
407
410
|
valueAsString = JSON.stringify(value);
|
|
408
411
|
} catch {
|
|
409
|
-
valueAsString = value;
|
|
412
|
+
valueAsString = String(value);
|
|
410
413
|
}
|
|
411
414
|
logger.error(`Unexpected case: ${valueAsString}`);
|
|
412
415
|
}
|
|
@@ -514,19 +517,20 @@ var BaseAcpAgent = class {
|
|
|
514
517
|
}
|
|
515
518
|
async cancel(params) {
|
|
516
519
|
if (this.sessionId !== params.sessionId) {
|
|
517
|
-
throw new Error("Session
|
|
520
|
+
throw new Error("Session ID mismatch");
|
|
518
521
|
}
|
|
519
522
|
this.session.cancelled = true;
|
|
520
523
|
const meta = params._meta;
|
|
521
524
|
if (meta?.interruptReason) {
|
|
522
525
|
this.session.interruptReason = meta.interruptReason;
|
|
523
526
|
}
|
|
524
|
-
await this.
|
|
527
|
+
await this.interrupt();
|
|
525
528
|
}
|
|
526
529
|
async closeSession() {
|
|
527
530
|
try {
|
|
528
531
|
this.session.abortController.abort();
|
|
529
532
|
await this.cancel({ sessionId: this.sessionId });
|
|
533
|
+
this.session.settingsManager.dispose();
|
|
530
534
|
this.logger.info("Closed session", { sessionId: this.sessionId });
|
|
531
535
|
} catch (err) {
|
|
532
536
|
this.logger.warn("Failed to close session", {
|
|
@@ -701,8 +705,8 @@ var ToolContentBuilder = class {
|
|
|
701
705
|
this.items.push({ type: "content", content: image(data, mimeType, uri) });
|
|
702
706
|
return this;
|
|
703
707
|
}
|
|
704
|
-
diff(
|
|
705
|
-
this.items.push({ type: "diff", path:
|
|
708
|
+
diff(path6, oldText, newText) {
|
|
709
|
+
this.items.push({ type: "diff", path: path6, oldText, newText });
|
|
706
710
|
return this;
|
|
707
711
|
}
|
|
708
712
|
build() {
|
|
@@ -722,7 +726,7 @@ var registerHookCallback = (toolUseID, {
|
|
|
722
726
|
onPostToolUseHook
|
|
723
727
|
};
|
|
724
728
|
};
|
|
725
|
-
var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
|
|
729
|
+
var createPostToolUseHook = ({ onModeChange, logger }) => async (input, toolUseID) => {
|
|
726
730
|
if (input.hook_event_name === "PostToolUse") {
|
|
727
731
|
const toolName = input.tool_name;
|
|
728
732
|
if (onModeChange && toolName === "EnterPlanMode") {
|
|
@@ -737,11 +741,54 @@ var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
|
|
|
737
741
|
input.tool_response
|
|
738
742
|
);
|
|
739
743
|
delete toolUseCallbacks[toolUseID];
|
|
744
|
+
} else {
|
|
745
|
+
logger?.error(
|
|
746
|
+
`No onPostToolUseHook found for tool use ID: ${toolUseID}`
|
|
747
|
+
);
|
|
748
|
+
delete toolUseCallbacks[toolUseID];
|
|
740
749
|
}
|
|
741
750
|
}
|
|
742
751
|
}
|
|
743
752
|
return { continue: true };
|
|
744
753
|
};
|
|
754
|
+
var createPreToolUseHook = (settingsManager, logger) => async (input, _toolUseID) => {
|
|
755
|
+
if (input.hook_event_name !== "PreToolUse") {
|
|
756
|
+
return { continue: true };
|
|
757
|
+
}
|
|
758
|
+
const toolName = input.tool_name;
|
|
759
|
+
const toolInput = input.tool_input;
|
|
760
|
+
const permissionCheck = settingsManager.checkPermission(
|
|
761
|
+
toolName,
|
|
762
|
+
toolInput
|
|
763
|
+
);
|
|
764
|
+
if (permissionCheck.decision !== "ask") {
|
|
765
|
+
logger.info(
|
|
766
|
+
`[PreToolUseHook] Tool: ${toolName}, Decision: ${permissionCheck.decision}, Rule: ${permissionCheck.rule}`
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
switch (permissionCheck.decision) {
|
|
770
|
+
case "allow":
|
|
771
|
+
return {
|
|
772
|
+
continue: true,
|
|
773
|
+
hookSpecificOutput: {
|
|
774
|
+
hookEventName: "PreToolUse",
|
|
775
|
+
permissionDecision: "allow",
|
|
776
|
+
permissionDecisionReason: `Allowed by settings rule: ${permissionCheck.rule}`
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
case "deny":
|
|
780
|
+
return {
|
|
781
|
+
continue: true,
|
|
782
|
+
hookSpecificOutput: {
|
|
783
|
+
hookEventName: "PreToolUse",
|
|
784
|
+
permissionDecision: "deny",
|
|
785
|
+
permissionDecisionReason: `Denied by settings rule: ${permissionCheck.rule}`
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
default:
|
|
789
|
+
return { continue: true };
|
|
790
|
+
}
|
|
791
|
+
};
|
|
745
792
|
|
|
746
793
|
// src/adapters/claude/mcp/tool-metadata.ts
|
|
747
794
|
var mcpToolMetadataCache = /* @__PURE__ */ new Map();
|
|
@@ -818,79 +865,7 @@ var SYSTEM_REMINDER = `
|
|
|
818
865
|
<system-reminder>
|
|
819
866
|
Whenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.
|
|
820
867
|
</system-reminder>`;
|
|
821
|
-
function
|
|
822
|
-
let currentContent = fileContent;
|
|
823
|
-
const randomHex = Array.from(crypto.getRandomValues(new Uint8Array(5))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
824
|
-
const markerPrefix = `__REPLACE_MARKER_${randomHex}_`;
|
|
825
|
-
let markerCounter = 0;
|
|
826
|
-
const markers = [];
|
|
827
|
-
for (const edit of edits) {
|
|
828
|
-
if (edit.oldText === "") {
|
|
829
|
-
throw new Error(
|
|
830
|
-
`The provided \`old_string\` is empty.
|
|
831
|
-
|
|
832
|
-
No edits were applied.`
|
|
833
|
-
);
|
|
834
|
-
}
|
|
835
|
-
if (edit.replaceAll) {
|
|
836
|
-
const parts = [];
|
|
837
|
-
let lastIndex = 0;
|
|
838
|
-
let searchIndex = 0;
|
|
839
|
-
while (true) {
|
|
840
|
-
const index = currentContent.indexOf(edit.oldText, searchIndex);
|
|
841
|
-
if (index === -1) {
|
|
842
|
-
if (searchIndex === 0) {
|
|
843
|
-
throw new Error(
|
|
844
|
-
`The provided \`old_string\` does not appear in the file: "${edit.oldText}".
|
|
845
|
-
|
|
846
|
-
No edits were applied.`
|
|
847
|
-
);
|
|
848
|
-
}
|
|
849
|
-
break;
|
|
850
|
-
}
|
|
851
|
-
parts.push(currentContent.substring(lastIndex, index));
|
|
852
|
-
const marker = `${markerPrefix}${markerCounter++}__`;
|
|
853
|
-
markers.push(marker);
|
|
854
|
-
parts.push(marker + edit.newText);
|
|
855
|
-
lastIndex = index + edit.oldText.length;
|
|
856
|
-
searchIndex = lastIndex;
|
|
857
|
-
}
|
|
858
|
-
parts.push(currentContent.substring(lastIndex));
|
|
859
|
-
currentContent = parts.join("");
|
|
860
|
-
} else {
|
|
861
|
-
const index = currentContent.indexOf(edit.oldText);
|
|
862
|
-
if (index === -1) {
|
|
863
|
-
throw new Error(
|
|
864
|
-
`The provided \`old_string\` does not appear in the file: "${edit.oldText}".
|
|
865
|
-
|
|
866
|
-
No edits were applied.`
|
|
867
|
-
);
|
|
868
|
-
} else {
|
|
869
|
-
const marker = `${markerPrefix}${markerCounter++}__`;
|
|
870
|
-
markers.push(marker);
|
|
871
|
-
currentContent = currentContent.substring(0, index) + marker + edit.newText + currentContent.substring(index + edit.oldText.length);
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
const lineNumbers = [];
|
|
876
|
-
for (const marker of markers) {
|
|
877
|
-
const index = currentContent.indexOf(marker);
|
|
878
|
-
if (index !== -1) {
|
|
879
|
-
const lineNumber = Math.max(
|
|
880
|
-
0,
|
|
881
|
-
currentContent.substring(0, index).split(/\r\n|\r|\n/).length - 1
|
|
882
|
-
);
|
|
883
|
-
lineNumbers.push(lineNumber);
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
let finalContent = currentContent;
|
|
887
|
-
for (const marker of markers) {
|
|
888
|
-
finalContent = finalContent.replace(marker, "");
|
|
889
|
-
}
|
|
890
|
-
const uniqueLineNumbers = [...new Set(lineNumbers)].sort();
|
|
891
|
-
return { newContent: finalContent, lineNumbers: uniqueLineNumbers };
|
|
892
|
-
}
|
|
893
|
-
function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ debug: false, prefix: "[ClaudeTools]" })) {
|
|
868
|
+
function toolInfoFromToolUse(toolUse, options) {
|
|
894
869
|
const name = toolUse.name;
|
|
895
870
|
const input = toolUse.input;
|
|
896
871
|
switch (name) {
|
|
@@ -915,6 +890,13 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
915
890
|
locations: input?.notebook_path ? [{ path: String(input.notebook_path) }] : []
|
|
916
891
|
};
|
|
917
892
|
case "Bash":
|
|
893
|
+
if (options?.supportsTerminalOutput && options?.toolUseId) {
|
|
894
|
+
return {
|
|
895
|
+
title: input?.description ? String(input.description) : "Execute command",
|
|
896
|
+
kind: "execute",
|
|
897
|
+
content: [{ type: "terminal", terminalId: options.toolUseId }]
|
|
898
|
+
};
|
|
899
|
+
}
|
|
918
900
|
return {
|
|
919
901
|
title: input?.description ? String(input.description) : "Execute command",
|
|
920
902
|
kind: "execute",
|
|
@@ -935,11 +917,11 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
935
917
|
case "Read": {
|
|
936
918
|
let limit = "";
|
|
937
919
|
const inputLimit = input?.limit;
|
|
938
|
-
const inputOffset = input?.offset ??
|
|
920
|
+
const inputOffset = input?.offset ?? 1;
|
|
939
921
|
if (inputLimit) {
|
|
940
|
-
limit = ` (${inputOffset
|
|
941
|
-
} else if (inputOffset) {
|
|
942
|
-
limit = ` (from line ${inputOffset
|
|
922
|
+
limit = ` (${inputOffset} - ${inputOffset + inputLimit - 1})`;
|
|
923
|
+
} else if (inputOffset > 1) {
|
|
924
|
+
limit = ` (from line ${inputOffset})`;
|
|
943
925
|
}
|
|
944
926
|
return {
|
|
945
927
|
title: `Read ${input?.file_path ? String(input.file_path) : "File"}${limit}`,
|
|
@@ -961,39 +943,21 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
961
943
|
locations: []
|
|
962
944
|
};
|
|
963
945
|
case "Edit": {
|
|
964
|
-
const
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
let affectedLines = [];
|
|
968
|
-
if (path5 && oldText) {
|
|
969
|
-
try {
|
|
970
|
-
const oldContent = cachedFileContent[path5] || "";
|
|
971
|
-
const newContent = replaceAndCalculateLocation(oldContent, [
|
|
972
|
-
{
|
|
973
|
-
oldText,
|
|
974
|
-
newText,
|
|
975
|
-
replaceAll: false
|
|
976
|
-
}
|
|
977
|
-
]);
|
|
978
|
-
oldText = oldContent;
|
|
979
|
-
newText = newContent.newContent;
|
|
980
|
-
affectedLines = newContent.lineNumbers;
|
|
981
|
-
} catch (e) {
|
|
982
|
-
logger.error("Failed to edit file", e);
|
|
983
|
-
}
|
|
984
|
-
}
|
|
946
|
+
const path6 = input?.file_path ? String(input.file_path) : void 0;
|
|
947
|
+
const oldText = input?.old_string ? String(input.old_string) : null;
|
|
948
|
+
const newText = input?.new_string ? String(input.new_string) : "";
|
|
985
949
|
return {
|
|
986
|
-
title:
|
|
950
|
+
title: path6 ? `Edit \`${path6}\`` : "Edit",
|
|
987
951
|
kind: "edit",
|
|
988
|
-
content: input &&
|
|
952
|
+
content: input && path6 ? [
|
|
989
953
|
{
|
|
990
954
|
type: "diff",
|
|
991
|
-
path:
|
|
955
|
+
path: path6,
|
|
992
956
|
oldText,
|
|
993
957
|
newText
|
|
994
958
|
}
|
|
995
959
|
] : [],
|
|
996
|
-
locations:
|
|
960
|
+
locations: path6 ? [{ path: path6 }] : []
|
|
997
961
|
};
|
|
998
962
|
}
|
|
999
963
|
case "Write": {
|
|
@@ -1047,10 +1011,10 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1047
1011
|
}
|
|
1048
1012
|
if (input?.output_mode) {
|
|
1049
1013
|
switch (input.output_mode) {
|
|
1050
|
-
case "
|
|
1014
|
+
case "files_with_matches":
|
|
1051
1015
|
label += " -l";
|
|
1052
1016
|
break;
|
|
1053
|
-
case "
|
|
1017
|
+
case "count":
|
|
1054
1018
|
label += " -c";
|
|
1055
1019
|
break;
|
|
1056
1020
|
default:
|
|
@@ -1069,7 +1033,9 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1069
1033
|
if (input?.multiline) {
|
|
1070
1034
|
label += " -P";
|
|
1071
1035
|
}
|
|
1072
|
-
|
|
1036
|
+
if (input?.pattern) {
|
|
1037
|
+
label += ` "${String(input.pattern)}"`;
|
|
1038
|
+
}
|
|
1073
1039
|
if (input?.path) {
|
|
1074
1040
|
label += ` ${String(input.path)}`;
|
|
1075
1041
|
}
|
|
@@ -1163,7 +1129,49 @@ function mcpToolInfo(name, _input) {
|
|
|
1163
1129
|
content: []
|
|
1164
1130
|
};
|
|
1165
1131
|
}
|
|
1166
|
-
function
|
|
1132
|
+
function toolUpdateFromEditToolResponse(toolResponse) {
|
|
1133
|
+
if (!toolResponse || typeof toolResponse !== "object") return null;
|
|
1134
|
+
const response = toolResponse;
|
|
1135
|
+
const patches = response.structuredPatch;
|
|
1136
|
+
if (!Array.isArray(patches) || patches.length === 0) return null;
|
|
1137
|
+
const content = [];
|
|
1138
|
+
const locations = [];
|
|
1139
|
+
for (const patch of patches) {
|
|
1140
|
+
if (!patch.hunks || patch.hunks.length === 0) continue;
|
|
1141
|
+
const filePath = patch.newFileName || patch.oldFileName;
|
|
1142
|
+
const oldLines = [];
|
|
1143
|
+
const newLines = [];
|
|
1144
|
+
for (const hunk of patch.hunks) {
|
|
1145
|
+
for (const line of hunk.lines) {
|
|
1146
|
+
if (line.startsWith("-")) {
|
|
1147
|
+
oldLines.push(line.slice(1));
|
|
1148
|
+
} else if (line.startsWith("+")) {
|
|
1149
|
+
newLines.push(line.slice(1));
|
|
1150
|
+
} else if (line.startsWith(" ")) {
|
|
1151
|
+
oldLines.push(line.slice(1));
|
|
1152
|
+
newLines.push(line.slice(1));
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
content.push({
|
|
1157
|
+
type: "diff",
|
|
1158
|
+
path: filePath,
|
|
1159
|
+
oldText: oldLines.join("\n"),
|
|
1160
|
+
newText: newLines.join("\n")
|
|
1161
|
+
});
|
|
1162
|
+
const firstHunk = patch.hunks[0];
|
|
1163
|
+
locations.push({
|
|
1164
|
+
path: filePath,
|
|
1165
|
+
line: firstHunk.newStart
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
if (content.length === 0) return null;
|
|
1169
|
+
return { content, locations };
|
|
1170
|
+
}
|
|
1171
|
+
function toolUpdateFromToolResult(toolResult, toolUse, options) {
|
|
1172
|
+
if ("is_error" in toolResult && toolResult.is_error && toolResult.content && toolResult.content.length > 0) {
|
|
1173
|
+
return toAcpContentUpdate(toolResult.content, true);
|
|
1174
|
+
}
|
|
1167
1175
|
switch (toolUse?.name) {
|
|
1168
1176
|
case "Read":
|
|
1169
1177
|
if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
|
|
@@ -1180,6 +1188,16 @@ function toolUpdateFromToolResult(toolResult, toolUse) {
|
|
|
1180
1188
|
)
|
|
1181
1189
|
};
|
|
1182
1190
|
}
|
|
1191
|
+
if (itemObj.type === "image" && itemObj.source) {
|
|
1192
|
+
return {
|
|
1193
|
+
type: "content",
|
|
1194
|
+
content: {
|
|
1195
|
+
type: "image",
|
|
1196
|
+
data: itemObj.source.data ?? "",
|
|
1197
|
+
mimeType: itemObj.source.media_type ?? "image/png"
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1183
1201
|
return {
|
|
1184
1202
|
type: "content",
|
|
1185
1203
|
content: item
|
|
@@ -1195,18 +1213,51 @@ function toolUpdateFromToolResult(toolResult, toolUse) {
|
|
|
1195
1213
|
}
|
|
1196
1214
|
return {};
|
|
1197
1215
|
case "Bash": {
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1216
|
+
const result = toolResult.content;
|
|
1217
|
+
const terminalId = "tool_use_id" in toolResult ? String(toolResult.tool_use_id) : "";
|
|
1218
|
+
const isError = "is_error" in toolResult && toolResult.is_error;
|
|
1219
|
+
let output = "";
|
|
1220
|
+
let exitCode = isError ? 1 : 0;
|
|
1221
|
+
if (result && typeof result === "object" && "type" in result && result.type === "bash_code_execution_result") {
|
|
1222
|
+
const bashResult = result;
|
|
1223
|
+
output = [bashResult.stdout, bashResult.stderr].filter(Boolean).join("\n");
|
|
1224
|
+
exitCode = bashResult.return_code;
|
|
1225
|
+
} else if (typeof result === "string") {
|
|
1226
|
+
output = result;
|
|
1227
|
+
} else if (Array.isArray(result) && result.length > 0 && "text" in result[0] && typeof result[0].text === "string") {
|
|
1228
|
+
output = result.map((c) => c.text ?? "").join("\n");
|
|
1229
|
+
}
|
|
1230
|
+
if (options?.supportsTerminalOutput) {
|
|
1231
|
+
return {
|
|
1232
|
+
content: [{ type: "terminal", terminalId }],
|
|
1233
|
+
_meta: {
|
|
1234
|
+
terminal_info: {
|
|
1235
|
+
terminal_id: terminalId
|
|
1236
|
+
},
|
|
1237
|
+
terminal_output: {
|
|
1238
|
+
terminal_id: terminalId,
|
|
1239
|
+
data: output
|
|
1240
|
+
},
|
|
1241
|
+
terminal_exit: {
|
|
1242
|
+
terminal_id: terminalId,
|
|
1243
|
+
exit_code: exitCode,
|
|
1244
|
+
signal: null
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
if (output.trim()) {
|
|
1250
|
+
return {
|
|
1251
|
+
content: toolContent().text(`\`\`\`console
|
|
1252
|
+
${output.trimEnd()}
|
|
1253
|
+
\`\`\``).build()
|
|
1254
|
+
};
|
|
1207
1255
|
}
|
|
1208
1256
|
return {};
|
|
1209
1257
|
}
|
|
1258
|
+
case "Edit":
|
|
1259
|
+
case "Write":
|
|
1260
|
+
return {};
|
|
1210
1261
|
case "ExitPlanMode": {
|
|
1211
1262
|
return { title: "Exited Plan Mode" };
|
|
1212
1263
|
}
|
|
@@ -1366,6 +1417,7 @@ function handleThinkingChunk(chunk, parentToolCallId) {
|
|
|
1366
1417
|
return update;
|
|
1367
1418
|
}
|
|
1368
1419
|
function handleToolUseChunk(chunk, ctx) {
|
|
1420
|
+
const alreadyCached = chunk.id in ctx.toolUseCache;
|
|
1369
1421
|
ctx.toolUseCache[chunk.id] = chunk;
|
|
1370
1422
|
if (chunk.name === "TodoWrite") {
|
|
1371
1423
|
const input = chunk.input;
|
|
@@ -1377,37 +1429,60 @@ function handleToolUseChunk(chunk, ctx) {
|
|
|
1377
1429
|
}
|
|
1378
1430
|
return null;
|
|
1379
1431
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1432
|
+
if (!alreadyCached && ctx.registerHooks !== false) {
|
|
1433
|
+
registerHookCallback(chunk.id, {
|
|
1434
|
+
onPostToolUseHook: async (toolUseId, _toolInput, toolResponse) => {
|
|
1435
|
+
const toolUse = ctx.toolUseCache[toolUseId];
|
|
1436
|
+
if (toolUse) {
|
|
1437
|
+
const editUpdate = toolUse.name === "Edit" ? toolUpdateFromEditToolResponse(toolResponse) : null;
|
|
1438
|
+
await ctx.client.sessionUpdate({
|
|
1439
|
+
sessionId: ctx.sessionId,
|
|
1440
|
+
update: {
|
|
1441
|
+
_meta: toolMeta(toolUse.name, toolResponse, ctx.parentToolCallId),
|
|
1442
|
+
toolCallId: toolUseId,
|
|
1443
|
+
sessionUpdate: "tool_call_update",
|
|
1444
|
+
...editUpdate ? editUpdate : {}
|
|
1445
|
+
}
|
|
1446
|
+
});
|
|
1447
|
+
} else {
|
|
1448
|
+
ctx.logger.error(
|
|
1449
|
+
`Got a tool response for tool use that wasn't tracked: ${toolUseId}`
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1396
1452
|
}
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1399
1455
|
let rawInput;
|
|
1400
1456
|
try {
|
|
1401
1457
|
rawInput = JSON.parse(JSON.stringify(chunk.input));
|
|
1402
1458
|
} catch {
|
|
1403
1459
|
}
|
|
1460
|
+
const toolInfo = toolInfoFromToolUse(chunk, {
|
|
1461
|
+
supportsTerminalOutput: ctx.supportsTerminalOutput,
|
|
1462
|
+
toolUseId: chunk.id
|
|
1463
|
+
});
|
|
1464
|
+
const meta = {
|
|
1465
|
+
...toolMeta(chunk.name, void 0, ctx.parentToolCallId)
|
|
1466
|
+
};
|
|
1467
|
+
if (chunk.name === "Bash" && ctx.supportsTerminalOutput && !alreadyCached) {
|
|
1468
|
+
meta.terminal_info = { terminal_id: chunk.id };
|
|
1469
|
+
}
|
|
1470
|
+
if (alreadyCached) {
|
|
1471
|
+
return {
|
|
1472
|
+
_meta: meta,
|
|
1473
|
+
toolCallId: chunk.id,
|
|
1474
|
+
sessionUpdate: "tool_call_update",
|
|
1475
|
+
rawInput,
|
|
1476
|
+
...toolInfo
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1404
1479
|
return {
|
|
1405
|
-
_meta:
|
|
1480
|
+
_meta: meta,
|
|
1406
1481
|
toolCallId: chunk.id,
|
|
1407
1482
|
sessionUpdate: "tool_call",
|
|
1408
1483
|
rawInput,
|
|
1409
1484
|
status: "pending",
|
|
1410
|
-
...
|
|
1485
|
+
...toolInfo
|
|
1411
1486
|
};
|
|
1412
1487
|
}
|
|
1413
1488
|
function handleToolResultChunk(chunk, ctx) {
|
|
@@ -1416,36 +1491,71 @@ function handleToolResultChunk(chunk, ctx) {
|
|
|
1416
1491
|
ctx.logger.error(
|
|
1417
1492
|
`Got a tool result for tool use that wasn't tracked: ${chunk.tool_use_id}`
|
|
1418
1493
|
);
|
|
1419
|
-
return
|
|
1494
|
+
return [];
|
|
1420
1495
|
}
|
|
1421
1496
|
if (toolUse.name === "TodoWrite") {
|
|
1422
|
-
return
|
|
1497
|
+
return [];
|
|
1423
1498
|
}
|
|
1424
|
-
|
|
1425
|
-
|
|
1499
|
+
const { _meta: resultMeta, ...toolUpdate } = toolUpdateFromToolResult(
|
|
1500
|
+
chunk,
|
|
1501
|
+
toolUse,
|
|
1502
|
+
{
|
|
1503
|
+
supportsTerminalOutput: ctx.supportsTerminalOutput,
|
|
1504
|
+
toolUseId: chunk.tool_use_id
|
|
1505
|
+
}
|
|
1506
|
+
);
|
|
1507
|
+
const updates = [];
|
|
1508
|
+
if (resultMeta?.terminal_output) {
|
|
1509
|
+
const terminalOutputMeta = {
|
|
1510
|
+
terminal_output: resultMeta.terminal_output
|
|
1511
|
+
};
|
|
1512
|
+
if (ctx.parentToolCallId) {
|
|
1513
|
+
terminalOutputMeta.claudeCode = {
|
|
1514
|
+
parentToolCallId: ctx.parentToolCallId
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
updates.push({
|
|
1518
|
+
_meta: terminalOutputMeta,
|
|
1519
|
+
toolCallId: chunk.tool_use_id,
|
|
1520
|
+
sessionUpdate: "tool_call_update"
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
const meta = {
|
|
1524
|
+
...toolMeta(toolUse.name, void 0, ctx.parentToolCallId),
|
|
1525
|
+
...resultMeta?.terminal_exit ? { terminal_exit: resultMeta.terminal_exit } : {}
|
|
1526
|
+
};
|
|
1527
|
+
updates.push({
|
|
1528
|
+
_meta: meta,
|
|
1426
1529
|
toolCallId: chunk.tool_use_id,
|
|
1427
1530
|
sessionUpdate: "tool_call_update",
|
|
1428
1531
|
status: chunk.is_error ? "failed" : "completed",
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
};
|
|
1532
|
+
rawOutput: chunk.content,
|
|
1533
|
+
...toolUpdate
|
|
1534
|
+
});
|
|
1535
|
+
return updates;
|
|
1434
1536
|
}
|
|
1435
1537
|
function processContentChunk(chunk, role, ctx) {
|
|
1436
1538
|
switch (chunk.type) {
|
|
1437
1539
|
case "text":
|
|
1438
|
-
case "text_delta":
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1540
|
+
case "text_delta": {
|
|
1541
|
+
const update = handleTextChunk(chunk, role, ctx.parentToolCallId);
|
|
1542
|
+
return update ? [update] : [];
|
|
1543
|
+
}
|
|
1544
|
+
case "image": {
|
|
1545
|
+
const update = handleImageChunk(chunk, role);
|
|
1546
|
+
return update ? [update] : [];
|
|
1547
|
+
}
|
|
1442
1548
|
case "thinking":
|
|
1443
|
-
case "thinking_delta":
|
|
1444
|
-
|
|
1549
|
+
case "thinking_delta": {
|
|
1550
|
+
const update = handleThinkingChunk(chunk, ctx.parentToolCallId);
|
|
1551
|
+
return update ? [update] : [];
|
|
1552
|
+
}
|
|
1445
1553
|
case "tool_use":
|
|
1446
1554
|
case "server_tool_use":
|
|
1447
|
-
case "mcp_tool_use":
|
|
1448
|
-
|
|
1555
|
+
case "mcp_tool_use": {
|
|
1556
|
+
const update = handleToolUseChunk(chunk, ctx);
|
|
1557
|
+
return update ? [update] : [];
|
|
1558
|
+
}
|
|
1449
1559
|
case "tool_result":
|
|
1450
1560
|
case "tool_search_tool_result":
|
|
1451
1561
|
case "web_fetch_tool_result":
|
|
@@ -1467,13 +1577,13 @@ function processContentChunk(chunk, role, ctx) {
|
|
|
1467
1577
|
case "container_upload":
|
|
1468
1578
|
case "compaction":
|
|
1469
1579
|
case "compaction_delta":
|
|
1470
|
-
return
|
|
1580
|
+
return [];
|
|
1471
1581
|
default:
|
|
1472
1582
|
unreachable(chunk, ctx.logger);
|
|
1473
|
-
return
|
|
1583
|
+
return [];
|
|
1474
1584
|
}
|
|
1475
1585
|
}
|
|
1476
|
-
function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId) {
|
|
1586
|
+
function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput) {
|
|
1477
1587
|
if (typeof content === "string") {
|
|
1478
1588
|
const update = {
|
|
1479
1589
|
sessionUpdate: messageUpdateType(role),
|
|
@@ -1494,18 +1604,19 @@ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentC
|
|
|
1494
1604
|
fileContentCache,
|
|
1495
1605
|
client,
|
|
1496
1606
|
logger,
|
|
1497
|
-
parentToolCallId
|
|
1607
|
+
parentToolCallId,
|
|
1608
|
+
registerHooks,
|
|
1609
|
+
supportsTerminalOutput
|
|
1498
1610
|
};
|
|
1499
1611
|
const output = [];
|
|
1500
1612
|
for (const chunk of content) {
|
|
1501
|
-
const update
|
|
1502
|
-
if (update) {
|
|
1613
|
+
for (const update of processContentChunk(chunk, role, ctx)) {
|
|
1503
1614
|
output.push({ sessionId, update });
|
|
1504
1615
|
}
|
|
1505
1616
|
}
|
|
1506
1617
|
return output;
|
|
1507
1618
|
}
|
|
1508
|
-
function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId) {
|
|
1619
|
+
function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput) {
|
|
1509
1620
|
const event = message.event;
|
|
1510
1621
|
switch (event.type) {
|
|
1511
1622
|
case "content_block_start":
|
|
@@ -1517,7 +1628,9 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
|
|
|
1517
1628
|
fileContentCache,
|
|
1518
1629
|
client,
|
|
1519
1630
|
logger,
|
|
1520
|
-
parentToolCallId
|
|
1631
|
+
parentToolCallId,
|
|
1632
|
+
registerHooks,
|
|
1633
|
+
supportsTerminalOutput
|
|
1521
1634
|
);
|
|
1522
1635
|
case "content_block_delta":
|
|
1523
1636
|
return toAcpNotifications(
|
|
@@ -1528,7 +1641,9 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
|
|
|
1528
1641
|
fileContentCache,
|
|
1529
1642
|
client,
|
|
1530
1643
|
logger,
|
|
1531
|
-
parentToolCallId
|
|
1644
|
+
parentToolCallId,
|
|
1645
|
+
registerHooks,
|
|
1646
|
+
supportsTerminalOutput
|
|
1532
1647
|
);
|
|
1533
1648
|
case "message_start":
|
|
1534
1649
|
case "message_delta":
|
|
@@ -1587,29 +1702,25 @@ async function handleSystemMessage(message, context) {
|
|
|
1587
1702
|
break;
|
|
1588
1703
|
}
|
|
1589
1704
|
}
|
|
1590
|
-
function handleResultMessage(message
|
|
1591
|
-
const
|
|
1592
|
-
if (session.cancelled) {
|
|
1593
|
-
return {
|
|
1594
|
-
shouldStop: true,
|
|
1595
|
-
stopReason: "cancelled"
|
|
1596
|
-
};
|
|
1597
|
-
}
|
|
1705
|
+
function handleResultMessage(message) {
|
|
1706
|
+
const usage = extractUsageFromResult(message);
|
|
1598
1707
|
switch (message.subtype) {
|
|
1599
1708
|
case "success": {
|
|
1600
1709
|
if (message.result.includes("Please run /login")) {
|
|
1601
1710
|
return {
|
|
1602
1711
|
shouldStop: true,
|
|
1603
|
-
error: RequestError.authRequired()
|
|
1712
|
+
error: RequestError.authRequired(),
|
|
1713
|
+
usage
|
|
1604
1714
|
};
|
|
1605
1715
|
}
|
|
1606
1716
|
if (message.is_error) {
|
|
1607
1717
|
return {
|
|
1608
1718
|
shouldStop: true,
|
|
1609
|
-
error: RequestError.internalError(void 0, message.result)
|
|
1719
|
+
error: RequestError.internalError(void 0, message.result),
|
|
1720
|
+
usage
|
|
1610
1721
|
};
|
|
1611
1722
|
}
|
|
1612
|
-
return { shouldStop: true, stopReason: "end_turn" };
|
|
1723
|
+
return { shouldStop: true, stopReason: "end_turn", usage };
|
|
1613
1724
|
}
|
|
1614
1725
|
case "error_during_execution":
|
|
1615
1726
|
if (message.is_error) {
|
|
@@ -1618,10 +1729,11 @@ function handleResultMessage(message, context) {
|
|
|
1618
1729
|
error: RequestError.internalError(
|
|
1619
1730
|
void 0,
|
|
1620
1731
|
message.errors.join(", ") || message.subtype
|
|
1621
|
-
)
|
|
1732
|
+
),
|
|
1733
|
+
usage
|
|
1622
1734
|
};
|
|
1623
1735
|
}
|
|
1624
|
-
return { shouldStop: true, stopReason: "end_turn" };
|
|
1736
|
+
return { shouldStop: true, stopReason: "end_turn", usage };
|
|
1625
1737
|
case "error_max_budget_usd":
|
|
1626
1738
|
case "error_max_turns":
|
|
1627
1739
|
case "error_max_structured_output_retries":
|
|
@@ -1631,13 +1743,37 @@ function handleResultMessage(message, context) {
|
|
|
1631
1743
|
error: RequestError.internalError(
|
|
1632
1744
|
void 0,
|
|
1633
1745
|
message.errors.join(", ") || message.subtype
|
|
1634
|
-
)
|
|
1746
|
+
),
|
|
1747
|
+
usage
|
|
1635
1748
|
};
|
|
1636
1749
|
}
|
|
1637
|
-
return { shouldStop: true, stopReason: "max_turn_requests" };
|
|
1750
|
+
return { shouldStop: true, stopReason: "max_turn_requests", usage };
|
|
1638
1751
|
default:
|
|
1639
|
-
return { shouldStop: false };
|
|
1752
|
+
return { shouldStop: false, usage };
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
function extractUsageFromResult(message) {
|
|
1756
|
+
const msg = message;
|
|
1757
|
+
const msgUsage = msg.usage;
|
|
1758
|
+
if (!msgUsage) return void 0;
|
|
1759
|
+
const modelUsage = msg.modelUsage;
|
|
1760
|
+
let contextWindowSize;
|
|
1761
|
+
if (modelUsage) {
|
|
1762
|
+
const contextWindows = Object.values(modelUsage).map(
|
|
1763
|
+
(m) => m.contextWindow
|
|
1764
|
+
);
|
|
1765
|
+
if (contextWindows.length > 0) {
|
|
1766
|
+
contextWindowSize = Math.min(...contextWindows);
|
|
1767
|
+
}
|
|
1640
1768
|
}
|
|
1769
|
+
return {
|
|
1770
|
+
inputTokens: msgUsage.input_tokens ?? 0,
|
|
1771
|
+
outputTokens: msgUsage.output_tokens ?? 0,
|
|
1772
|
+
cachedReadTokens: msgUsage.cache_read_input_tokens ?? 0,
|
|
1773
|
+
cachedWriteTokens: msgUsage.cache_creation_input_tokens ?? 0,
|
|
1774
|
+
costUsd: typeof msg.total_cost_usd === "number" ? msg.total_cost_usd : void 0,
|
|
1775
|
+
contextWindowSize
|
|
1776
|
+
};
|
|
1641
1777
|
}
|
|
1642
1778
|
async function handleStreamEvent(message, context) {
|
|
1643
1779
|
const { sessionId, client, toolUseCache, fileContentCache, logger } = context;
|
|
@@ -1649,7 +1785,9 @@ async function handleStreamEvent(message, context) {
|
|
|
1649
1785
|
fileContentCache,
|
|
1650
1786
|
client,
|
|
1651
1787
|
logger,
|
|
1652
|
-
parentToolCallId
|
|
1788
|
+
parentToolCallId,
|
|
1789
|
+
context.registerHooks,
|
|
1790
|
+
context.supportsTerminalOutput
|
|
1653
1791
|
)) {
|
|
1654
1792
|
await client.sessionUpdate(notification);
|
|
1655
1793
|
context.session.notificationHistory.push(notification);
|
|
@@ -1661,14 +1799,15 @@ function hasLocalCommandStdout(content) {
|
|
|
1661
1799
|
function hasLocalCommandStderr(content) {
|
|
1662
1800
|
return typeof content === "string" && content.includes("<local-command-stderr>");
|
|
1663
1801
|
}
|
|
1664
|
-
function isSimpleUserMessage(message) {
|
|
1665
|
-
return message.type === "user" && (typeof message.message.content === "string" || Array.isArray(message.message.content) && message.message.content.length === 1 && message.message.content[0].type === "text");
|
|
1666
|
-
}
|
|
1667
1802
|
function isLoginRequiredMessage(message) {
|
|
1668
1803
|
return message.type === "assistant" && message.message.model === "<synthetic>" && Array.isArray(message.message.content) && message.message.content.length === 1 && message.message.content[0].type === "text" && message.message.content[0].text?.includes("Please run /login") === true;
|
|
1669
1804
|
}
|
|
1805
|
+
function isPlainTextUserMessage(message) {
|
|
1806
|
+
const content = message.message.content;
|
|
1807
|
+
return message.type === "user" && (typeof content === "string" || Array.isArray(content) && content.length === 1 && content[0].type === "text");
|
|
1808
|
+
}
|
|
1670
1809
|
function shouldSkipUserAssistantMessage(message) {
|
|
1671
|
-
return hasLocalCommandStdout(message.message.content) || hasLocalCommandStderr(message.message.content) ||
|
|
1810
|
+
return hasLocalCommandStdout(message.message.content) || hasLocalCommandStderr(message.message.content) || isLoginRequiredMessage(message);
|
|
1672
1811
|
}
|
|
1673
1812
|
function logSpecialMessages(message, logger) {
|
|
1674
1813
|
const content = message.message.content;
|
|
@@ -1689,18 +1828,33 @@ function filterMessageContent(content) {
|
|
|
1689
1828
|
}
|
|
1690
1829
|
async function handleUserAssistantMessage(message, context) {
|
|
1691
1830
|
const { session, sessionId, client, toolUseCache, fileContentCache, logger } = context;
|
|
1692
|
-
if (session.cancelled) {
|
|
1693
|
-
return {};
|
|
1694
|
-
}
|
|
1695
1831
|
if (shouldSkipUserAssistantMessage(message)) {
|
|
1832
|
+
const content2 = message.message.content;
|
|
1833
|
+
if (typeof content2 === "string" && hasLocalCommandStdout(content2) && content2.includes("Context Usage")) {
|
|
1834
|
+
const stripped = content2.replace("<local-command-stdout>", "").replace("</local-command-stdout>", "");
|
|
1835
|
+
for (const notification of toAcpNotifications(
|
|
1836
|
+
stripped,
|
|
1837
|
+
"assistant",
|
|
1838
|
+
sessionId,
|
|
1839
|
+
toolUseCache,
|
|
1840
|
+
fileContentCache,
|
|
1841
|
+
client,
|
|
1842
|
+
logger
|
|
1843
|
+
)) {
|
|
1844
|
+
await client.sessionUpdate(notification);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1696
1847
|
logSpecialMessages(message, logger);
|
|
1697
1848
|
if (isLoginRequiredMessage(message)) {
|
|
1698
1849
|
return { shouldStop: true, error: RequestError.authRequired() };
|
|
1699
1850
|
}
|
|
1700
1851
|
return {};
|
|
1701
1852
|
}
|
|
1853
|
+
if (isPlainTextUserMessage(message)) {
|
|
1854
|
+
return {};
|
|
1855
|
+
}
|
|
1702
1856
|
const content = message.message.content;
|
|
1703
|
-
const contentToProcess = filterMessageContent(content);
|
|
1857
|
+
const contentToProcess = message.type === "assistant" ? filterMessageContent(content) : content;
|
|
1704
1858
|
const parentToolCallId = "parent_tool_use_id" in message ? message.parent_tool_use_id ?? void 0 : void 0;
|
|
1705
1859
|
for (const notification of toAcpNotifications(
|
|
1706
1860
|
contentToProcess,
|
|
@@ -1710,7 +1864,9 @@ async function handleUserAssistantMessage(message, context) {
|
|
|
1710
1864
|
fileContentCache,
|
|
1711
1865
|
client,
|
|
1712
1866
|
logger,
|
|
1713
|
-
parentToolCallId
|
|
1867
|
+
parentToolCallId,
|
|
1868
|
+
context.registerHooks,
|
|
1869
|
+
context.supportsTerminalOutput
|
|
1714
1870
|
)) {
|
|
1715
1871
|
await client.sessionUpdate(notification);
|
|
1716
1872
|
session.notificationHistory.push(notification);
|
|
@@ -1796,36 +1952,45 @@ function normalizeAskUserQuestionInput(input) {
|
|
|
1796
1952
|
}
|
|
1797
1953
|
|
|
1798
1954
|
// src/execution-mode.ts
|
|
1799
|
-
var
|
|
1955
|
+
var ALLOW_BYPASS = !IS_ROOT;
|
|
1956
|
+
var availableModes = [
|
|
1800
1957
|
{
|
|
1801
1958
|
id: "default",
|
|
1802
|
-
name: "
|
|
1803
|
-
description: "
|
|
1959
|
+
name: "Default",
|
|
1960
|
+
description: "Standard behavior, prompts for dangerous operations"
|
|
1804
1961
|
},
|
|
1805
1962
|
{
|
|
1806
1963
|
id: "acceptEdits",
|
|
1807
1964
|
name: "Accept Edits",
|
|
1808
|
-
description: "
|
|
1965
|
+
description: "Auto-accept file edit operations"
|
|
1809
1966
|
},
|
|
1810
1967
|
{
|
|
1811
1968
|
id: "plan",
|
|
1812
1969
|
name: "Plan Mode",
|
|
1813
|
-
description: "
|
|
1814
|
-
},
|
|
1815
|
-
{
|
|
1816
|
-
id: "bypassPermissions",
|
|
1817
|
-
name: "Bypass Permissions",
|
|
1818
|
-
description: "Skips all permission prompts"
|
|
1970
|
+
description: "Planning mode, no actual tool execution"
|
|
1819
1971
|
}
|
|
1972
|
+
// {
|
|
1973
|
+
// id: "dontAsk",
|
|
1974
|
+
// name: "Don't Ask",
|
|
1975
|
+
// description: "Don't prompt for permissions, deny if not pre-approved",
|
|
1976
|
+
// },
|
|
1820
1977
|
];
|
|
1978
|
+
if (ALLOW_BYPASS) {
|
|
1979
|
+
availableModes.push({
|
|
1980
|
+
id: "bypassPermissions",
|
|
1981
|
+
name: "Bypass Permissions",
|
|
1982
|
+
description: "Bypass all permission checks"
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1821
1985
|
var TWIG_EXECUTION_MODES = [
|
|
1822
1986
|
"default",
|
|
1823
1987
|
"acceptEdits",
|
|
1824
1988
|
"plan",
|
|
1989
|
+
// "dontAsk",
|
|
1825
1990
|
"bypassPermissions"
|
|
1826
1991
|
];
|
|
1827
1992
|
function getAvailableModes() {
|
|
1828
|
-
return IS_ROOT ?
|
|
1993
|
+
return IS_ROOT ? availableModes.filter((m) => m.id !== "bypassPermissions") : availableModes;
|
|
1829
1994
|
}
|
|
1830
1995
|
|
|
1831
1996
|
// src/adapters/claude/tools.ts
|
|
@@ -1853,6 +2018,7 @@ var AUTO_ALLOWED_TOOLS = {
|
|
|
1853
2018
|
default: new Set(BASE_ALLOWED_TOOLS),
|
|
1854
2019
|
acceptEdits: /* @__PURE__ */ new Set([...BASE_ALLOWED_TOOLS, ...WRITE_TOOLS]),
|
|
1855
2020
|
plan: new Set(BASE_ALLOWED_TOOLS)
|
|
2021
|
+
// dontAsk: new Set(BASE_ALLOWED_TOOLS),
|
|
1856
2022
|
};
|
|
1857
2023
|
function isToolAllowedForMode(toolName, mode) {
|
|
1858
2024
|
if (mode === "bypassPermissions") {
|
|
@@ -2000,12 +2166,11 @@ async function validatePlanContent(planText, context) {
|
|
|
2000
2166
|
return { valid: true };
|
|
2001
2167
|
}
|
|
2002
2168
|
async function requestPlanApproval(context, updatedInput) {
|
|
2003
|
-
const { client, sessionId, toolUseID
|
|
2004
|
-
const toolInfo = toolInfoFromToolUse(
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
);
|
|
2169
|
+
const { client, sessionId, toolUseID } = context;
|
|
2170
|
+
const toolInfo = toolInfoFromToolUse({
|
|
2171
|
+
name: context.toolName,
|
|
2172
|
+
input: updatedInput
|
|
2173
|
+
});
|
|
2009
2174
|
return await client.requestPermission({
|
|
2010
2175
|
options: buildExitPlanModePermissionOptions(),
|
|
2011
2176
|
sessionId,
|
|
@@ -2024,7 +2189,14 @@ async function applyPlanApproval(response, context, updatedInput) {
|
|
|
2024
2189
|
if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "default" || response.outcome.optionId === "acceptEdits")) {
|
|
2025
2190
|
session.permissionMode = response.outcome.optionId;
|
|
2026
2191
|
await session.query.setPermissionMode(response.outcome.optionId);
|
|
2027
|
-
await context.
|
|
2192
|
+
await context.client.sessionUpdate({
|
|
2193
|
+
sessionId: context.sessionId,
|
|
2194
|
+
update: {
|
|
2195
|
+
sessionUpdate: "current_mode_update",
|
|
2196
|
+
currentModeId: response.outcome.optionId
|
|
2197
|
+
}
|
|
2198
|
+
});
|
|
2199
|
+
await context.updateConfigOption("mode", response.outcome.optionId);
|
|
2028
2200
|
return {
|
|
2029
2201
|
behavior: "allow",
|
|
2030
2202
|
updatedInput,
|
|
@@ -2045,7 +2217,7 @@ async function handleEnterPlanModeTool(context) {
|
|
|
2045
2217
|
const { session, toolInput } = context;
|
|
2046
2218
|
session.permissionMode = "plan";
|
|
2047
2219
|
await session.query.setPermissionMode("plan");
|
|
2048
|
-
await context.
|
|
2220
|
+
await context.updateConfigOption("mode", "plan");
|
|
2049
2221
|
return {
|
|
2050
2222
|
behavior: "allow",
|
|
2051
2223
|
updatedInput: toolInput
|
|
@@ -2063,6 +2235,9 @@ async function handleExitPlanModeTool(context) {
|
|
|
2063
2235
|
return validationResult.error;
|
|
2064
2236
|
}
|
|
2065
2237
|
const response = await requestPlanApproval(context, updatedInput);
|
|
2238
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
2239
|
+
throw new Error("Tool use aborted");
|
|
2240
|
+
}
|
|
2066
2241
|
return await applyPlanApproval(response, context, updatedInput);
|
|
2067
2242
|
}
|
|
2068
2243
|
function buildQuestionOptions(question) {
|
|
@@ -2086,14 +2261,13 @@ async function handleAskUserQuestionTool(context) {
|
|
|
2086
2261
|
interrupt: true
|
|
2087
2262
|
};
|
|
2088
2263
|
}
|
|
2089
|
-
const { client, sessionId, toolUseID, toolInput
|
|
2264
|
+
const { client, sessionId, toolUseID, toolInput } = context;
|
|
2090
2265
|
const firstQuestion = questions[0];
|
|
2091
2266
|
const options = buildQuestionOptions(firstQuestion);
|
|
2092
|
-
const toolInfo = toolInfoFromToolUse(
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
);
|
|
2267
|
+
const toolInfo = toolInfoFromToolUse({
|
|
2268
|
+
name: context.toolName,
|
|
2269
|
+
input: toolInput
|
|
2270
|
+
});
|
|
2097
2271
|
const response = await client.requestPermission({
|
|
2098
2272
|
options,
|
|
2099
2273
|
sessionId,
|
|
@@ -2108,6 +2282,9 @@ async function handleAskUserQuestionTool(context) {
|
|
|
2108
2282
|
}
|
|
2109
2283
|
}
|
|
2110
2284
|
});
|
|
2285
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
2286
|
+
throw new Error("Tool use aborted");
|
|
2287
|
+
}
|
|
2111
2288
|
if (response.outcome?.outcome !== "selected") {
|
|
2112
2289
|
const customMessage = response._meta?.message;
|
|
2113
2290
|
return {
|
|
@@ -2140,14 +2317,9 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
2140
2317
|
toolUseID,
|
|
2141
2318
|
client,
|
|
2142
2319
|
sessionId,
|
|
2143
|
-
fileContentCache,
|
|
2144
2320
|
suggestions
|
|
2145
2321
|
} = context;
|
|
2146
|
-
const toolInfo = toolInfoFromToolUse(
|
|
2147
|
-
{ name: toolName, input: toolInput },
|
|
2148
|
-
fileContentCache,
|
|
2149
|
-
context.logger
|
|
2150
|
-
);
|
|
2322
|
+
const toolInfo = toolInfoFromToolUse({ name: toolName, input: toolInput });
|
|
2151
2323
|
const options = buildPermissionOptions(
|
|
2152
2324
|
toolName,
|
|
2153
2325
|
toolInput,
|
|
@@ -2166,6 +2338,9 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
2166
2338
|
rawInput: toolInput
|
|
2167
2339
|
}
|
|
2168
2340
|
});
|
|
2341
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
2342
|
+
throw new Error("Tool use aborted");
|
|
2343
|
+
}
|
|
2169
2344
|
if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "allow" || response.outcome.optionId === "allow_always")) {
|
|
2170
2345
|
if (response.outcome.optionId === "allow_always") {
|
|
2171
2346
|
return {
|
|
@@ -2242,16 +2417,18 @@ async function canUseTool(context) {
|
|
|
2242
2417
|
var UNSUPPORTED_COMMANDS = [
|
|
2243
2418
|
"context",
|
|
2244
2419
|
"cost",
|
|
2420
|
+
"keybindings-help",
|
|
2245
2421
|
"login",
|
|
2246
2422
|
"logout",
|
|
2247
2423
|
"output-style:new",
|
|
2248
2424
|
"release-notes",
|
|
2249
2425
|
"todos"
|
|
2250
2426
|
];
|
|
2251
|
-
|
|
2252
|
-
const commands = await q.supportedCommands();
|
|
2427
|
+
function getAvailableSlashCommands(commands) {
|
|
2253
2428
|
return commands.map((command) => {
|
|
2254
|
-
const input = command.argumentHint ? {
|
|
2429
|
+
const input = command.argumentHint != null ? {
|
|
2430
|
+
hint: Array.isArray(command.argumentHint) ? command.argumentHint.join(" ") : command.argumentHint
|
|
2431
|
+
} : null;
|
|
2255
2432
|
let name = command.name;
|
|
2256
2433
|
if (command.name.endsWith(" (MCP)")) {
|
|
2257
2434
|
name = `mcp:${name.replace(" (MCP)", "")}`;
|
|
@@ -2349,13 +2526,19 @@ function buildEnvironment() {
|
|
|
2349
2526
|
ENABLE_TOOL_SEARCH: "auto:0"
|
|
2350
2527
|
};
|
|
2351
2528
|
}
|
|
2352
|
-
function buildHooks(userHooks, onModeChange) {
|
|
2529
|
+
function buildHooks(userHooks, onModeChange, settingsManager, logger) {
|
|
2353
2530
|
return {
|
|
2354
2531
|
...userHooks,
|
|
2355
2532
|
PostToolUse: [
|
|
2356
2533
|
...userHooks?.PostToolUse || [],
|
|
2357
2534
|
{
|
|
2358
|
-
hooks: [createPostToolUseHook({ onModeChange })]
|
|
2535
|
+
hooks: [createPostToolUseHook({ onModeChange, logger })]
|
|
2536
|
+
}
|
|
2537
|
+
],
|
|
2538
|
+
PreToolUse: [
|
|
2539
|
+
...userHooks?.PreToolUse || [],
|
|
2540
|
+
{
|
|
2541
|
+
hooks: [createPreToolUseHook(settingsManager, logger)]
|
|
2359
2542
|
}
|
|
2360
2543
|
]
|
|
2361
2544
|
};
|
|
@@ -2449,12 +2632,22 @@ function buildSessionOptions(params) {
|
|
|
2449
2632
|
permissionMode: params.permissionMode,
|
|
2450
2633
|
canUseTool: params.canUseTool,
|
|
2451
2634
|
executable: "node",
|
|
2635
|
+
tools: { type: "preset", preset: "claude_code" },
|
|
2636
|
+
extraArgs: {
|
|
2637
|
+
...params.userProvidedOptions?.extraArgs,
|
|
2638
|
+
"replay-user-messages": ""
|
|
2639
|
+
},
|
|
2452
2640
|
mcpServers: buildMcpServers(
|
|
2453
2641
|
params.userProvidedOptions?.mcpServers,
|
|
2454
2642
|
params.mcpServers
|
|
2455
2643
|
),
|
|
2456
2644
|
env: buildEnvironment(),
|
|
2457
|
-
hooks: buildHooks(
|
|
2645
|
+
hooks: buildHooks(
|
|
2646
|
+
params.userProvidedOptions?.hooks,
|
|
2647
|
+
params.onModeChange,
|
|
2648
|
+
params.settingsManager,
|
|
2649
|
+
params.logger
|
|
2650
|
+
),
|
|
2458
2651
|
abortController: getAbortController(
|
|
2459
2652
|
params.userProvidedOptions?.abortController
|
|
2460
2653
|
),
|
|
@@ -2471,13 +2664,36 @@ function buildSessionOptions(params) {
|
|
|
2471
2664
|
}
|
|
2472
2665
|
if (params.isResume) {
|
|
2473
2666
|
options.resume = params.sessionId;
|
|
2474
|
-
options.forkSession = false;
|
|
2667
|
+
options.forkSession = params.forkSession ?? false;
|
|
2475
2668
|
} else {
|
|
2476
2669
|
options.sessionId = params.sessionId;
|
|
2670
|
+
options.model = DEFAULT_MODEL;
|
|
2477
2671
|
}
|
|
2478
2672
|
if (params.additionalDirectories) {
|
|
2479
2673
|
options.additionalDirectories = params.additionalDirectories;
|
|
2480
2674
|
}
|
|
2675
|
+
if (params.disableBuiltInTools) {
|
|
2676
|
+
const builtInTools = [
|
|
2677
|
+
"Read",
|
|
2678
|
+
"Write",
|
|
2679
|
+
"Edit",
|
|
2680
|
+
"Bash",
|
|
2681
|
+
"Glob",
|
|
2682
|
+
"Grep",
|
|
2683
|
+
"Task",
|
|
2684
|
+
"TodoWrite",
|
|
2685
|
+
"ExitPlanMode",
|
|
2686
|
+
"WebSearch",
|
|
2687
|
+
"WebFetch",
|
|
2688
|
+
"SlashCommand",
|
|
2689
|
+
"Skill",
|
|
2690
|
+
"NotebookEdit"
|
|
2691
|
+
];
|
|
2692
|
+
options.disallowedTools = [
|
|
2693
|
+
...options.disallowedTools ?? [],
|
|
2694
|
+
...builtInTools
|
|
2695
|
+
];
|
|
2696
|
+
}
|
|
2481
2697
|
clearStatsigCache();
|
|
2482
2698
|
return options;
|
|
2483
2699
|
}
|
|
@@ -2490,154 +2706,698 @@ function clearStatsigCache() {
|
|
|
2490
2706
|
});
|
|
2491
2707
|
}
|
|
2492
2708
|
|
|
2493
|
-
// src/adapters/claude/
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2709
|
+
// src/adapters/claude/session/settings.ts
|
|
2710
|
+
import * as fs2 from "fs";
|
|
2711
|
+
import * as os3 from "os";
|
|
2712
|
+
import * as path3 from "path";
|
|
2713
|
+
import { minimatch } from "minimatch";
|
|
2714
|
+
var ACP_TOOL_NAME_PREFIX = "mcp__acp__";
|
|
2715
|
+
var acpToolNames = {
|
|
2716
|
+
read: `${ACP_TOOL_NAME_PREFIX}Read`,
|
|
2717
|
+
edit: `${ACP_TOOL_NAME_PREFIX}Edit`,
|
|
2718
|
+
write: `${ACP_TOOL_NAME_PREFIX}Write`,
|
|
2719
|
+
bash: `${ACP_TOOL_NAME_PREFIX}Bash`
|
|
2720
|
+
};
|
|
2721
|
+
var SHELL_OPERATORS = ["&&", "||", ";", "|", "$(", "`", "\n"];
|
|
2722
|
+
function containsShellOperator(str) {
|
|
2723
|
+
return SHELL_OPERATORS.some((op) => str.includes(op));
|
|
2724
|
+
}
|
|
2725
|
+
var FILE_EDITING_TOOLS = [acpToolNames.edit, acpToolNames.write];
|
|
2726
|
+
var FILE_READING_TOOLS = [acpToolNames.read];
|
|
2727
|
+
var TOOL_ARG_ACCESSORS = {
|
|
2728
|
+
[acpToolNames.read]: (input) => input?.file_path,
|
|
2729
|
+
[acpToolNames.edit]: (input) => input?.file_path,
|
|
2730
|
+
[acpToolNames.write]: (input) => input?.file_path,
|
|
2731
|
+
[acpToolNames.bash]: (input) => input?.command
|
|
2732
|
+
};
|
|
2733
|
+
function parseRule(rule) {
|
|
2734
|
+
const match = rule.match(/^(\w+)(?:\((.+)\))?$/);
|
|
2735
|
+
if (!match) {
|
|
2736
|
+
return { toolName: rule };
|
|
2737
|
+
}
|
|
2738
|
+
const toolName = match[1] ?? rule;
|
|
2739
|
+
const argument = match[2];
|
|
2740
|
+
if (argument?.endsWith(":*")) {
|
|
2510
2741
|
return {
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
image: true,
|
|
2515
|
-
embeddedContext: true
|
|
2516
|
-
},
|
|
2517
|
-
mcpCapabilities: {
|
|
2518
|
-
http: true,
|
|
2519
|
-
sse: true
|
|
2520
|
-
},
|
|
2521
|
-
loadSession: true,
|
|
2522
|
-
_meta: {
|
|
2523
|
-
posthog: {
|
|
2524
|
-
resumeSession: true
|
|
2525
|
-
}
|
|
2526
|
-
}
|
|
2527
|
-
},
|
|
2528
|
-
agentInfo: {
|
|
2529
|
-
name: package_default.name,
|
|
2530
|
-
title: "Claude Code",
|
|
2531
|
-
version: package_default.version
|
|
2532
|
-
},
|
|
2533
|
-
authMethods: [
|
|
2534
|
-
{
|
|
2535
|
-
id: "claude-login",
|
|
2536
|
-
name: "Log in with Claude Code",
|
|
2537
|
-
description: "Run `claude /login` in the terminal"
|
|
2538
|
-
}
|
|
2539
|
-
]
|
|
2742
|
+
toolName,
|
|
2743
|
+
argument: argument.slice(0, -2),
|
|
2744
|
+
isWildcard: true
|
|
2540
2745
|
};
|
|
2541
2746
|
}
|
|
2542
|
-
|
|
2543
|
-
|
|
2747
|
+
return { toolName, argument };
|
|
2748
|
+
}
|
|
2749
|
+
function normalizePath(filePath, cwd) {
|
|
2750
|
+
let resolved = filePath;
|
|
2751
|
+
if (resolved.startsWith("~/")) {
|
|
2752
|
+
resolved = path3.join(os3.homedir(), resolved.slice(2));
|
|
2753
|
+
} else if (resolved.startsWith("./")) {
|
|
2754
|
+
resolved = path3.join(cwd, resolved.slice(2));
|
|
2755
|
+
} else if (!path3.isAbsolute(resolved)) {
|
|
2756
|
+
resolved = path3.join(cwd, resolved);
|
|
2757
|
+
}
|
|
2758
|
+
return path3.normalize(resolved).replace(/\\/g, "/");
|
|
2759
|
+
}
|
|
2760
|
+
function matchesGlob(pattern, filePath, cwd) {
|
|
2761
|
+
const normalizedPattern = normalizePath(pattern, cwd);
|
|
2762
|
+
const normalizedPath = normalizePath(filePath, cwd);
|
|
2763
|
+
return minimatch(normalizedPath, normalizedPattern, {
|
|
2764
|
+
dot: true,
|
|
2765
|
+
matchBase: false,
|
|
2766
|
+
nocase: process.platform === "win32"
|
|
2767
|
+
});
|
|
2768
|
+
}
|
|
2769
|
+
function matchesRule(rule, toolName, toolInput, cwd) {
|
|
2770
|
+
const ruleAppliesToTool = rule.toolName === "Bash" && toolName === acpToolNames.bash || rule.toolName === "Edit" && FILE_EDITING_TOOLS.includes(toolName) || rule.toolName === "Read" && FILE_READING_TOOLS.includes(toolName);
|
|
2771
|
+
if (!ruleAppliesToTool) {
|
|
2772
|
+
return false;
|
|
2544
2773
|
}
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2774
|
+
if (!rule.argument) {
|
|
2775
|
+
return true;
|
|
2776
|
+
}
|
|
2777
|
+
const argAccessor = TOOL_ARG_ACCESSORS[toolName];
|
|
2778
|
+
if (!argAccessor) {
|
|
2779
|
+
return true;
|
|
2780
|
+
}
|
|
2781
|
+
const actualArg = argAccessor(toolInput);
|
|
2782
|
+
if (!actualArg) {
|
|
2783
|
+
return false;
|
|
2784
|
+
}
|
|
2785
|
+
if (toolName === acpToolNames.bash) {
|
|
2786
|
+
if (rule.isWildcard) {
|
|
2787
|
+
if (!actualArg.startsWith(rule.argument)) {
|
|
2788
|
+
return false;
|
|
2789
|
+
}
|
|
2790
|
+
const remainder = actualArg.slice(rule.argument.length);
|
|
2791
|
+
if (containsShellOperator(remainder)) {
|
|
2792
|
+
return false;
|
|
2793
|
+
}
|
|
2794
|
+
return true;
|
|
2795
|
+
}
|
|
2796
|
+
return actualArg === rule.argument;
|
|
2797
|
+
}
|
|
2798
|
+
return matchesGlob(rule.argument, actualArg, cwd);
|
|
2799
|
+
}
|
|
2800
|
+
async function loadSettingsFile(filePath) {
|
|
2801
|
+
if (!filePath) {
|
|
2802
|
+
return {};
|
|
2803
|
+
}
|
|
2804
|
+
try {
|
|
2805
|
+
const content = await fs2.promises.readFile(filePath, "utf-8");
|
|
2806
|
+
return JSON.parse(content);
|
|
2807
|
+
} catch {
|
|
2808
|
+
return {};
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
function getManagedSettingsPath() {
|
|
2812
|
+
switch (process.platform) {
|
|
2813
|
+
case "darwin":
|
|
2814
|
+
return "/Library/Application Support/ClaudeCode/managed-settings.json";
|
|
2815
|
+
case "linux":
|
|
2816
|
+
return "/etc/claude-code/managed-settings.json";
|
|
2817
|
+
case "win32":
|
|
2818
|
+
return "C:\\Program Files\\ClaudeCode\\managed-settings.json";
|
|
2819
|
+
default:
|
|
2820
|
+
return "/etc/claude-code/managed-settings.json";
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
var SettingsManager = class {
|
|
2824
|
+
cwd;
|
|
2825
|
+
userSettings = {};
|
|
2826
|
+
projectSettings = {};
|
|
2827
|
+
localSettings = {};
|
|
2828
|
+
enterpriseSettings = {};
|
|
2829
|
+
mergedSettings = {};
|
|
2830
|
+
initialized = false;
|
|
2831
|
+
constructor(cwd) {
|
|
2832
|
+
this.cwd = cwd;
|
|
2833
|
+
}
|
|
2834
|
+
async initialize() {
|
|
2835
|
+
if (this.initialized) {
|
|
2836
|
+
return;
|
|
2837
|
+
}
|
|
2838
|
+
await this.loadAllSettings();
|
|
2839
|
+
this.initialized = true;
|
|
2840
|
+
}
|
|
2841
|
+
getUserSettingsPath() {
|
|
2842
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || path3.join(os3.homedir(), ".claude");
|
|
2843
|
+
return path3.join(configDir, "settings.json");
|
|
2844
|
+
}
|
|
2845
|
+
getProjectSettingsPath() {
|
|
2846
|
+
return path3.join(this.cwd, ".claude", "settings.json");
|
|
2847
|
+
}
|
|
2848
|
+
getLocalSettingsPath() {
|
|
2849
|
+
return path3.join(this.cwd, ".claude", "settings.local.json");
|
|
2850
|
+
}
|
|
2851
|
+
async loadAllSettings() {
|
|
2852
|
+
const [userSettings, projectSettings, localSettings, enterpriseSettings] = await Promise.all([
|
|
2853
|
+
loadSettingsFile(this.getUserSettingsPath()),
|
|
2854
|
+
loadSettingsFile(this.getProjectSettingsPath()),
|
|
2855
|
+
loadSettingsFile(this.getLocalSettingsPath()),
|
|
2856
|
+
loadSettingsFile(getManagedSettingsPath())
|
|
2857
|
+
]);
|
|
2858
|
+
this.userSettings = userSettings;
|
|
2859
|
+
this.projectSettings = projectSettings;
|
|
2860
|
+
this.localSettings = localSettings;
|
|
2861
|
+
this.enterpriseSettings = enterpriseSettings;
|
|
2862
|
+
this.mergeAllSettings();
|
|
2863
|
+
}
|
|
2864
|
+
mergeAllSettings() {
|
|
2865
|
+
const allSettings = [
|
|
2866
|
+
this.userSettings,
|
|
2867
|
+
this.projectSettings,
|
|
2868
|
+
this.localSettings,
|
|
2869
|
+
this.enterpriseSettings
|
|
2870
|
+
];
|
|
2871
|
+
const permissions = {
|
|
2872
|
+
allow: [],
|
|
2873
|
+
deny: [],
|
|
2874
|
+
ask: []
|
|
2875
|
+
};
|
|
2876
|
+
const merged = { permissions };
|
|
2877
|
+
for (const settings of allSettings) {
|
|
2878
|
+
if (settings.permissions) {
|
|
2879
|
+
if (settings.permissions.allow) {
|
|
2880
|
+
permissions.allow?.push(...settings.permissions.allow);
|
|
2881
|
+
}
|
|
2882
|
+
if (settings.permissions.deny) {
|
|
2883
|
+
permissions.deny?.push(...settings.permissions.deny);
|
|
2884
|
+
}
|
|
2885
|
+
if (settings.permissions.ask) {
|
|
2886
|
+
permissions.ask?.push(...settings.permissions.ask);
|
|
2887
|
+
}
|
|
2888
|
+
if (settings.permissions.additionalDirectories) {
|
|
2889
|
+
permissions.additionalDirectories = [
|
|
2890
|
+
...permissions.additionalDirectories || [],
|
|
2891
|
+
...settings.permissions.additionalDirectories
|
|
2892
|
+
];
|
|
2893
|
+
}
|
|
2894
|
+
if (settings.permissions.defaultMode) {
|
|
2895
|
+
permissions.defaultMode = settings.permissions.defaultMode;
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
if (settings.env) {
|
|
2899
|
+
merged.env = { ...merged.env, ...settings.env };
|
|
2900
|
+
}
|
|
2901
|
+
if (settings.model) {
|
|
2902
|
+
merged.model = settings.model;
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
this.mergedSettings = merged;
|
|
2906
|
+
}
|
|
2907
|
+
checkPermission(toolName, toolInput) {
|
|
2908
|
+
if (!toolName.startsWith(ACP_TOOL_NAME_PREFIX)) {
|
|
2909
|
+
return { decision: "ask" };
|
|
2910
|
+
}
|
|
2911
|
+
const permissions = this.mergedSettings.permissions;
|
|
2912
|
+
if (!permissions) {
|
|
2913
|
+
return { decision: "ask" };
|
|
2914
|
+
}
|
|
2915
|
+
for (const rule of permissions.deny || []) {
|
|
2916
|
+
const parsed = parseRule(rule);
|
|
2917
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
2918
|
+
return { decision: "deny", rule, source: "deny" };
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
for (const rule of permissions.allow || []) {
|
|
2922
|
+
const parsed = parseRule(rule);
|
|
2923
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
2924
|
+
return { decision: "allow", rule, source: "allow" };
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
for (const rule of permissions.ask || []) {
|
|
2928
|
+
const parsed = parseRule(rule);
|
|
2929
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
2930
|
+
return { decision: "ask", rule, source: "ask" };
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
return { decision: "ask" };
|
|
2934
|
+
}
|
|
2935
|
+
getSettings() {
|
|
2936
|
+
return this.mergedSettings;
|
|
2937
|
+
}
|
|
2938
|
+
getCwd() {
|
|
2939
|
+
return this.cwd;
|
|
2940
|
+
}
|
|
2941
|
+
async setCwd(cwd) {
|
|
2942
|
+
if (this.cwd === cwd) {
|
|
2943
|
+
return;
|
|
2944
|
+
}
|
|
2945
|
+
this.dispose();
|
|
2946
|
+
this.cwd = cwd;
|
|
2947
|
+
this.initialized = false;
|
|
2948
|
+
await this.initialize();
|
|
2949
|
+
}
|
|
2950
|
+
dispose() {
|
|
2951
|
+
this.initialized = false;
|
|
2952
|
+
}
|
|
2953
|
+
};
|
|
2954
|
+
|
|
2955
|
+
// src/adapters/claude/claude-agent.ts
|
|
2956
|
+
var SESSION_VALIDATION_TIMEOUT_MS = 1e4;
|
|
2957
|
+
var MAX_TITLE_LENGTH = 256;
|
|
2958
|
+
function sanitizeTitle(text2) {
|
|
2959
|
+
const sanitized = text2.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
2960
|
+
if (sanitized.length <= MAX_TITLE_LENGTH) {
|
|
2961
|
+
return sanitized;
|
|
2962
|
+
}
|
|
2963
|
+
return `${sanitized.slice(0, MAX_TITLE_LENGTH - 1)}\u2026`;
|
|
2964
|
+
}
|
|
2965
|
+
var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
2966
|
+
adapterName = "claude";
|
|
2967
|
+
toolUseCache;
|
|
2968
|
+
backgroundTerminals = {};
|
|
2969
|
+
clientCapabilities;
|
|
2970
|
+
options;
|
|
2971
|
+
constructor(client, options) {
|
|
2972
|
+
super(client);
|
|
2973
|
+
this.options = options;
|
|
2974
|
+
this.toolUseCache = {};
|
|
2975
|
+
this.logger = new Logger({ debug: true, prefix: "[ClaudeAcpAgent]" });
|
|
2976
|
+
}
|
|
2977
|
+
async initialize(request) {
|
|
2978
|
+
this.clientCapabilities = request.clientCapabilities;
|
|
2979
|
+
return {
|
|
2980
|
+
protocolVersion: 1,
|
|
2981
|
+
agentCapabilities: {
|
|
2982
|
+
promptCapabilities: {
|
|
2983
|
+
image: true,
|
|
2984
|
+
embeddedContext: true
|
|
2985
|
+
},
|
|
2986
|
+
mcpCapabilities: {
|
|
2987
|
+
http: true,
|
|
2988
|
+
sse: true
|
|
2989
|
+
},
|
|
2990
|
+
loadSession: true,
|
|
2991
|
+
sessionCapabilities: {
|
|
2992
|
+
list: {},
|
|
2993
|
+
fork: {},
|
|
2994
|
+
resume: {}
|
|
2995
|
+
},
|
|
2996
|
+
_meta: {
|
|
2997
|
+
posthog: {
|
|
2998
|
+
resumeSession: true
|
|
2999
|
+
},
|
|
3000
|
+
claudeCode: {
|
|
3001
|
+
promptQueueing: true
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
},
|
|
3005
|
+
agentInfo: {
|
|
3006
|
+
name: package_default.name,
|
|
3007
|
+
title: "Claude Agent",
|
|
3008
|
+
version: package_default.version
|
|
3009
|
+
},
|
|
3010
|
+
authMethods: []
|
|
3011
|
+
};
|
|
3012
|
+
}
|
|
3013
|
+
async newSession(params) {
|
|
3014
|
+
if (fs3.existsSync(path4.resolve(os4.homedir(), ".claude.json.backup")) && !fs3.existsSync(path4.resolve(os4.homedir(), ".claude.json"))) {
|
|
3015
|
+
throw RequestError2.authRequired();
|
|
3016
|
+
}
|
|
3017
|
+
const response = await this.createSession(params, {
|
|
3018
|
+
// Revisit these meta values once we support resume
|
|
3019
|
+
resume: params._meta?.claudeCode?.options?.resume
|
|
2571
3020
|
});
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
3021
|
+
return response;
|
|
3022
|
+
}
|
|
3023
|
+
async unstable_forkSession(params) {
|
|
3024
|
+
return this.createSession(
|
|
3025
|
+
{
|
|
3026
|
+
cwd: params.cwd,
|
|
3027
|
+
mcpServers: params.mcpServers ?? [],
|
|
3028
|
+
_meta: params._meta
|
|
3029
|
+
},
|
|
3030
|
+
{ resume: params.sessionId, forkSession: true }
|
|
2582
3031
|
);
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
3032
|
+
}
|
|
3033
|
+
async unstable_resumeSession(params) {
|
|
3034
|
+
const response = await this.createSession(
|
|
3035
|
+
{
|
|
3036
|
+
cwd: params.cwd,
|
|
3037
|
+
mcpServers: params.mcpServers ?? [],
|
|
3038
|
+
_meta: params._meta
|
|
3039
|
+
},
|
|
3040
|
+
{
|
|
3041
|
+
resume: params.sessionId
|
|
3042
|
+
}
|
|
3043
|
+
);
|
|
3044
|
+
return response;
|
|
3045
|
+
}
|
|
3046
|
+
async loadSession(params) {
|
|
3047
|
+
const response = await this.createSession(
|
|
3048
|
+
{
|
|
3049
|
+
cwd: params.cwd,
|
|
3050
|
+
mcpServers: params.mcpServers ?? [],
|
|
3051
|
+
_meta: params._meta
|
|
3052
|
+
},
|
|
3053
|
+
{ resume: params.sessionId, skipBackgroundFetches: true }
|
|
3054
|
+
);
|
|
3055
|
+
await this.replaySessionHistory(params.sessionId);
|
|
3056
|
+
this.deferBackgroundFetches(this.session.query);
|
|
3057
|
+
return {
|
|
3058
|
+
modes: response.modes,
|
|
3059
|
+
models: response.models,
|
|
3060
|
+
configOptions: response.configOptions
|
|
3061
|
+
};
|
|
3062
|
+
}
|
|
3063
|
+
async unstable_listSessions(params) {
|
|
3064
|
+
const sdkSessions = await listSessions({ dir: params.cwd ?? void 0 });
|
|
3065
|
+
const sessions = [];
|
|
3066
|
+
for (const session of sdkSessions) {
|
|
3067
|
+
if (!session.cwd) continue;
|
|
3068
|
+
sessions.push({
|
|
3069
|
+
sessionId: session.sessionId,
|
|
3070
|
+
cwd: session.cwd,
|
|
3071
|
+
title: sanitizeTitle(session.customTitle || session.summary || ""),
|
|
3072
|
+
updatedAt: new Date(session.lastModified).toISOString()
|
|
2589
3073
|
});
|
|
2590
3074
|
}
|
|
2591
|
-
const modelOptions = await this.getModelConfigOptions();
|
|
2592
|
-
this.deferBackgroundFetches(q, sessionId);
|
|
2593
|
-
session.modelId = modelOptions.currentModelId;
|
|
2594
|
-
const resolvedSdkModel = toSdkModelId(modelOptions.currentModelId);
|
|
2595
|
-
if (resolvedSdkModel !== DEFAULT_MODEL) {
|
|
2596
|
-
await this.trySetModel(q, modelOptions.currentModelId);
|
|
2597
|
-
}
|
|
2598
|
-
const configOptions = await this.buildConfigOptions(modelOptions);
|
|
2599
3075
|
return {
|
|
2600
|
-
|
|
2601
|
-
configOptions
|
|
3076
|
+
sessions
|
|
2602
3077
|
};
|
|
2603
3078
|
}
|
|
2604
|
-
async
|
|
2605
|
-
|
|
3079
|
+
async prompt(params) {
|
|
3080
|
+
this.session.cancelled = false;
|
|
3081
|
+
this.session.interruptReason = void 0;
|
|
3082
|
+
this.session.accumulatedUsage = {
|
|
3083
|
+
inputTokens: 0,
|
|
3084
|
+
outputTokens: 0,
|
|
3085
|
+
cachedReadTokens: 0,
|
|
3086
|
+
cachedWriteTokens: 0
|
|
3087
|
+
};
|
|
3088
|
+
const userMessage = promptToClaude(params);
|
|
3089
|
+
if (this.session.promptRunning) {
|
|
3090
|
+
const uuid = randomUUID();
|
|
3091
|
+
userMessage.uuid = uuid;
|
|
3092
|
+
this.session.input.push(userMessage);
|
|
3093
|
+
const order = this.session.nextPendingOrder++;
|
|
3094
|
+
const cancelled = await new Promise((resolve3) => {
|
|
3095
|
+
this.session.pendingMessages.set(uuid, { resolve: resolve3, order });
|
|
3096
|
+
});
|
|
3097
|
+
if (cancelled) {
|
|
3098
|
+
return { stopReason: "cancelled" };
|
|
3099
|
+
}
|
|
3100
|
+
} else {
|
|
3101
|
+
this.session.input.push(userMessage);
|
|
3102
|
+
}
|
|
3103
|
+
await this.broadcastUserMessage(params);
|
|
3104
|
+
this.session.promptRunning = true;
|
|
3105
|
+
let handedOff = false;
|
|
3106
|
+
let lastAssistantTotalUsage = null;
|
|
3107
|
+
const supportsTerminalOutput = this.clientCapabilities?._meta?.terminal_output === true;
|
|
3108
|
+
const context = {
|
|
3109
|
+
session: this.session,
|
|
3110
|
+
sessionId: params.sessionId,
|
|
3111
|
+
client: this.client,
|
|
3112
|
+
toolUseCache: this.toolUseCache,
|
|
3113
|
+
fileContentCache: this.fileContentCache,
|
|
3114
|
+
logger: this.logger,
|
|
3115
|
+
supportsTerminalOutput
|
|
3116
|
+
};
|
|
3117
|
+
try {
|
|
3118
|
+
while (true) {
|
|
3119
|
+
const { value: message, done } = await this.session.query.next();
|
|
3120
|
+
if (done || !message) {
|
|
3121
|
+
if (this.session.cancelled) {
|
|
3122
|
+
return {
|
|
3123
|
+
stopReason: "cancelled",
|
|
3124
|
+
_meta: this.session.interruptReason ? { interruptReason: this.session.interruptReason } : void 0
|
|
3125
|
+
};
|
|
3126
|
+
}
|
|
3127
|
+
break;
|
|
3128
|
+
}
|
|
3129
|
+
switch (message.type) {
|
|
3130
|
+
case "system":
|
|
3131
|
+
if (message.subtype === "compact_boundary") {
|
|
3132
|
+
lastAssistantTotalUsage = 0;
|
|
3133
|
+
}
|
|
3134
|
+
await handleSystemMessage(message, context);
|
|
3135
|
+
break;
|
|
3136
|
+
case "result": {
|
|
3137
|
+
if (this.session.cancelled) {
|
|
3138
|
+
return { stopReason: "cancelled" };
|
|
3139
|
+
}
|
|
3140
|
+
this.session.accumulatedUsage.inputTokens += message.usage.input_tokens;
|
|
3141
|
+
this.session.accumulatedUsage.outputTokens += message.usage.output_tokens;
|
|
3142
|
+
this.session.accumulatedUsage.cachedReadTokens += message.usage.cache_read_input_tokens;
|
|
3143
|
+
this.session.accumulatedUsage.cachedWriteTokens += message.usage.cache_creation_input_tokens;
|
|
3144
|
+
const contextWindows = Object.values(message.modelUsage).map(
|
|
3145
|
+
(m) => m.contextWindow
|
|
3146
|
+
);
|
|
3147
|
+
const contextWindowSize = contextWindows.length > 0 ? Math.min(...contextWindows) : 2e5;
|
|
3148
|
+
if (lastAssistantTotalUsage !== null) {
|
|
3149
|
+
await this.client.sessionUpdate({
|
|
3150
|
+
sessionId: params.sessionId,
|
|
3151
|
+
update: {
|
|
3152
|
+
sessionUpdate: "usage_update",
|
|
3153
|
+
used: lastAssistantTotalUsage,
|
|
3154
|
+
size: contextWindowSize,
|
|
3155
|
+
cost: {
|
|
3156
|
+
amount: message.total_cost_usd,
|
|
3157
|
+
currency: "USD"
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
});
|
|
3161
|
+
}
|
|
3162
|
+
await this.client.extNotification("_posthog/usage_update", {
|
|
3163
|
+
sessionId: params.sessionId,
|
|
3164
|
+
used: {
|
|
3165
|
+
inputTokens: message.usage.input_tokens,
|
|
3166
|
+
outputTokens: message.usage.output_tokens,
|
|
3167
|
+
cachedReadTokens: message.usage.cache_read_input_tokens,
|
|
3168
|
+
cachedWriteTokens: message.usage.cache_creation_input_tokens
|
|
3169
|
+
},
|
|
3170
|
+
cost: message.total_cost_usd
|
|
3171
|
+
});
|
|
3172
|
+
const usage = {
|
|
3173
|
+
inputTokens: this.session.accumulatedUsage.inputTokens,
|
|
3174
|
+
outputTokens: this.session.accumulatedUsage.outputTokens,
|
|
3175
|
+
cachedReadTokens: this.session.accumulatedUsage.cachedReadTokens,
|
|
3176
|
+
cachedWriteTokens: this.session.accumulatedUsage.cachedWriteTokens,
|
|
3177
|
+
totalTokens: this.session.accumulatedUsage.inputTokens + this.session.accumulatedUsage.outputTokens + this.session.accumulatedUsage.cachedReadTokens + this.session.accumulatedUsage.cachedWriteTokens
|
|
3178
|
+
};
|
|
3179
|
+
const result = handleResultMessage(message);
|
|
3180
|
+
if (result.error) throw result.error;
|
|
3181
|
+
switch (message.subtype) {
|
|
3182
|
+
case "error_max_budget_usd":
|
|
3183
|
+
case "error_max_turns":
|
|
3184
|
+
case "error_max_structured_output_retries":
|
|
3185
|
+
return { stopReason: "max_turn_requests", usage };
|
|
3186
|
+
default:
|
|
3187
|
+
return { stopReason: "end_turn", usage };
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
case "stream_event":
|
|
3191
|
+
await handleStreamEvent(message, context);
|
|
3192
|
+
break;
|
|
3193
|
+
case "user":
|
|
3194
|
+
case "assistant": {
|
|
3195
|
+
if (this.session.cancelled) {
|
|
3196
|
+
break;
|
|
3197
|
+
}
|
|
3198
|
+
if (message.type === "user" && "uuid" in message && message.uuid) {
|
|
3199
|
+
const pending = this.session.pendingMessages.get(
|
|
3200
|
+
message.uuid
|
|
3201
|
+
);
|
|
3202
|
+
if (pending) {
|
|
3203
|
+
pending.resolve(false);
|
|
3204
|
+
this.session.pendingMessages.delete(message.uuid);
|
|
3205
|
+
handedOff = true;
|
|
3206
|
+
return { stopReason: "end_turn" };
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
if ("usage" in message.message && message.parent_tool_use_id === null) {
|
|
3210
|
+
const usage = message.message.usage;
|
|
3211
|
+
lastAssistantTotalUsage = usage.input_tokens + usage.output_tokens + usage.cache_read_input_tokens + usage.cache_creation_input_tokens;
|
|
3212
|
+
}
|
|
3213
|
+
const result = await handleUserAssistantMessage(message, context);
|
|
3214
|
+
if (result.error) throw result.error;
|
|
3215
|
+
if (result.shouldStop) {
|
|
3216
|
+
return { stopReason: "end_turn" };
|
|
3217
|
+
}
|
|
3218
|
+
break;
|
|
3219
|
+
}
|
|
3220
|
+
case "tool_progress":
|
|
3221
|
+
case "auth_status":
|
|
3222
|
+
case "tool_use_summary":
|
|
3223
|
+
break;
|
|
3224
|
+
default:
|
|
3225
|
+
unreachable(message, this.logger);
|
|
3226
|
+
break;
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
throw new Error("Session did not end in result");
|
|
3230
|
+
} finally {
|
|
3231
|
+
if (!handedOff) {
|
|
3232
|
+
this.session.promptRunning = false;
|
|
3233
|
+
for (const [key, pending] of this.session.pendingMessages) {
|
|
3234
|
+
pending.resolve(true);
|
|
3235
|
+
this.session.pendingMessages.delete(key);
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
// Called by BaseAcpAgent#cancel() to interrupt the session
|
|
3241
|
+
async interrupt() {
|
|
3242
|
+
this.session.cancelled = true;
|
|
3243
|
+
for (const [, pending] of this.session.pendingMessages) {
|
|
3244
|
+
pending.resolve(true);
|
|
3245
|
+
}
|
|
3246
|
+
this.session.pendingMessages.clear();
|
|
3247
|
+
await this.session.query.interrupt();
|
|
3248
|
+
}
|
|
3249
|
+
async unstable_setSessionModel(params) {
|
|
3250
|
+
const sdkModelId = toSdkModelId(params.modelId);
|
|
3251
|
+
await this.session.query.setModel(sdkModelId);
|
|
3252
|
+
this.session.modelId = params.modelId;
|
|
3253
|
+
await this.updateConfigOption("model", params.modelId);
|
|
3254
|
+
return {};
|
|
2606
3255
|
}
|
|
2607
|
-
async
|
|
3256
|
+
async setSessionMode(params) {
|
|
3257
|
+
await this.applySessionMode(params.modeId);
|
|
3258
|
+
await this.updateConfigOption("mode", params.modeId);
|
|
3259
|
+
return {};
|
|
3260
|
+
}
|
|
3261
|
+
async setSessionConfigOption(params) {
|
|
3262
|
+
const option = this.session.configOptions.find(
|
|
3263
|
+
(o) => o.id === params.configId
|
|
3264
|
+
);
|
|
3265
|
+
if (!option) {
|
|
3266
|
+
throw new Error(`Unknown config option: ${params.configId}`);
|
|
3267
|
+
}
|
|
3268
|
+
const allValues = "options" in option && Array.isArray(option.options) ? option.options.flatMap(
|
|
3269
|
+
(o) => "options" in o && Array.isArray(o.options) ? o.options : [o]
|
|
3270
|
+
) : [];
|
|
3271
|
+
const validValue = allValues.find((o) => o.value === params.value);
|
|
3272
|
+
if (!validValue) {
|
|
3273
|
+
throw new Error(
|
|
3274
|
+
`Invalid value for config option ${params.configId}: ${params.value}`
|
|
3275
|
+
);
|
|
3276
|
+
}
|
|
3277
|
+
if (params.configId === "mode") {
|
|
3278
|
+
await this.applySessionMode(params.value);
|
|
3279
|
+
await this.client.sessionUpdate({
|
|
3280
|
+
sessionId: this.sessionId,
|
|
3281
|
+
update: {
|
|
3282
|
+
sessionUpdate: "current_mode_update",
|
|
3283
|
+
currentModeId: params.value
|
|
3284
|
+
}
|
|
3285
|
+
});
|
|
3286
|
+
} else if (params.configId === "model") {
|
|
3287
|
+
const sdkModelId = toSdkModelId(params.value);
|
|
3288
|
+
await this.session.query.setModel(sdkModelId);
|
|
3289
|
+
this.session.modelId = params.value;
|
|
3290
|
+
}
|
|
3291
|
+
this.session.configOptions = this.session.configOptions.map(
|
|
3292
|
+
(o) => o.id === params.configId ? { ...o, currentValue: params.value } : o
|
|
3293
|
+
);
|
|
3294
|
+
return { configOptions: this.session.configOptions };
|
|
3295
|
+
}
|
|
3296
|
+
async updateConfigOption(configId, value) {
|
|
3297
|
+
this.session.configOptions = this.session.configOptions.map(
|
|
3298
|
+
(o) => o.id === configId ? { ...o, currentValue: value } : o
|
|
3299
|
+
);
|
|
3300
|
+
await this.client.sessionUpdate({
|
|
3301
|
+
sessionId: this.sessionId,
|
|
3302
|
+
update: {
|
|
3303
|
+
sessionUpdate: "config_option_update",
|
|
3304
|
+
configOptions: this.session.configOptions
|
|
3305
|
+
}
|
|
3306
|
+
});
|
|
3307
|
+
}
|
|
3308
|
+
async applySessionMode(modeId) {
|
|
3309
|
+
if (!TWIG_EXECUTION_MODES.includes(modeId)) {
|
|
3310
|
+
throw new Error("Invalid Mode");
|
|
3311
|
+
}
|
|
3312
|
+
const previousMode = this.session.permissionMode;
|
|
3313
|
+
this.session.permissionMode = modeId;
|
|
3314
|
+
try {
|
|
3315
|
+
await this.session.query.setPermissionMode(modeId);
|
|
3316
|
+
} catch (error) {
|
|
3317
|
+
this.session.permissionMode = previousMode;
|
|
3318
|
+
if (error instanceof Error) {
|
|
3319
|
+
if (!error.message) {
|
|
3320
|
+
error.message = "Invalid Mode";
|
|
3321
|
+
}
|
|
3322
|
+
throw error;
|
|
3323
|
+
}
|
|
3324
|
+
throw new Error("Invalid Mode");
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
async createSession(params, creationOpts = {}) {
|
|
3328
|
+
const { cwd } = params;
|
|
3329
|
+
const { resume, forkSession } = creationOpts;
|
|
3330
|
+
const isResume = !!resume;
|
|
2608
3331
|
const meta = params._meta;
|
|
2609
3332
|
const taskId = meta?.persistence?.taskId;
|
|
2610
|
-
|
|
2611
|
-
if (
|
|
2612
|
-
|
|
2613
|
-
}
|
|
2614
|
-
|
|
2615
|
-
|
|
3333
|
+
let sessionId;
|
|
3334
|
+
if (forkSession) {
|
|
3335
|
+
sessionId = uuidv7();
|
|
3336
|
+
} else if (isResume) {
|
|
3337
|
+
sessionId = resume;
|
|
3338
|
+
} else {
|
|
3339
|
+
sessionId = uuidv7();
|
|
2616
3340
|
}
|
|
2617
|
-
|
|
3341
|
+
const input = new Pushable();
|
|
3342
|
+
const settingsManager = new SettingsManager(cwd);
|
|
3343
|
+
await settingsManager.initialize();
|
|
3344
|
+
const mcpServers = parseMcpServers(params);
|
|
3345
|
+
const systemPrompt = buildSystemPrompt(meta?.systemPrompt);
|
|
3346
|
+
this.logger.info(isResume ? "Resuming session" : "Creating new session", {
|
|
2618
3347
|
sessionId,
|
|
2619
3348
|
taskId,
|
|
2620
3349
|
taskRunId: meta?.taskRunId,
|
|
2621
|
-
cwd
|
|
3350
|
+
cwd
|
|
2622
3351
|
});
|
|
2623
|
-
const mcpServers = parseMcpServers(params);
|
|
2624
3352
|
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
2625
|
-
const
|
|
2626
|
-
cwd
|
|
2627
|
-
permissionMode,
|
|
3353
|
+
const options = buildSessionOptions({
|
|
3354
|
+
cwd,
|
|
2628
3355
|
mcpServers,
|
|
2629
|
-
|
|
3356
|
+
permissionMode,
|
|
3357
|
+
canUseTool: this.createCanUseTool(sessionId),
|
|
3358
|
+
logger: this.logger,
|
|
3359
|
+
systemPrompt,
|
|
2630
3360
|
userProvidedOptions: meta?.claudeCode?.options,
|
|
2631
3361
|
sessionId,
|
|
2632
|
-
isResume
|
|
2633
|
-
|
|
3362
|
+
isResume,
|
|
3363
|
+
forkSession,
|
|
3364
|
+
additionalDirectories: meta?.claudeCode?.options?.additionalDirectories,
|
|
3365
|
+
disableBuiltInTools: meta?.disableBuiltInTools,
|
|
3366
|
+
settingsManager,
|
|
3367
|
+
onModeChange: this.createOnModeChange(),
|
|
3368
|
+
onProcessSpawned: this.options?.onProcessSpawned,
|
|
3369
|
+
onProcessExited: this.options?.onProcessExited
|
|
2634
3370
|
});
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
3371
|
+
const abortController = options.abortController;
|
|
3372
|
+
const q = query({ prompt: input, options });
|
|
3373
|
+
const session = {
|
|
3374
|
+
query: q,
|
|
3375
|
+
input,
|
|
3376
|
+
cancelled: false,
|
|
3377
|
+
settingsManager,
|
|
3378
|
+
permissionMode,
|
|
3379
|
+
abortController,
|
|
3380
|
+
accumulatedUsage: {
|
|
3381
|
+
inputTokens: 0,
|
|
3382
|
+
outputTokens: 0,
|
|
3383
|
+
cachedReadTokens: 0,
|
|
3384
|
+
cachedWriteTokens: 0
|
|
3385
|
+
},
|
|
3386
|
+
configOptions: [],
|
|
3387
|
+
promptRunning: false,
|
|
3388
|
+
pendingMessages: /* @__PURE__ */ new Map(),
|
|
3389
|
+
nextPendingOrder: 0,
|
|
3390
|
+
// Custom properties
|
|
3391
|
+
cwd,
|
|
3392
|
+
notificationHistory: [],
|
|
2638
3393
|
taskRunId: meta?.taskRunId
|
|
2639
|
-
}
|
|
2640
|
-
session
|
|
3394
|
+
};
|
|
3395
|
+
this.session = session;
|
|
3396
|
+
this.sessionId = sessionId;
|
|
3397
|
+
this.logger.info(
|
|
3398
|
+
isResume ? "Session query initialized, awaiting resumption" : "Session query initialized, awaiting initialization",
|
|
3399
|
+
{ sessionId, taskId, taskRunId: meta?.taskRunId }
|
|
3400
|
+
);
|
|
2641
3401
|
try {
|
|
2642
3402
|
const result = await withTimeout(
|
|
2643
3403
|
q.initializationResult(),
|
|
@@ -2645,231 +3405,181 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
2645
3405
|
);
|
|
2646
3406
|
if (result.result === "timeout") {
|
|
2647
3407
|
throw new Error(
|
|
2648
|
-
`Session resumption timed out for sessionId=${sessionId}`
|
|
3408
|
+
`Session ${isResume ? forkSession ? "fork" : "resumption" : "initialization"} timed out for sessionId=${sessionId}`
|
|
2649
3409
|
);
|
|
2650
3410
|
}
|
|
2651
3411
|
} catch (err) {
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
3412
|
+
settingsManager.dispose();
|
|
3413
|
+
this.logger.error(
|
|
3414
|
+
isResume ? forkSession ? "Session fork failed" : "Session resumption failed" : "Session initialization failed",
|
|
3415
|
+
{
|
|
3416
|
+
sessionId,
|
|
3417
|
+
taskId,
|
|
3418
|
+
taskRunId: meta?.taskRunId,
|
|
3419
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3420
|
+
}
|
|
3421
|
+
);
|
|
2658
3422
|
throw err;
|
|
2659
3423
|
}
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
const configOptions = await this.buildConfigOptions();
|
|
2667
|
-
return { configOptions };
|
|
2668
|
-
}
|
|
2669
|
-
async prompt(params) {
|
|
2670
|
-
this.session.cancelled = false;
|
|
2671
|
-
this.session.interruptReason = void 0;
|
|
2672
|
-
await this.broadcastUserMessage(params);
|
|
2673
|
-
this.session.input.push(promptToClaude(params));
|
|
2674
|
-
return this.processMessages(params.sessionId);
|
|
2675
|
-
}
|
|
2676
|
-
async setSessionConfigOption(params) {
|
|
2677
|
-
const configId = params.configId;
|
|
2678
|
-
const value = params.value;
|
|
2679
|
-
if (configId === "mode") {
|
|
2680
|
-
const modeId = value;
|
|
2681
|
-
if (!TWIG_EXECUTION_MODES.includes(modeId)) {
|
|
2682
|
-
throw new Error("Invalid Mode");
|
|
2683
|
-
}
|
|
2684
|
-
this.session.permissionMode = modeId;
|
|
2685
|
-
await this.session.query.setPermissionMode(modeId);
|
|
2686
|
-
} else if (configId === "model") {
|
|
2687
|
-
await this.setModelWithFallback(this.session.query, value);
|
|
2688
|
-
this.session.modelId = value;
|
|
2689
|
-
} else {
|
|
2690
|
-
throw new Error("Unsupported config option");
|
|
3424
|
+
if (meta?.taskRunId) {
|
|
3425
|
+
await this.client.extNotification("_posthog/sdk_session", {
|
|
3426
|
+
taskRunId: meta.taskRunId,
|
|
3427
|
+
sessionId,
|
|
3428
|
+
adapter: "claude"
|
|
3429
|
+
});
|
|
2691
3430
|
}
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
params
|
|
2702
|
-
);
|
|
2703
|
-
return {
|
|
2704
|
-
_meta: {
|
|
2705
|
-
configOptions: result.configOptions
|
|
2706
|
-
}
|
|
2707
|
-
};
|
|
3431
|
+
const settingsModel = settingsManager.getSettings().model;
|
|
3432
|
+
const modelOptions = await this.getModelConfigOptions();
|
|
3433
|
+
const resolvedModelId = settingsModel || modelOptions.currentModelId;
|
|
3434
|
+
session.modelId = resolvedModelId;
|
|
3435
|
+
if (!isResume) {
|
|
3436
|
+
const resolvedSdkModel = toSdkModelId(resolvedModelId);
|
|
3437
|
+
if (resolvedSdkModel !== DEFAULT_MODEL) {
|
|
3438
|
+
await this.session.query.setModel(resolvedSdkModel);
|
|
3439
|
+
}
|
|
2708
3440
|
}
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
cwd,
|
|
2718
|
-
notificationHistory: [],
|
|
2719
|
-
abortController
|
|
3441
|
+
const availableModes2 = getAvailableModes();
|
|
3442
|
+
const modes = {
|
|
3443
|
+
currentModeId: permissionMode,
|
|
3444
|
+
availableModes: availableModes2.map((mode) => ({
|
|
3445
|
+
id: mode.id,
|
|
3446
|
+
name: mode.name,
|
|
3447
|
+
description: mode.description ?? void 0
|
|
3448
|
+
}))
|
|
2720
3449
|
};
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
const abortController = options.abortController;
|
|
2744
|
-
const session = this.createSession(
|
|
2745
|
-
config.sessionId,
|
|
2746
|
-
q,
|
|
2747
|
-
input,
|
|
2748
|
-
config.permissionMode,
|
|
2749
|
-
config.cwd,
|
|
2750
|
-
abortController
|
|
3450
|
+
const models = {
|
|
3451
|
+
currentModelId: resolvedModelId,
|
|
3452
|
+
availableModels: modelOptions.options.map(
|
|
3453
|
+
(opt) => ({
|
|
3454
|
+
modelId: opt.value,
|
|
3455
|
+
name: opt.name,
|
|
3456
|
+
description: opt.description
|
|
3457
|
+
})
|
|
3458
|
+
)
|
|
3459
|
+
};
|
|
3460
|
+
const configOptions = this.buildConfigOptions(permissionMode, modelOptions);
|
|
3461
|
+
session.configOptions = configOptions;
|
|
3462
|
+
if (!creationOpts.skipBackgroundFetches) {
|
|
3463
|
+
this.deferBackgroundFetches(q);
|
|
3464
|
+
}
|
|
3465
|
+
this.logger.info(
|
|
3466
|
+
isResume ? "Session resumed successfully" : "Session created successfully",
|
|
3467
|
+
{
|
|
3468
|
+
sessionId,
|
|
3469
|
+
taskId,
|
|
3470
|
+
taskRunId: meta?.taskRunId
|
|
3471
|
+
}
|
|
2751
3472
|
);
|
|
2752
|
-
return {
|
|
3473
|
+
return { sessionId, modes, models, configOptions };
|
|
2753
3474
|
}
|
|
2754
3475
|
createCanUseTool(sessionId) {
|
|
2755
|
-
return async (toolName, toolInput, { suggestions, toolUseID }) => canUseTool({
|
|
3476
|
+
return async (toolName, toolInput, { suggestions, toolUseID, signal }) => canUseTool({
|
|
2756
3477
|
session: this.session,
|
|
2757
3478
|
toolName,
|
|
2758
3479
|
toolInput,
|
|
2759
3480
|
toolUseID,
|
|
2760
3481
|
suggestions,
|
|
3482
|
+
signal,
|
|
2761
3483
|
client: this.client,
|
|
2762
3484
|
sessionId,
|
|
2763
3485
|
fileContentCache: this.fileContentCache,
|
|
2764
3486
|
logger: this.logger,
|
|
2765
|
-
|
|
3487
|
+
updateConfigOption: (configId, value) => this.updateConfigOption(configId, value)
|
|
2766
3488
|
});
|
|
2767
3489
|
}
|
|
2768
|
-
createOnModeChange(
|
|
3490
|
+
createOnModeChange() {
|
|
2769
3491
|
return async (newMode) => {
|
|
2770
3492
|
if (this.session) {
|
|
2771
3493
|
this.session.permissionMode = newMode;
|
|
2772
3494
|
}
|
|
2773
|
-
await this.
|
|
3495
|
+
await this.updateConfigOption("mode", newMode);
|
|
2774
3496
|
};
|
|
2775
3497
|
}
|
|
2776
|
-
|
|
2777
|
-
const options = [];
|
|
3498
|
+
buildConfigOptions(currentModeId, modelOptions) {
|
|
2778
3499
|
const modeOptions = getAvailableModes().map((mode) => ({
|
|
2779
3500
|
value: mode.id,
|
|
2780
3501
|
name: mode.name,
|
|
2781
3502
|
description: mode.description ?? void 0
|
|
2782
3503
|
}));
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
return options;
|
|
3504
|
+
return [
|
|
3505
|
+
{
|
|
3506
|
+
id: "mode",
|
|
3507
|
+
name: "Approval Preset",
|
|
3508
|
+
type: "select",
|
|
3509
|
+
currentValue: currentModeId,
|
|
3510
|
+
options: modeOptions,
|
|
3511
|
+
category: "mode",
|
|
3512
|
+
description: "Choose an approval and sandboxing preset for your session"
|
|
3513
|
+
},
|
|
3514
|
+
{
|
|
3515
|
+
id: "model",
|
|
3516
|
+
name: "Model",
|
|
3517
|
+
type: "select",
|
|
3518
|
+
currentValue: modelOptions.currentModelId,
|
|
3519
|
+
options: modelOptions.options,
|
|
3520
|
+
category: "model",
|
|
3521
|
+
description: "Choose which model Claude should use"
|
|
3522
|
+
}
|
|
3523
|
+
];
|
|
2804
3524
|
}
|
|
2805
|
-
async
|
|
2806
|
-
const
|
|
2807
|
-
const serialized = JSON.stringify(configOptions);
|
|
2808
|
-
if (this.lastSentConfigOptions && JSON.stringify(this.lastSentConfigOptions) === serialized) {
|
|
2809
|
-
return;
|
|
2810
|
-
}
|
|
2811
|
-
this.lastSentConfigOptions = configOptions;
|
|
3525
|
+
async sendAvailableCommandsUpdate() {
|
|
3526
|
+
const commands = await this.session.query.supportedCommands();
|
|
2812
3527
|
await this.client.sessionUpdate({
|
|
2813
|
-
sessionId:
|
|
3528
|
+
sessionId: this.sessionId,
|
|
2814
3529
|
update: {
|
|
2815
|
-
sessionUpdate: "
|
|
2816
|
-
|
|
3530
|
+
sessionUpdate: "available_commands_update",
|
|
3531
|
+
availableCommands: getAvailableSlashCommands(commands)
|
|
2817
3532
|
}
|
|
2818
3533
|
});
|
|
2819
3534
|
}
|
|
2820
|
-
|
|
2821
|
-
const backupExists = fs2.existsSync(
|
|
2822
|
-
path3.resolve(os3.homedir(), ".claude.json.backup")
|
|
2823
|
-
);
|
|
2824
|
-
const configExists = fs2.existsSync(
|
|
2825
|
-
path3.resolve(os3.homedir(), ".claude.json")
|
|
2826
|
-
);
|
|
2827
|
-
if (backupExists && !configExists) {
|
|
2828
|
-
throw RequestError2.authRequired();
|
|
2829
|
-
}
|
|
2830
|
-
}
|
|
2831
|
-
async trySetModel(q, modelId) {
|
|
2832
|
-
try {
|
|
2833
|
-
await this.setModelWithFallback(q, modelId);
|
|
2834
|
-
} catch (err) {
|
|
2835
|
-
this.logger.warn("Failed to set model", { modelId, error: err });
|
|
2836
|
-
}
|
|
2837
|
-
}
|
|
2838
|
-
async setModelWithFallback(q, modelId) {
|
|
2839
|
-
const sdkModelId = toSdkModelId(modelId);
|
|
3535
|
+
async replaySessionHistory(sessionId) {
|
|
2840
3536
|
try {
|
|
2841
|
-
await
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
3537
|
+
const messages = await getSessionMessages(sessionId, {
|
|
3538
|
+
dir: this.session.cwd
|
|
3539
|
+
});
|
|
3540
|
+
const replayContext = {
|
|
3541
|
+
session: this.session,
|
|
3542
|
+
sessionId,
|
|
3543
|
+
client: this.client,
|
|
3544
|
+
toolUseCache: this.toolUseCache,
|
|
3545
|
+
fileContentCache: this.fileContentCache,
|
|
3546
|
+
logger: this.logger,
|
|
3547
|
+
registerHooks: false
|
|
3548
|
+
};
|
|
3549
|
+
for (const msg of messages) {
|
|
3550
|
+
const sdkMessage = {
|
|
3551
|
+
type: msg.type,
|
|
3552
|
+
message: msg.message,
|
|
3553
|
+
parent_tool_use_id: msg.parent_tool_use_id
|
|
3554
|
+
};
|
|
3555
|
+
await handleUserAssistantMessage(
|
|
3556
|
+
sdkMessage,
|
|
3557
|
+
replayContext
|
|
3558
|
+
);
|
|
2845
3559
|
}
|
|
2846
|
-
|
|
3560
|
+
} catch (err) {
|
|
3561
|
+
this.logger.warn("Failed to replay session history", {
|
|
3562
|
+
sessionId,
|
|
3563
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3564
|
+
});
|
|
2847
3565
|
}
|
|
2848
3566
|
}
|
|
3567
|
+
// ================================
|
|
3568
|
+
// EXTENSION METHODS
|
|
3569
|
+
// ================================
|
|
2849
3570
|
/**
|
|
2850
3571
|
* Fire-and-forget: fetch slash commands and MCP tool metadata in parallel.
|
|
2851
3572
|
* Both populate caches used later — neither is needed to return configOptions.
|
|
2852
3573
|
*/
|
|
2853
|
-
deferBackgroundFetches(q
|
|
3574
|
+
deferBackgroundFetches(q) {
|
|
2854
3575
|
Promise.all([
|
|
2855
|
-
|
|
3576
|
+
new Promise((resolve3) => setTimeout(resolve3, 10)).then(
|
|
3577
|
+
() => this.sendAvailableCommandsUpdate()
|
|
3578
|
+
),
|
|
2856
3579
|
fetchMcpToolMetadata(q, this.logger)
|
|
2857
|
-
]).
|
|
2858
|
-
this.
|
|
2859
|
-
|
|
2860
|
-
this.logger.warn("Failed to fetch deferred session data", { err });
|
|
2861
|
-
});
|
|
2862
|
-
}
|
|
2863
|
-
sendAvailableCommandsUpdate(sessionId, availableCommands) {
|
|
2864
|
-
setTimeout(() => {
|
|
2865
|
-
this.client.sessionUpdate({
|
|
2866
|
-
sessionId,
|
|
2867
|
-
update: {
|
|
2868
|
-
sessionUpdate: "available_commands_update",
|
|
2869
|
-
availableCommands
|
|
2870
|
-
}
|
|
2871
|
-
});
|
|
2872
|
-
}, 0);
|
|
3580
|
+
]).catch(
|
|
3581
|
+
(err) => this.logger.error("Background fetch failed", { error: err })
|
|
3582
|
+
);
|
|
2873
3583
|
}
|
|
2874
3584
|
async broadcastUserMessage(params) {
|
|
2875
3585
|
for (const chunk of params.prompt) {
|
|
@@ -2884,71 +3594,6 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
2884
3594
|
this.appendNotification(params.sessionId, notification);
|
|
2885
3595
|
}
|
|
2886
3596
|
}
|
|
2887
|
-
async processMessages(sessionId) {
|
|
2888
|
-
const context = {
|
|
2889
|
-
session: this.session,
|
|
2890
|
-
sessionId,
|
|
2891
|
-
client: this.client,
|
|
2892
|
-
toolUseCache: this.toolUseCache,
|
|
2893
|
-
fileContentCache: this.fileContentCache,
|
|
2894
|
-
logger: this.logger
|
|
2895
|
-
};
|
|
2896
|
-
while (true) {
|
|
2897
|
-
const { value: message, done } = await this.session.query.next();
|
|
2898
|
-
if (done || !message) {
|
|
2899
|
-
return this.handleSessionEnd();
|
|
2900
|
-
}
|
|
2901
|
-
const response = await this.handleMessage(message, context);
|
|
2902
|
-
if (response) {
|
|
2903
|
-
return response;
|
|
2904
|
-
}
|
|
2905
|
-
}
|
|
2906
|
-
}
|
|
2907
|
-
handleSessionEnd() {
|
|
2908
|
-
if (this.session.cancelled) {
|
|
2909
|
-
return {
|
|
2910
|
-
stopReason: "cancelled",
|
|
2911
|
-
_meta: this.session.interruptReason ? { interruptReason: this.session.interruptReason } : void 0
|
|
2912
|
-
};
|
|
2913
|
-
}
|
|
2914
|
-
throw new Error("Session did not end in result");
|
|
2915
|
-
}
|
|
2916
|
-
async handleMessage(message, context) {
|
|
2917
|
-
switch (message.type) {
|
|
2918
|
-
case "system":
|
|
2919
|
-
await handleSystemMessage(message, context);
|
|
2920
|
-
return null;
|
|
2921
|
-
case "result": {
|
|
2922
|
-
const result = handleResultMessage(message, context);
|
|
2923
|
-
if (result.error) throw result.error;
|
|
2924
|
-
if (result.shouldStop) {
|
|
2925
|
-
return {
|
|
2926
|
-
stopReason: result.stopReason
|
|
2927
|
-
};
|
|
2928
|
-
}
|
|
2929
|
-
return null;
|
|
2930
|
-
}
|
|
2931
|
-
case "stream_event":
|
|
2932
|
-
await handleStreamEvent(message, context);
|
|
2933
|
-
return null;
|
|
2934
|
-
case "user":
|
|
2935
|
-
case "assistant": {
|
|
2936
|
-
const result = await handleUserAssistantMessage(message, context);
|
|
2937
|
-
if (result.error) throw result.error;
|
|
2938
|
-
if (result.shouldStop) {
|
|
2939
|
-
return { stopReason: "end_turn" };
|
|
2940
|
-
}
|
|
2941
|
-
return null;
|
|
2942
|
-
}
|
|
2943
|
-
case "tool_progress":
|
|
2944
|
-
case "auth_status":
|
|
2945
|
-
case "tool_use_summary":
|
|
2946
|
-
return null;
|
|
2947
|
-
default:
|
|
2948
|
-
unreachable(message, this.logger);
|
|
2949
|
-
return null;
|
|
2950
|
-
}
|
|
2951
|
-
}
|
|
2952
3597
|
};
|
|
2953
3598
|
|
|
2954
3599
|
// src/adapters/codex/spawn.ts
|
|
@@ -3575,8 +4220,8 @@ var PostHogAPIClient = class {
|
|
|
3575
4220
|
};
|
|
3576
4221
|
|
|
3577
4222
|
// src/session-log-writer.ts
|
|
3578
|
-
import
|
|
3579
|
-
import
|
|
4223
|
+
import fs4 from "fs";
|
|
4224
|
+
import path5 from "path";
|
|
3580
4225
|
var SessionLogWriter = class _SessionLogWriter {
|
|
3581
4226
|
static FLUSH_DEBOUNCE_MS = 500;
|
|
3582
4227
|
static FLUSH_MAX_INTERVAL_MS = 5e3;
|
|
@@ -3588,7 +4233,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
3588
4233
|
lastFlushAttemptTime = /* @__PURE__ */ new Map();
|
|
3589
4234
|
retryCounts = /* @__PURE__ */ new Map();
|
|
3590
4235
|
sessions = /* @__PURE__ */ new Map();
|
|
3591
|
-
messageCounts = /* @__PURE__ */ new Map();
|
|
3592
4236
|
logger;
|
|
3593
4237
|
localCachePath;
|
|
3594
4238
|
constructor(options = {}) {
|
|
@@ -3598,19 +4242,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
3598
4242
|
}
|
|
3599
4243
|
async flushAll() {
|
|
3600
4244
|
const sessionIds = [...this.sessions.keys()];
|
|
3601
|
-
const pendingCounts = sessionIds.map((id) => {
|
|
3602
|
-
const session = this.sessions.get(id);
|
|
3603
|
-
return {
|
|
3604
|
-
taskId: session?.context.taskId,
|
|
3605
|
-
runId: session?.context.runId,
|
|
3606
|
-
pending: this.pendingEntries.get(id)?.length ?? 0,
|
|
3607
|
-
messages: this.messageCounts.get(id) ?? 0
|
|
3608
|
-
};
|
|
3609
|
-
});
|
|
3610
|
-
this.logger.info("flushAll called", {
|
|
3611
|
-
sessions: sessionIds.length,
|
|
3612
|
-
pending: pendingCounts
|
|
3613
|
-
});
|
|
3614
4245
|
const flushPromises = [];
|
|
3615
4246
|
for (const sessionId of sessionIds) {
|
|
3616
4247
|
flushPromises.push(this.flush(sessionId));
|
|
@@ -3628,13 +4259,13 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
3628
4259
|
this.sessions.set(sessionId, { context });
|
|
3629
4260
|
this.lastFlushAttemptTime.set(sessionId, Date.now());
|
|
3630
4261
|
if (this.localCachePath) {
|
|
3631
|
-
const sessionDir =
|
|
4262
|
+
const sessionDir = path5.join(
|
|
3632
4263
|
this.localCachePath,
|
|
3633
4264
|
"sessions",
|
|
3634
4265
|
context.runId
|
|
3635
4266
|
);
|
|
3636
4267
|
try {
|
|
3637
|
-
|
|
4268
|
+
fs4.mkdirSync(sessionDir, { recursive: true });
|
|
3638
4269
|
} catch (error) {
|
|
3639
4270
|
this.logger.warn("Failed to create local cache directory", {
|
|
3640
4271
|
sessionDir,
|
|
@@ -3654,15 +4285,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
3654
4285
|
});
|
|
3655
4286
|
return;
|
|
3656
4287
|
}
|
|
3657
|
-
const count = (this.messageCounts.get(sessionId) ?? 0) + 1;
|
|
3658
|
-
this.messageCounts.set(sessionId, count);
|
|
3659
|
-
if (count % 10 === 1) {
|
|
3660
|
-
this.logger.info("Messages received", {
|
|
3661
|
-
count,
|
|
3662
|
-
taskId: session.context.taskId,
|
|
3663
|
-
runId: session.context.runId
|
|
3664
|
-
});
|
|
3665
|
-
}
|
|
3666
4288
|
try {
|
|
3667
4289
|
const message = JSON.parse(line);
|
|
3668
4290
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -3711,12 +4333,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
3711
4333
|
this.emitCoalescedMessage(sessionId, session);
|
|
3712
4334
|
const pending = this.pendingEntries.get(sessionId);
|
|
3713
4335
|
if (!this.posthogAPI || !pending?.length) {
|
|
3714
|
-
this.logger.info("flush: nothing to persist", {
|
|
3715
|
-
taskId: session.context.taskId,
|
|
3716
|
-
runId: session.context.runId,
|
|
3717
|
-
hasPosthogAPI: !!this.posthogAPI,
|
|
3718
|
-
pendingCount: pending?.length ?? 0
|
|
3719
|
-
});
|
|
3720
4336
|
return;
|
|
3721
4337
|
}
|
|
3722
4338
|
this.pendingEntries.delete(sessionId);
|
|
@@ -3733,11 +4349,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
3733
4349
|
pending
|
|
3734
4350
|
);
|
|
3735
4351
|
this.retryCounts.set(sessionId, 0);
|
|
3736
|
-
this.logger.info("Flushed session logs", {
|
|
3737
|
-
taskId: session.context.taskId,
|
|
3738
|
-
runId: session.context.runId,
|
|
3739
|
-
entryCount: pending.length
|
|
3740
|
-
});
|
|
3741
4352
|
} catch (error) {
|
|
3742
4353
|
const retryCount = (this.retryCounts.get(sessionId) ?? 0) + 1;
|
|
3743
4354
|
this.retryCounts.set(sessionId, retryCount);
|
|
@@ -3851,14 +4462,14 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
3851
4462
|
if (!this.localCachePath) return;
|
|
3852
4463
|
const session = this.sessions.get(sessionId);
|
|
3853
4464
|
if (!session) return;
|
|
3854
|
-
const logPath =
|
|
4465
|
+
const logPath = path5.join(
|
|
3855
4466
|
this.localCachePath,
|
|
3856
4467
|
"sessions",
|
|
3857
4468
|
session.context.runId,
|
|
3858
4469
|
"logs.ndjson"
|
|
3859
4470
|
);
|
|
3860
4471
|
try {
|
|
3861
|
-
|
|
4472
|
+
fs4.appendFileSync(logPath, `${JSON.stringify(entry)}
|
|
3862
4473
|
`);
|
|
3863
4474
|
} catch (error) {
|
|
3864
4475
|
this.logger.warn("Failed to write to local cache", {
|