@posthog/agent 2.1.131 → 2.1.138
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 +118 -165
- 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/session/jsonl-hydration.d.ts +45 -0
- package/dist/adapters/claude/session/jsonl-hydration.js +444 -0
- package/dist/adapters/claude/session/jsonl-hydration.js.map +1 -0
- package/dist/adapters/claude/tools.js +21 -11
- package/dist/adapters/claude/tools.js.map +1 -1
- package/dist/agent.d.ts +2 -0
- package/dist/agent.js +1261 -608
- package/dist/agent.js.map +1 -1
- package/dist/posthog-api.js +6 -2
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.js +1307 -657
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +1285 -637
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +8 -4
- 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 +176 -150
- 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/jsonl-hydration.test.ts +903 -0
- package/src/adapters/claude/session/jsonl-hydration.ts +581 -0
- 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/adapters/codex/spawn.ts +1 -1
- package/src/agent.ts +4 -0
- package/src/execution-mode.ts +26 -10
- package/src/server/agent-server.test.ts +41 -1
- 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.138",
|
|
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: {
|
|
@@ -316,6 +319,10 @@ var package_default = {
|
|
|
316
319
|
types: "./dist/adapters/claude/conversion/tool-use-to-acp.d.ts",
|
|
317
320
|
import: "./dist/adapters/claude/conversion/tool-use-to-acp.js"
|
|
318
321
|
},
|
|
322
|
+
"./adapters/claude/session/jsonl-hydration": {
|
|
323
|
+
types: "./dist/adapters/claude/session/jsonl-hydration.d.ts",
|
|
324
|
+
import: "./dist/adapters/claude/session/jsonl-hydration.js"
|
|
325
|
+
},
|
|
319
326
|
"./server": {
|
|
320
327
|
types: "./dist/server/agent-server.d.ts",
|
|
321
328
|
import: "./dist/server/agent-server.js"
|
|
@@ -352,7 +359,6 @@ var package_default = {
|
|
|
352
359
|
"@twig/git": "workspace:*",
|
|
353
360
|
"@types/bun": "latest",
|
|
354
361
|
"@types/tar": "^6.1.13",
|
|
355
|
-
minimatch: "^10.0.3",
|
|
356
362
|
msw: "^2.12.7",
|
|
357
363
|
tsup: "^8.5.1",
|
|
358
364
|
tsx: "^4.20.6",
|
|
@@ -373,6 +379,7 @@ var package_default = {
|
|
|
373
379
|
commander: "^14.0.2",
|
|
374
380
|
hono: "^4.11.7",
|
|
375
381
|
jsonwebtoken: "^9.0.2",
|
|
382
|
+
minimatch: "^10.0.3",
|
|
376
383
|
tar: "^7.5.0",
|
|
377
384
|
uuid: "13.0.0",
|
|
378
385
|
"yoga-wasm-web": "^0.3.3",
|
|
@@ -406,7 +413,7 @@ function unreachable(value, logger) {
|
|
|
406
413
|
try {
|
|
407
414
|
valueAsString = JSON.stringify(value);
|
|
408
415
|
} catch {
|
|
409
|
-
valueAsString = value;
|
|
416
|
+
valueAsString = String(value);
|
|
410
417
|
}
|
|
411
418
|
logger.error(`Unexpected case: ${valueAsString}`);
|
|
412
419
|
}
|
|
@@ -514,19 +521,20 @@ var BaseAcpAgent = class {
|
|
|
514
521
|
}
|
|
515
522
|
async cancel(params) {
|
|
516
523
|
if (this.sessionId !== params.sessionId) {
|
|
517
|
-
throw new Error("Session
|
|
524
|
+
throw new Error("Session ID mismatch");
|
|
518
525
|
}
|
|
519
526
|
this.session.cancelled = true;
|
|
520
527
|
const meta = params._meta;
|
|
521
528
|
if (meta?.interruptReason) {
|
|
522
529
|
this.session.interruptReason = meta.interruptReason;
|
|
523
530
|
}
|
|
524
|
-
await this.
|
|
531
|
+
await this.interrupt();
|
|
525
532
|
}
|
|
526
533
|
async closeSession() {
|
|
527
534
|
try {
|
|
528
535
|
this.session.abortController.abort();
|
|
529
536
|
await this.cancel({ sessionId: this.sessionId });
|
|
537
|
+
this.session.settingsManager.dispose();
|
|
530
538
|
this.logger.info("Closed session", { sessionId: this.sessionId });
|
|
531
539
|
} catch (err) {
|
|
532
540
|
this.logger.warn("Failed to close session", {
|
|
@@ -701,8 +709,8 @@ var ToolContentBuilder = class {
|
|
|
701
709
|
this.items.push({ type: "content", content: image(data, mimeType, uri) });
|
|
702
710
|
return this;
|
|
703
711
|
}
|
|
704
|
-
diff(
|
|
705
|
-
this.items.push({ type: "diff", path:
|
|
712
|
+
diff(path6, oldText, newText) {
|
|
713
|
+
this.items.push({ type: "diff", path: path6, oldText, newText });
|
|
706
714
|
return this;
|
|
707
715
|
}
|
|
708
716
|
build() {
|
|
@@ -722,7 +730,7 @@ var registerHookCallback = (toolUseID, {
|
|
|
722
730
|
onPostToolUseHook
|
|
723
731
|
};
|
|
724
732
|
};
|
|
725
|
-
var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
|
|
733
|
+
var createPostToolUseHook = ({ onModeChange, logger }) => async (input, toolUseID) => {
|
|
726
734
|
if (input.hook_event_name === "PostToolUse") {
|
|
727
735
|
const toolName = input.tool_name;
|
|
728
736
|
if (onModeChange && toolName === "EnterPlanMode") {
|
|
@@ -737,11 +745,54 @@ var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
|
|
|
737
745
|
input.tool_response
|
|
738
746
|
);
|
|
739
747
|
delete toolUseCallbacks[toolUseID];
|
|
748
|
+
} else {
|
|
749
|
+
logger?.error(
|
|
750
|
+
`No onPostToolUseHook found for tool use ID: ${toolUseID}`
|
|
751
|
+
);
|
|
752
|
+
delete toolUseCallbacks[toolUseID];
|
|
740
753
|
}
|
|
741
754
|
}
|
|
742
755
|
}
|
|
743
756
|
return { continue: true };
|
|
744
757
|
};
|
|
758
|
+
var createPreToolUseHook = (settingsManager, logger) => async (input, _toolUseID) => {
|
|
759
|
+
if (input.hook_event_name !== "PreToolUse") {
|
|
760
|
+
return { continue: true };
|
|
761
|
+
}
|
|
762
|
+
const toolName = input.tool_name;
|
|
763
|
+
const toolInput = input.tool_input;
|
|
764
|
+
const permissionCheck = settingsManager.checkPermission(
|
|
765
|
+
toolName,
|
|
766
|
+
toolInput
|
|
767
|
+
);
|
|
768
|
+
if (permissionCheck.decision !== "ask") {
|
|
769
|
+
logger.info(
|
|
770
|
+
`[PreToolUseHook] Tool: ${toolName}, Decision: ${permissionCheck.decision}, Rule: ${permissionCheck.rule}`
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
switch (permissionCheck.decision) {
|
|
774
|
+
case "allow":
|
|
775
|
+
return {
|
|
776
|
+
continue: true,
|
|
777
|
+
hookSpecificOutput: {
|
|
778
|
+
hookEventName: "PreToolUse",
|
|
779
|
+
permissionDecision: "allow",
|
|
780
|
+
permissionDecisionReason: `Allowed by settings rule: ${permissionCheck.rule}`
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
case "deny":
|
|
784
|
+
return {
|
|
785
|
+
continue: true,
|
|
786
|
+
hookSpecificOutput: {
|
|
787
|
+
hookEventName: "PreToolUse",
|
|
788
|
+
permissionDecision: "deny",
|
|
789
|
+
permissionDecisionReason: `Denied by settings rule: ${permissionCheck.rule}`
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
default:
|
|
793
|
+
return { continue: true };
|
|
794
|
+
}
|
|
795
|
+
};
|
|
745
796
|
|
|
746
797
|
// src/adapters/claude/mcp/tool-metadata.ts
|
|
747
798
|
var mcpToolMetadataCache = /* @__PURE__ */ new Map();
|
|
@@ -818,85 +869,14 @@ var SYSTEM_REMINDER = `
|
|
|
818
869
|
<system-reminder>
|
|
819
870
|
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
871
|
</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]" })) {
|
|
872
|
+
function toolInfoFromToolUse(toolUse, options) {
|
|
894
873
|
const name = toolUse.name;
|
|
895
874
|
const input = toolUse.input;
|
|
896
875
|
switch (name) {
|
|
897
876
|
case "Task":
|
|
877
|
+
case "Agent":
|
|
898
878
|
return {
|
|
899
|
-
title: input?.description ? String(input.description) :
|
|
879
|
+
title: input?.description ? String(input.description) : name,
|
|
900
880
|
kind: "think",
|
|
901
881
|
content: input?.prompt ? toolContent().text(String(input.prompt)).build() : []
|
|
902
882
|
};
|
|
@@ -915,6 +895,13 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
915
895
|
locations: input?.notebook_path ? [{ path: String(input.notebook_path) }] : []
|
|
916
896
|
};
|
|
917
897
|
case "Bash":
|
|
898
|
+
if (options?.supportsTerminalOutput && options?.toolUseId) {
|
|
899
|
+
return {
|
|
900
|
+
title: input?.description ? String(input.description) : "Execute command",
|
|
901
|
+
kind: "execute",
|
|
902
|
+
content: [{ type: "terminal", terminalId: options.toolUseId }]
|
|
903
|
+
};
|
|
904
|
+
}
|
|
918
905
|
return {
|
|
919
906
|
title: input?.description ? String(input.description) : "Execute command",
|
|
920
907
|
kind: "execute",
|
|
@@ -935,11 +922,11 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
935
922
|
case "Read": {
|
|
936
923
|
let limit = "";
|
|
937
924
|
const inputLimit = input?.limit;
|
|
938
|
-
const inputOffset = input?.offset ??
|
|
925
|
+
const inputOffset = input?.offset ?? 1;
|
|
939
926
|
if (inputLimit) {
|
|
940
|
-
limit = ` (${inputOffset
|
|
941
|
-
} else if (inputOffset) {
|
|
942
|
-
limit = ` (from line ${inputOffset
|
|
927
|
+
limit = ` (${inputOffset} - ${inputOffset + inputLimit - 1})`;
|
|
928
|
+
} else if (inputOffset > 1) {
|
|
929
|
+
limit = ` (from line ${inputOffset})`;
|
|
943
930
|
}
|
|
944
931
|
return {
|
|
945
932
|
title: `Read ${input?.file_path ? String(input.file_path) : "File"}${limit}`,
|
|
@@ -961,39 +948,21 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
961
948
|
locations: []
|
|
962
949
|
};
|
|
963
950
|
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
|
-
}
|
|
951
|
+
const path6 = input?.file_path ? String(input.file_path) : void 0;
|
|
952
|
+
const oldText = input?.old_string ? String(input.old_string) : null;
|
|
953
|
+
const newText = input?.new_string ? String(input.new_string) : "";
|
|
985
954
|
return {
|
|
986
|
-
title:
|
|
955
|
+
title: path6 ? `Edit \`${path6}\`` : "Edit",
|
|
987
956
|
kind: "edit",
|
|
988
|
-
content: input &&
|
|
957
|
+
content: input && path6 ? [
|
|
989
958
|
{
|
|
990
959
|
type: "diff",
|
|
991
|
-
path:
|
|
960
|
+
path: path6,
|
|
992
961
|
oldText,
|
|
993
962
|
newText
|
|
994
963
|
}
|
|
995
964
|
] : [],
|
|
996
|
-
locations:
|
|
965
|
+
locations: path6 ? [{ path: path6 }] : []
|
|
997
966
|
};
|
|
998
967
|
}
|
|
999
968
|
case "Write": {
|
|
@@ -1047,10 +1016,10 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1047
1016
|
}
|
|
1048
1017
|
if (input?.output_mode) {
|
|
1049
1018
|
switch (input.output_mode) {
|
|
1050
|
-
case "
|
|
1019
|
+
case "files_with_matches":
|
|
1051
1020
|
label += " -l";
|
|
1052
1021
|
break;
|
|
1053
|
-
case "
|
|
1022
|
+
case "count":
|
|
1054
1023
|
label += " -c";
|
|
1055
1024
|
break;
|
|
1056
1025
|
default:
|
|
@@ -1069,7 +1038,9 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1069
1038
|
if (input?.multiline) {
|
|
1070
1039
|
label += " -P";
|
|
1071
1040
|
}
|
|
1072
|
-
|
|
1041
|
+
if (input?.pattern) {
|
|
1042
|
+
label += ` "${String(input.pattern)}"`;
|
|
1043
|
+
}
|
|
1073
1044
|
if (input?.path) {
|
|
1074
1045
|
label += ` ${String(input.path)}`;
|
|
1075
1046
|
}
|
|
@@ -1163,7 +1134,49 @@ function mcpToolInfo(name, _input) {
|
|
|
1163
1134
|
content: []
|
|
1164
1135
|
};
|
|
1165
1136
|
}
|
|
1166
|
-
function
|
|
1137
|
+
function toolUpdateFromEditToolResponse(toolResponse) {
|
|
1138
|
+
if (!toolResponse || typeof toolResponse !== "object") return null;
|
|
1139
|
+
const response = toolResponse;
|
|
1140
|
+
const patches = response.structuredPatch;
|
|
1141
|
+
if (!Array.isArray(patches) || patches.length === 0) return null;
|
|
1142
|
+
const content = [];
|
|
1143
|
+
const locations = [];
|
|
1144
|
+
for (const patch of patches) {
|
|
1145
|
+
if (!patch.hunks || patch.hunks.length === 0) continue;
|
|
1146
|
+
const filePath = patch.newFileName || patch.oldFileName;
|
|
1147
|
+
const oldLines = [];
|
|
1148
|
+
const newLines = [];
|
|
1149
|
+
for (const hunk of patch.hunks) {
|
|
1150
|
+
for (const line of hunk.lines) {
|
|
1151
|
+
if (line.startsWith("-")) {
|
|
1152
|
+
oldLines.push(line.slice(1));
|
|
1153
|
+
} else if (line.startsWith("+")) {
|
|
1154
|
+
newLines.push(line.slice(1));
|
|
1155
|
+
} else if (line.startsWith(" ")) {
|
|
1156
|
+
oldLines.push(line.slice(1));
|
|
1157
|
+
newLines.push(line.slice(1));
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
content.push({
|
|
1162
|
+
type: "diff",
|
|
1163
|
+
path: filePath,
|
|
1164
|
+
oldText: oldLines.join("\n"),
|
|
1165
|
+
newText: newLines.join("\n")
|
|
1166
|
+
});
|
|
1167
|
+
const firstHunk = patch.hunks[0];
|
|
1168
|
+
locations.push({
|
|
1169
|
+
path: filePath,
|
|
1170
|
+
line: firstHunk.newStart
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
if (content.length === 0) return null;
|
|
1174
|
+
return { content, locations };
|
|
1175
|
+
}
|
|
1176
|
+
function toolUpdateFromToolResult(toolResult, toolUse, options) {
|
|
1177
|
+
if ("is_error" in toolResult && toolResult.is_error && toolResult.content && toolResult.content.length > 0) {
|
|
1178
|
+
return toAcpContentUpdate(toolResult.content, true);
|
|
1179
|
+
}
|
|
1167
1180
|
switch (toolUse?.name) {
|
|
1168
1181
|
case "Read":
|
|
1169
1182
|
if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
|
|
@@ -1180,6 +1193,16 @@ function toolUpdateFromToolResult(toolResult, toolUse) {
|
|
|
1180
1193
|
)
|
|
1181
1194
|
};
|
|
1182
1195
|
}
|
|
1196
|
+
if (itemObj.type === "image" && itemObj.source) {
|
|
1197
|
+
return {
|
|
1198
|
+
type: "content",
|
|
1199
|
+
content: {
|
|
1200
|
+
type: "image",
|
|
1201
|
+
data: itemObj.source.data ?? "",
|
|
1202
|
+
mimeType: itemObj.source.media_type ?? "image/png"
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1183
1206
|
return {
|
|
1184
1207
|
type: "content",
|
|
1185
1208
|
content: item
|
|
@@ -1195,18 +1218,51 @@ function toolUpdateFromToolResult(toolResult, toolUse) {
|
|
|
1195
1218
|
}
|
|
1196
1219
|
return {};
|
|
1197
1220
|
case "Bash": {
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1221
|
+
const result = toolResult.content;
|
|
1222
|
+
const terminalId = "tool_use_id" in toolResult ? String(toolResult.tool_use_id) : "";
|
|
1223
|
+
const isError = "is_error" in toolResult && toolResult.is_error;
|
|
1224
|
+
let output = "";
|
|
1225
|
+
let exitCode = isError ? 1 : 0;
|
|
1226
|
+
if (result && typeof result === "object" && "type" in result && result.type === "bash_code_execution_result") {
|
|
1227
|
+
const bashResult = result;
|
|
1228
|
+
output = [bashResult.stdout, bashResult.stderr].filter(Boolean).join("\n");
|
|
1229
|
+
exitCode = bashResult.return_code;
|
|
1230
|
+
} else if (typeof result === "string") {
|
|
1231
|
+
output = result;
|
|
1232
|
+
} else if (Array.isArray(result) && result.length > 0 && "text" in result[0] && typeof result[0].text === "string") {
|
|
1233
|
+
output = result.map((c) => c.text ?? "").join("\n");
|
|
1234
|
+
}
|
|
1235
|
+
if (options?.supportsTerminalOutput) {
|
|
1236
|
+
return {
|
|
1237
|
+
content: [{ type: "terminal", terminalId }],
|
|
1238
|
+
_meta: {
|
|
1239
|
+
terminal_info: {
|
|
1240
|
+
terminal_id: terminalId
|
|
1241
|
+
},
|
|
1242
|
+
terminal_output: {
|
|
1243
|
+
terminal_id: terminalId,
|
|
1244
|
+
data: output
|
|
1245
|
+
},
|
|
1246
|
+
terminal_exit: {
|
|
1247
|
+
terminal_id: terminalId,
|
|
1248
|
+
exit_code: exitCode,
|
|
1249
|
+
signal: null
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
if (output.trim()) {
|
|
1255
|
+
return {
|
|
1256
|
+
content: toolContent().text(`\`\`\`console
|
|
1257
|
+
${output.trimEnd()}
|
|
1258
|
+
\`\`\``).build()
|
|
1259
|
+
};
|
|
1207
1260
|
}
|
|
1208
1261
|
return {};
|
|
1209
1262
|
}
|
|
1263
|
+
case "Edit":
|
|
1264
|
+
case "Write":
|
|
1265
|
+
return {};
|
|
1210
1266
|
case "ExitPlanMode": {
|
|
1211
1267
|
return { title: "Exited Plan Mode" };
|
|
1212
1268
|
}
|
|
@@ -1366,6 +1422,7 @@ function handleThinkingChunk(chunk, parentToolCallId) {
|
|
|
1366
1422
|
return update;
|
|
1367
1423
|
}
|
|
1368
1424
|
function handleToolUseChunk(chunk, ctx) {
|
|
1425
|
+
const alreadyCached = chunk.id in ctx.toolUseCache;
|
|
1369
1426
|
ctx.toolUseCache[chunk.id] = chunk;
|
|
1370
1427
|
if (chunk.name === "TodoWrite") {
|
|
1371
1428
|
const input = chunk.input;
|
|
@@ -1377,37 +1434,60 @@ function handleToolUseChunk(chunk, ctx) {
|
|
|
1377
1434
|
}
|
|
1378
1435
|
return null;
|
|
1379
1436
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1437
|
+
if (!alreadyCached && ctx.registerHooks !== false) {
|
|
1438
|
+
registerHookCallback(chunk.id, {
|
|
1439
|
+
onPostToolUseHook: async (toolUseId, _toolInput, toolResponse) => {
|
|
1440
|
+
const toolUse = ctx.toolUseCache[toolUseId];
|
|
1441
|
+
if (toolUse) {
|
|
1442
|
+
const editUpdate = toolUse.name === "Edit" ? toolUpdateFromEditToolResponse(toolResponse) : null;
|
|
1443
|
+
await ctx.client.sessionUpdate({
|
|
1444
|
+
sessionId: ctx.sessionId,
|
|
1445
|
+
update: {
|
|
1446
|
+
_meta: toolMeta(toolUse.name, toolResponse, ctx.parentToolCallId),
|
|
1447
|
+
toolCallId: toolUseId,
|
|
1448
|
+
sessionUpdate: "tool_call_update",
|
|
1449
|
+
...editUpdate ? editUpdate : {}
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1452
|
+
} else {
|
|
1453
|
+
ctx.logger.error(
|
|
1454
|
+
`Got a tool response for tool use that wasn't tracked: ${toolUseId}`
|
|
1455
|
+
);
|
|
1456
|
+
}
|
|
1396
1457
|
}
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1399
1460
|
let rawInput;
|
|
1400
1461
|
try {
|
|
1401
1462
|
rawInput = JSON.parse(JSON.stringify(chunk.input));
|
|
1402
1463
|
} catch {
|
|
1403
1464
|
}
|
|
1465
|
+
const toolInfo = toolInfoFromToolUse(chunk, {
|
|
1466
|
+
supportsTerminalOutput: ctx.supportsTerminalOutput,
|
|
1467
|
+
toolUseId: chunk.id
|
|
1468
|
+
});
|
|
1469
|
+
const meta = {
|
|
1470
|
+
...toolMeta(chunk.name, void 0, ctx.parentToolCallId)
|
|
1471
|
+
};
|
|
1472
|
+
if (chunk.name === "Bash" && ctx.supportsTerminalOutput && !alreadyCached) {
|
|
1473
|
+
meta.terminal_info = { terminal_id: chunk.id };
|
|
1474
|
+
}
|
|
1475
|
+
if (alreadyCached) {
|
|
1476
|
+
return {
|
|
1477
|
+
_meta: meta,
|
|
1478
|
+
toolCallId: chunk.id,
|
|
1479
|
+
sessionUpdate: "tool_call_update",
|
|
1480
|
+
rawInput,
|
|
1481
|
+
...toolInfo
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1404
1484
|
return {
|
|
1405
|
-
_meta:
|
|
1485
|
+
_meta: meta,
|
|
1406
1486
|
toolCallId: chunk.id,
|
|
1407
1487
|
sessionUpdate: "tool_call",
|
|
1408
1488
|
rawInput,
|
|
1409
1489
|
status: "pending",
|
|
1410
|
-
...
|
|
1490
|
+
...toolInfo
|
|
1411
1491
|
};
|
|
1412
1492
|
}
|
|
1413
1493
|
function handleToolResultChunk(chunk, ctx) {
|
|
@@ -1416,36 +1496,71 @@ function handleToolResultChunk(chunk, ctx) {
|
|
|
1416
1496
|
ctx.logger.error(
|
|
1417
1497
|
`Got a tool result for tool use that wasn't tracked: ${chunk.tool_use_id}`
|
|
1418
1498
|
);
|
|
1419
|
-
return
|
|
1499
|
+
return [];
|
|
1420
1500
|
}
|
|
1421
1501
|
if (toolUse.name === "TodoWrite") {
|
|
1422
|
-
return
|
|
1502
|
+
return [];
|
|
1423
1503
|
}
|
|
1424
|
-
|
|
1425
|
-
|
|
1504
|
+
const { _meta: resultMeta, ...toolUpdate } = toolUpdateFromToolResult(
|
|
1505
|
+
chunk,
|
|
1506
|
+
toolUse,
|
|
1507
|
+
{
|
|
1508
|
+
supportsTerminalOutput: ctx.supportsTerminalOutput,
|
|
1509
|
+
toolUseId: chunk.tool_use_id
|
|
1510
|
+
}
|
|
1511
|
+
);
|
|
1512
|
+
const updates = [];
|
|
1513
|
+
if (resultMeta?.terminal_output) {
|
|
1514
|
+
const terminalOutputMeta = {
|
|
1515
|
+
terminal_output: resultMeta.terminal_output
|
|
1516
|
+
};
|
|
1517
|
+
if (ctx.parentToolCallId) {
|
|
1518
|
+
terminalOutputMeta.claudeCode = {
|
|
1519
|
+
parentToolCallId: ctx.parentToolCallId
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
updates.push({
|
|
1523
|
+
_meta: terminalOutputMeta,
|
|
1524
|
+
toolCallId: chunk.tool_use_id,
|
|
1525
|
+
sessionUpdate: "tool_call_update"
|
|
1526
|
+
});
|
|
1527
|
+
}
|
|
1528
|
+
const meta = {
|
|
1529
|
+
...toolMeta(toolUse.name, void 0, ctx.parentToolCallId),
|
|
1530
|
+
...resultMeta?.terminal_exit ? { terminal_exit: resultMeta.terminal_exit } : {}
|
|
1531
|
+
};
|
|
1532
|
+
updates.push({
|
|
1533
|
+
_meta: meta,
|
|
1426
1534
|
toolCallId: chunk.tool_use_id,
|
|
1427
1535
|
sessionUpdate: "tool_call_update",
|
|
1428
1536
|
status: chunk.is_error ? "failed" : "completed",
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
};
|
|
1537
|
+
rawOutput: chunk.content,
|
|
1538
|
+
...toolUpdate
|
|
1539
|
+
});
|
|
1540
|
+
return updates;
|
|
1434
1541
|
}
|
|
1435
1542
|
function processContentChunk(chunk, role, ctx) {
|
|
1436
1543
|
switch (chunk.type) {
|
|
1437
1544
|
case "text":
|
|
1438
|
-
case "text_delta":
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1545
|
+
case "text_delta": {
|
|
1546
|
+
const update = handleTextChunk(chunk, role, ctx.parentToolCallId);
|
|
1547
|
+
return update ? [update] : [];
|
|
1548
|
+
}
|
|
1549
|
+
case "image": {
|
|
1550
|
+
const update = handleImageChunk(chunk, role);
|
|
1551
|
+
return update ? [update] : [];
|
|
1552
|
+
}
|
|
1442
1553
|
case "thinking":
|
|
1443
|
-
case "thinking_delta":
|
|
1444
|
-
|
|
1554
|
+
case "thinking_delta": {
|
|
1555
|
+
const update = handleThinkingChunk(chunk, ctx.parentToolCallId);
|
|
1556
|
+
return update ? [update] : [];
|
|
1557
|
+
}
|
|
1445
1558
|
case "tool_use":
|
|
1446
1559
|
case "server_tool_use":
|
|
1447
|
-
case "mcp_tool_use":
|
|
1448
|
-
|
|
1560
|
+
case "mcp_tool_use": {
|
|
1561
|
+
const update = handleToolUseChunk(chunk, ctx);
|
|
1562
|
+
return update ? [update] : [];
|
|
1563
|
+
}
|
|
1449
1564
|
case "tool_result":
|
|
1450
1565
|
case "tool_search_tool_result":
|
|
1451
1566
|
case "web_fetch_tool_result":
|
|
@@ -1467,13 +1582,13 @@ function processContentChunk(chunk, role, ctx) {
|
|
|
1467
1582
|
case "container_upload":
|
|
1468
1583
|
case "compaction":
|
|
1469
1584
|
case "compaction_delta":
|
|
1470
|
-
return
|
|
1585
|
+
return [];
|
|
1471
1586
|
default:
|
|
1472
1587
|
unreachable(chunk, ctx.logger);
|
|
1473
|
-
return
|
|
1588
|
+
return [];
|
|
1474
1589
|
}
|
|
1475
1590
|
}
|
|
1476
|
-
function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId) {
|
|
1591
|
+
function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput) {
|
|
1477
1592
|
if (typeof content === "string") {
|
|
1478
1593
|
const update = {
|
|
1479
1594
|
sessionUpdate: messageUpdateType(role),
|
|
@@ -1494,18 +1609,19 @@ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentC
|
|
|
1494
1609
|
fileContentCache,
|
|
1495
1610
|
client,
|
|
1496
1611
|
logger,
|
|
1497
|
-
parentToolCallId
|
|
1612
|
+
parentToolCallId,
|
|
1613
|
+
registerHooks,
|
|
1614
|
+
supportsTerminalOutput
|
|
1498
1615
|
};
|
|
1499
1616
|
const output = [];
|
|
1500
1617
|
for (const chunk of content) {
|
|
1501
|
-
const update
|
|
1502
|
-
if (update) {
|
|
1618
|
+
for (const update of processContentChunk(chunk, role, ctx)) {
|
|
1503
1619
|
output.push({ sessionId, update });
|
|
1504
1620
|
}
|
|
1505
1621
|
}
|
|
1506
1622
|
return output;
|
|
1507
1623
|
}
|
|
1508
|
-
function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId) {
|
|
1624
|
+
function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput) {
|
|
1509
1625
|
const event = message.event;
|
|
1510
1626
|
switch (event.type) {
|
|
1511
1627
|
case "content_block_start":
|
|
@@ -1517,7 +1633,9 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
|
|
|
1517
1633
|
fileContentCache,
|
|
1518
1634
|
client,
|
|
1519
1635
|
logger,
|
|
1520
|
-
parentToolCallId
|
|
1636
|
+
parentToolCallId,
|
|
1637
|
+
registerHooks,
|
|
1638
|
+
supportsTerminalOutput
|
|
1521
1639
|
);
|
|
1522
1640
|
case "content_block_delta":
|
|
1523
1641
|
return toAcpNotifications(
|
|
@@ -1528,7 +1646,9 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
|
|
|
1528
1646
|
fileContentCache,
|
|
1529
1647
|
client,
|
|
1530
1648
|
logger,
|
|
1531
|
-
parentToolCallId
|
|
1649
|
+
parentToolCallId,
|
|
1650
|
+
registerHooks,
|
|
1651
|
+
supportsTerminalOutput
|
|
1532
1652
|
);
|
|
1533
1653
|
case "message_start":
|
|
1534
1654
|
case "message_delta":
|
|
@@ -1587,29 +1707,25 @@ async function handleSystemMessage(message, context) {
|
|
|
1587
1707
|
break;
|
|
1588
1708
|
}
|
|
1589
1709
|
}
|
|
1590
|
-
function handleResultMessage(message
|
|
1591
|
-
const
|
|
1592
|
-
if (session.cancelled) {
|
|
1593
|
-
return {
|
|
1594
|
-
shouldStop: true,
|
|
1595
|
-
stopReason: "cancelled"
|
|
1596
|
-
};
|
|
1597
|
-
}
|
|
1710
|
+
function handleResultMessage(message) {
|
|
1711
|
+
const usage = extractUsageFromResult(message);
|
|
1598
1712
|
switch (message.subtype) {
|
|
1599
1713
|
case "success": {
|
|
1600
1714
|
if (message.result.includes("Please run /login")) {
|
|
1601
1715
|
return {
|
|
1602
1716
|
shouldStop: true,
|
|
1603
|
-
error: RequestError.authRequired()
|
|
1717
|
+
error: RequestError.authRequired(),
|
|
1718
|
+
usage
|
|
1604
1719
|
};
|
|
1605
1720
|
}
|
|
1606
1721
|
if (message.is_error) {
|
|
1607
1722
|
return {
|
|
1608
1723
|
shouldStop: true,
|
|
1609
|
-
error: RequestError.internalError(void 0, message.result)
|
|
1724
|
+
error: RequestError.internalError(void 0, message.result),
|
|
1725
|
+
usage
|
|
1610
1726
|
};
|
|
1611
1727
|
}
|
|
1612
|
-
return { shouldStop: true, stopReason: "end_turn" };
|
|
1728
|
+
return { shouldStop: true, stopReason: "end_turn", usage };
|
|
1613
1729
|
}
|
|
1614
1730
|
case "error_during_execution":
|
|
1615
1731
|
if (message.is_error) {
|
|
@@ -1618,10 +1734,11 @@ function handleResultMessage(message, context) {
|
|
|
1618
1734
|
error: RequestError.internalError(
|
|
1619
1735
|
void 0,
|
|
1620
1736
|
message.errors.join(", ") || message.subtype
|
|
1621
|
-
)
|
|
1737
|
+
),
|
|
1738
|
+
usage
|
|
1622
1739
|
};
|
|
1623
1740
|
}
|
|
1624
|
-
return { shouldStop: true, stopReason: "end_turn" };
|
|
1741
|
+
return { shouldStop: true, stopReason: "end_turn", usage };
|
|
1625
1742
|
case "error_max_budget_usd":
|
|
1626
1743
|
case "error_max_turns":
|
|
1627
1744
|
case "error_max_structured_output_retries":
|
|
@@ -1631,13 +1748,37 @@ function handleResultMessage(message, context) {
|
|
|
1631
1748
|
error: RequestError.internalError(
|
|
1632
1749
|
void 0,
|
|
1633
1750
|
message.errors.join(", ") || message.subtype
|
|
1634
|
-
)
|
|
1751
|
+
),
|
|
1752
|
+
usage
|
|
1635
1753
|
};
|
|
1636
1754
|
}
|
|
1637
|
-
return { shouldStop: true, stopReason: "max_turn_requests" };
|
|
1755
|
+
return { shouldStop: true, stopReason: "max_turn_requests", usage };
|
|
1638
1756
|
default:
|
|
1639
|
-
return { shouldStop: false };
|
|
1757
|
+
return { shouldStop: false, usage };
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
function extractUsageFromResult(message) {
|
|
1761
|
+
const msg = message;
|
|
1762
|
+
const msgUsage = msg.usage;
|
|
1763
|
+
if (!msgUsage) return void 0;
|
|
1764
|
+
const modelUsage = msg.modelUsage;
|
|
1765
|
+
let contextWindowSize;
|
|
1766
|
+
if (modelUsage) {
|
|
1767
|
+
const contextWindows = Object.values(modelUsage).map(
|
|
1768
|
+
(m) => m.contextWindow
|
|
1769
|
+
);
|
|
1770
|
+
if (contextWindows.length > 0) {
|
|
1771
|
+
contextWindowSize = Math.min(...contextWindows);
|
|
1772
|
+
}
|
|
1640
1773
|
}
|
|
1774
|
+
return {
|
|
1775
|
+
inputTokens: msgUsage.input_tokens ?? 0,
|
|
1776
|
+
outputTokens: msgUsage.output_tokens ?? 0,
|
|
1777
|
+
cachedReadTokens: msgUsage.cache_read_input_tokens ?? 0,
|
|
1778
|
+
cachedWriteTokens: msgUsage.cache_creation_input_tokens ?? 0,
|
|
1779
|
+
costUsd: typeof msg.total_cost_usd === "number" ? msg.total_cost_usd : void 0,
|
|
1780
|
+
contextWindowSize
|
|
1781
|
+
};
|
|
1641
1782
|
}
|
|
1642
1783
|
async function handleStreamEvent(message, context) {
|
|
1643
1784
|
const { sessionId, client, toolUseCache, fileContentCache, logger } = context;
|
|
@@ -1649,7 +1790,9 @@ async function handleStreamEvent(message, context) {
|
|
|
1649
1790
|
fileContentCache,
|
|
1650
1791
|
client,
|
|
1651
1792
|
logger,
|
|
1652
|
-
parentToolCallId
|
|
1793
|
+
parentToolCallId,
|
|
1794
|
+
context.registerHooks,
|
|
1795
|
+
context.supportsTerminalOutput
|
|
1653
1796
|
)) {
|
|
1654
1797
|
await client.sessionUpdate(notification);
|
|
1655
1798
|
context.session.notificationHistory.push(notification);
|
|
@@ -1661,14 +1804,15 @@ function hasLocalCommandStdout(content) {
|
|
|
1661
1804
|
function hasLocalCommandStderr(content) {
|
|
1662
1805
|
return typeof content === "string" && content.includes("<local-command-stderr>");
|
|
1663
1806
|
}
|
|
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
1807
|
function isLoginRequiredMessage(message) {
|
|
1668
1808
|
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
1809
|
}
|
|
1810
|
+
function isPlainTextUserMessage(message) {
|
|
1811
|
+
const content = message.message.content;
|
|
1812
|
+
return message.type === "user" && (typeof content === "string" || Array.isArray(content) && content.length === 1 && content[0].type === "text");
|
|
1813
|
+
}
|
|
1670
1814
|
function shouldSkipUserAssistantMessage(message) {
|
|
1671
|
-
return hasLocalCommandStdout(message.message.content) || hasLocalCommandStderr(message.message.content) ||
|
|
1815
|
+
return hasLocalCommandStdout(message.message.content) || hasLocalCommandStderr(message.message.content) || isLoginRequiredMessage(message);
|
|
1672
1816
|
}
|
|
1673
1817
|
function logSpecialMessages(message, logger) {
|
|
1674
1818
|
const content = message.message.content;
|
|
@@ -1689,18 +1833,33 @@ function filterMessageContent(content) {
|
|
|
1689
1833
|
}
|
|
1690
1834
|
async function handleUserAssistantMessage(message, context) {
|
|
1691
1835
|
const { session, sessionId, client, toolUseCache, fileContentCache, logger } = context;
|
|
1692
|
-
if (session.cancelled) {
|
|
1693
|
-
return {};
|
|
1694
|
-
}
|
|
1695
1836
|
if (shouldSkipUserAssistantMessage(message)) {
|
|
1837
|
+
const content2 = message.message.content;
|
|
1838
|
+
if (typeof content2 === "string" && hasLocalCommandStdout(content2) && content2.includes("Context Usage")) {
|
|
1839
|
+
const stripped = content2.replace("<local-command-stdout>", "").replace("</local-command-stdout>", "");
|
|
1840
|
+
for (const notification of toAcpNotifications(
|
|
1841
|
+
stripped,
|
|
1842
|
+
"assistant",
|
|
1843
|
+
sessionId,
|
|
1844
|
+
toolUseCache,
|
|
1845
|
+
fileContentCache,
|
|
1846
|
+
client,
|
|
1847
|
+
logger
|
|
1848
|
+
)) {
|
|
1849
|
+
await client.sessionUpdate(notification);
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1696
1852
|
logSpecialMessages(message, logger);
|
|
1697
1853
|
if (isLoginRequiredMessage(message)) {
|
|
1698
1854
|
return { shouldStop: true, error: RequestError.authRequired() };
|
|
1699
1855
|
}
|
|
1700
1856
|
return {};
|
|
1701
1857
|
}
|
|
1858
|
+
if (isPlainTextUserMessage(message)) {
|
|
1859
|
+
return {};
|
|
1860
|
+
}
|
|
1702
1861
|
const content = message.message.content;
|
|
1703
|
-
const contentToProcess = filterMessageContent(content);
|
|
1862
|
+
const contentToProcess = message.type === "assistant" ? filterMessageContent(content) : content;
|
|
1704
1863
|
const parentToolCallId = "parent_tool_use_id" in message ? message.parent_tool_use_id ?? void 0 : void 0;
|
|
1705
1864
|
for (const notification of toAcpNotifications(
|
|
1706
1865
|
contentToProcess,
|
|
@@ -1710,7 +1869,9 @@ async function handleUserAssistantMessage(message, context) {
|
|
|
1710
1869
|
fileContentCache,
|
|
1711
1870
|
client,
|
|
1712
1871
|
logger,
|
|
1713
|
-
parentToolCallId
|
|
1872
|
+
parentToolCallId,
|
|
1873
|
+
context.registerHooks,
|
|
1874
|
+
context.supportsTerminalOutput
|
|
1714
1875
|
)) {
|
|
1715
1876
|
await client.sessionUpdate(notification);
|
|
1716
1877
|
session.notificationHistory.push(notification);
|
|
@@ -1796,36 +1957,45 @@ function normalizeAskUserQuestionInput(input) {
|
|
|
1796
1957
|
}
|
|
1797
1958
|
|
|
1798
1959
|
// src/execution-mode.ts
|
|
1799
|
-
var
|
|
1960
|
+
var ALLOW_BYPASS = !IS_ROOT;
|
|
1961
|
+
var availableModes = [
|
|
1800
1962
|
{
|
|
1801
1963
|
id: "default",
|
|
1802
|
-
name: "
|
|
1803
|
-
description: "
|
|
1964
|
+
name: "Default",
|
|
1965
|
+
description: "Standard behavior, prompts for dangerous operations"
|
|
1804
1966
|
},
|
|
1805
1967
|
{
|
|
1806
1968
|
id: "acceptEdits",
|
|
1807
1969
|
name: "Accept Edits",
|
|
1808
|
-
description: "
|
|
1970
|
+
description: "Auto-accept file edit operations"
|
|
1809
1971
|
},
|
|
1810
1972
|
{
|
|
1811
1973
|
id: "plan",
|
|
1812
1974
|
name: "Plan Mode",
|
|
1813
|
-
description: "
|
|
1814
|
-
},
|
|
1815
|
-
{
|
|
1816
|
-
id: "bypassPermissions",
|
|
1817
|
-
name: "Bypass Permissions",
|
|
1818
|
-
description: "Skips all permission prompts"
|
|
1975
|
+
description: "Planning mode, no actual tool execution"
|
|
1819
1976
|
}
|
|
1977
|
+
// {
|
|
1978
|
+
// id: "dontAsk",
|
|
1979
|
+
// name: "Don't Ask",
|
|
1980
|
+
// description: "Don't prompt for permissions, deny if not pre-approved",
|
|
1981
|
+
// },
|
|
1820
1982
|
];
|
|
1983
|
+
if (ALLOW_BYPASS) {
|
|
1984
|
+
availableModes.push({
|
|
1985
|
+
id: "bypassPermissions",
|
|
1986
|
+
name: "Bypass Permissions",
|
|
1987
|
+
description: "Bypass all permission checks"
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1821
1990
|
var TWIG_EXECUTION_MODES = [
|
|
1822
1991
|
"default",
|
|
1823
1992
|
"acceptEdits",
|
|
1824
1993
|
"plan",
|
|
1994
|
+
// "dontAsk",
|
|
1825
1995
|
"bypassPermissions"
|
|
1826
1996
|
];
|
|
1827
1997
|
function getAvailableModes() {
|
|
1828
|
-
return IS_ROOT ?
|
|
1998
|
+
return IS_ROOT ? availableModes.filter((m) => m.id !== "bypassPermissions") : availableModes;
|
|
1829
1999
|
}
|
|
1830
2000
|
|
|
1831
2001
|
// src/adapters/claude/tools.ts
|
|
@@ -1853,6 +2023,7 @@ var AUTO_ALLOWED_TOOLS = {
|
|
|
1853
2023
|
default: new Set(BASE_ALLOWED_TOOLS),
|
|
1854
2024
|
acceptEdits: /* @__PURE__ */ new Set([...BASE_ALLOWED_TOOLS, ...WRITE_TOOLS]),
|
|
1855
2025
|
plan: new Set(BASE_ALLOWED_TOOLS)
|
|
2026
|
+
// dontAsk: new Set(BASE_ALLOWED_TOOLS),
|
|
1856
2027
|
};
|
|
1857
2028
|
function isToolAllowedForMode(toolName, mode) {
|
|
1858
2029
|
if (mode === "bypassPermissions") {
|
|
@@ -2000,12 +2171,11 @@ async function validatePlanContent(planText, context) {
|
|
|
2000
2171
|
return { valid: true };
|
|
2001
2172
|
}
|
|
2002
2173
|
async function requestPlanApproval(context, updatedInput) {
|
|
2003
|
-
const { client, sessionId, toolUseID
|
|
2004
|
-
const toolInfo = toolInfoFromToolUse(
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
);
|
|
2174
|
+
const { client, sessionId, toolUseID } = context;
|
|
2175
|
+
const toolInfo = toolInfoFromToolUse({
|
|
2176
|
+
name: context.toolName,
|
|
2177
|
+
input: updatedInput
|
|
2178
|
+
});
|
|
2009
2179
|
return await client.requestPermission({
|
|
2010
2180
|
options: buildExitPlanModePermissionOptions(),
|
|
2011
2181
|
sessionId,
|
|
@@ -2024,7 +2194,14 @@ async function applyPlanApproval(response, context, updatedInput) {
|
|
|
2024
2194
|
if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "default" || response.outcome.optionId === "acceptEdits")) {
|
|
2025
2195
|
session.permissionMode = response.outcome.optionId;
|
|
2026
2196
|
await session.query.setPermissionMode(response.outcome.optionId);
|
|
2027
|
-
await context.
|
|
2197
|
+
await context.client.sessionUpdate({
|
|
2198
|
+
sessionId: context.sessionId,
|
|
2199
|
+
update: {
|
|
2200
|
+
sessionUpdate: "current_mode_update",
|
|
2201
|
+
currentModeId: response.outcome.optionId
|
|
2202
|
+
}
|
|
2203
|
+
});
|
|
2204
|
+
await context.updateConfigOption("mode", response.outcome.optionId);
|
|
2028
2205
|
return {
|
|
2029
2206
|
behavior: "allow",
|
|
2030
2207
|
updatedInput,
|
|
@@ -2045,7 +2222,7 @@ async function handleEnterPlanModeTool(context) {
|
|
|
2045
2222
|
const { session, toolInput } = context;
|
|
2046
2223
|
session.permissionMode = "plan";
|
|
2047
2224
|
await session.query.setPermissionMode("plan");
|
|
2048
|
-
await context.
|
|
2225
|
+
await context.updateConfigOption("mode", "plan");
|
|
2049
2226
|
return {
|
|
2050
2227
|
behavior: "allow",
|
|
2051
2228
|
updatedInput: toolInput
|
|
@@ -2063,6 +2240,9 @@ async function handleExitPlanModeTool(context) {
|
|
|
2063
2240
|
return validationResult.error;
|
|
2064
2241
|
}
|
|
2065
2242
|
const response = await requestPlanApproval(context, updatedInput);
|
|
2243
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
2244
|
+
throw new Error("Tool use aborted");
|
|
2245
|
+
}
|
|
2066
2246
|
return await applyPlanApproval(response, context, updatedInput);
|
|
2067
2247
|
}
|
|
2068
2248
|
function buildQuestionOptions(question) {
|
|
@@ -2086,14 +2266,13 @@ async function handleAskUserQuestionTool(context) {
|
|
|
2086
2266
|
interrupt: true
|
|
2087
2267
|
};
|
|
2088
2268
|
}
|
|
2089
|
-
const { client, sessionId, toolUseID, toolInput
|
|
2269
|
+
const { client, sessionId, toolUseID, toolInput } = context;
|
|
2090
2270
|
const firstQuestion = questions[0];
|
|
2091
2271
|
const options = buildQuestionOptions(firstQuestion);
|
|
2092
|
-
const toolInfo = toolInfoFromToolUse(
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
);
|
|
2272
|
+
const toolInfo = toolInfoFromToolUse({
|
|
2273
|
+
name: context.toolName,
|
|
2274
|
+
input: toolInput
|
|
2275
|
+
});
|
|
2097
2276
|
const response = await client.requestPermission({
|
|
2098
2277
|
options,
|
|
2099
2278
|
sessionId,
|
|
@@ -2108,6 +2287,9 @@ async function handleAskUserQuestionTool(context) {
|
|
|
2108
2287
|
}
|
|
2109
2288
|
}
|
|
2110
2289
|
});
|
|
2290
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
2291
|
+
throw new Error("Tool use aborted");
|
|
2292
|
+
}
|
|
2111
2293
|
if (response.outcome?.outcome !== "selected") {
|
|
2112
2294
|
const customMessage = response._meta?.message;
|
|
2113
2295
|
return {
|
|
@@ -2140,14 +2322,9 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
2140
2322
|
toolUseID,
|
|
2141
2323
|
client,
|
|
2142
2324
|
sessionId,
|
|
2143
|
-
fileContentCache,
|
|
2144
2325
|
suggestions
|
|
2145
2326
|
} = context;
|
|
2146
|
-
const toolInfo = toolInfoFromToolUse(
|
|
2147
|
-
{ name: toolName, input: toolInput },
|
|
2148
|
-
fileContentCache,
|
|
2149
|
-
context.logger
|
|
2150
|
-
);
|
|
2327
|
+
const toolInfo = toolInfoFromToolUse({ name: toolName, input: toolInput });
|
|
2151
2328
|
const options = buildPermissionOptions(
|
|
2152
2329
|
toolName,
|
|
2153
2330
|
toolInput,
|
|
@@ -2166,6 +2343,9 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
2166
2343
|
rawInput: toolInput
|
|
2167
2344
|
}
|
|
2168
2345
|
});
|
|
2346
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
2347
|
+
throw new Error("Tool use aborted");
|
|
2348
|
+
}
|
|
2169
2349
|
if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "allow" || response.outcome.optionId === "allow_always")) {
|
|
2170
2350
|
if (response.outcome.optionId === "allow_always") {
|
|
2171
2351
|
return {
|
|
@@ -2242,16 +2422,18 @@ async function canUseTool(context) {
|
|
|
2242
2422
|
var UNSUPPORTED_COMMANDS = [
|
|
2243
2423
|
"context",
|
|
2244
2424
|
"cost",
|
|
2425
|
+
"keybindings-help",
|
|
2245
2426
|
"login",
|
|
2246
2427
|
"logout",
|
|
2247
2428
|
"output-style:new",
|
|
2248
2429
|
"release-notes",
|
|
2249
2430
|
"todos"
|
|
2250
2431
|
];
|
|
2251
|
-
|
|
2252
|
-
const commands = await q.supportedCommands();
|
|
2432
|
+
function getAvailableSlashCommands(commands) {
|
|
2253
2433
|
return commands.map((command) => {
|
|
2254
|
-
const input = command.argumentHint ? {
|
|
2434
|
+
const input = command.argumentHint != null ? {
|
|
2435
|
+
hint: Array.isArray(command.argumentHint) ? command.argumentHint.join(" ") : command.argumentHint
|
|
2436
|
+
} : null;
|
|
2255
2437
|
let name = command.name;
|
|
2256
2438
|
if (command.name.endsWith(" (MCP)")) {
|
|
2257
2439
|
name = `mcp:${name.replace(" (MCP)", "")}`;
|
|
@@ -2349,13 +2531,19 @@ function buildEnvironment() {
|
|
|
2349
2531
|
ENABLE_TOOL_SEARCH: "auto:0"
|
|
2350
2532
|
};
|
|
2351
2533
|
}
|
|
2352
|
-
function buildHooks(userHooks, onModeChange) {
|
|
2534
|
+
function buildHooks(userHooks, onModeChange, settingsManager, logger) {
|
|
2353
2535
|
return {
|
|
2354
2536
|
...userHooks,
|
|
2355
2537
|
PostToolUse: [
|
|
2356
2538
|
...userHooks?.PostToolUse || [],
|
|
2357
2539
|
{
|
|
2358
|
-
hooks: [createPostToolUseHook({ onModeChange })]
|
|
2540
|
+
hooks: [createPostToolUseHook({ onModeChange, logger })]
|
|
2541
|
+
}
|
|
2542
|
+
],
|
|
2543
|
+
PreToolUse: [
|
|
2544
|
+
...userHooks?.PreToolUse || [],
|
|
2545
|
+
{
|
|
2546
|
+
hooks: [createPreToolUseHook(settingsManager, logger)]
|
|
2359
2547
|
}
|
|
2360
2548
|
]
|
|
2361
2549
|
};
|
|
@@ -2449,12 +2637,22 @@ function buildSessionOptions(params) {
|
|
|
2449
2637
|
permissionMode: params.permissionMode,
|
|
2450
2638
|
canUseTool: params.canUseTool,
|
|
2451
2639
|
executable: "node",
|
|
2640
|
+
tools: { type: "preset", preset: "claude_code" },
|
|
2641
|
+
extraArgs: {
|
|
2642
|
+
...params.userProvidedOptions?.extraArgs,
|
|
2643
|
+
"replay-user-messages": ""
|
|
2644
|
+
},
|
|
2452
2645
|
mcpServers: buildMcpServers(
|
|
2453
2646
|
params.userProvidedOptions?.mcpServers,
|
|
2454
2647
|
params.mcpServers
|
|
2455
2648
|
),
|
|
2456
2649
|
env: buildEnvironment(),
|
|
2457
|
-
hooks: buildHooks(
|
|
2650
|
+
hooks: buildHooks(
|
|
2651
|
+
params.userProvidedOptions?.hooks,
|
|
2652
|
+
params.onModeChange,
|
|
2653
|
+
params.settingsManager,
|
|
2654
|
+
params.logger
|
|
2655
|
+
),
|
|
2458
2656
|
abortController: getAbortController(
|
|
2459
2657
|
params.userProvidedOptions?.abortController
|
|
2460
2658
|
),
|
|
@@ -2471,13 +2669,36 @@ function buildSessionOptions(params) {
|
|
|
2471
2669
|
}
|
|
2472
2670
|
if (params.isResume) {
|
|
2473
2671
|
options.resume = params.sessionId;
|
|
2474
|
-
options.forkSession = false;
|
|
2672
|
+
options.forkSession = params.forkSession ?? false;
|
|
2475
2673
|
} else {
|
|
2476
2674
|
options.sessionId = params.sessionId;
|
|
2675
|
+
options.model = DEFAULT_MODEL;
|
|
2477
2676
|
}
|
|
2478
2677
|
if (params.additionalDirectories) {
|
|
2479
2678
|
options.additionalDirectories = params.additionalDirectories;
|
|
2480
2679
|
}
|
|
2680
|
+
if (params.disableBuiltInTools) {
|
|
2681
|
+
const builtInTools = [
|
|
2682
|
+
"Read",
|
|
2683
|
+
"Write",
|
|
2684
|
+
"Edit",
|
|
2685
|
+
"Bash",
|
|
2686
|
+
"Glob",
|
|
2687
|
+
"Grep",
|
|
2688
|
+
"Task",
|
|
2689
|
+
"TodoWrite",
|
|
2690
|
+
"ExitPlanMode",
|
|
2691
|
+
"WebSearch",
|
|
2692
|
+
"WebFetch",
|
|
2693
|
+
"SlashCommand",
|
|
2694
|
+
"Skill",
|
|
2695
|
+
"NotebookEdit"
|
|
2696
|
+
];
|
|
2697
|
+
options.disallowedTools = [
|
|
2698
|
+
...options.disallowedTools ?? [],
|
|
2699
|
+
...builtInTools
|
|
2700
|
+
];
|
|
2701
|
+
}
|
|
2481
2702
|
clearStatsigCache();
|
|
2482
2703
|
return options;
|
|
2483
2704
|
}
|
|
@@ -2490,154 +2711,698 @@ function clearStatsigCache() {
|
|
|
2490
2711
|
});
|
|
2491
2712
|
}
|
|
2492
2713
|
|
|
2493
|
-
// src/adapters/claude/
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2714
|
+
// src/adapters/claude/session/settings.ts
|
|
2715
|
+
import * as fs2 from "fs";
|
|
2716
|
+
import * as os3 from "os";
|
|
2717
|
+
import * as path3 from "path";
|
|
2718
|
+
import { minimatch } from "minimatch";
|
|
2719
|
+
var ACP_TOOL_NAME_PREFIX = "mcp__acp__";
|
|
2720
|
+
var acpToolNames = {
|
|
2721
|
+
read: `${ACP_TOOL_NAME_PREFIX}Read`,
|
|
2722
|
+
edit: `${ACP_TOOL_NAME_PREFIX}Edit`,
|
|
2723
|
+
write: `${ACP_TOOL_NAME_PREFIX}Write`,
|
|
2724
|
+
bash: `${ACP_TOOL_NAME_PREFIX}Bash`
|
|
2725
|
+
};
|
|
2726
|
+
var SHELL_OPERATORS = ["&&", "||", ";", "|", "$(", "`", "\n"];
|
|
2727
|
+
function containsShellOperator(str) {
|
|
2728
|
+
return SHELL_OPERATORS.some((op) => str.includes(op));
|
|
2729
|
+
}
|
|
2730
|
+
var FILE_EDITING_TOOLS = [acpToolNames.edit, acpToolNames.write];
|
|
2731
|
+
var FILE_READING_TOOLS = [acpToolNames.read];
|
|
2732
|
+
var TOOL_ARG_ACCESSORS = {
|
|
2733
|
+
[acpToolNames.read]: (input) => input?.file_path,
|
|
2734
|
+
[acpToolNames.edit]: (input) => input?.file_path,
|
|
2735
|
+
[acpToolNames.write]: (input) => input?.file_path,
|
|
2736
|
+
[acpToolNames.bash]: (input) => input?.command
|
|
2737
|
+
};
|
|
2738
|
+
function parseRule(rule) {
|
|
2739
|
+
const match = rule.match(/^(\w+)(?:\((.+)\))?$/);
|
|
2740
|
+
if (!match) {
|
|
2741
|
+
return { toolName: rule };
|
|
2742
|
+
}
|
|
2743
|
+
const toolName = match[1] ?? rule;
|
|
2744
|
+
const argument = match[2];
|
|
2745
|
+
if (argument?.endsWith(":*")) {
|
|
2510
2746
|
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
|
-
]
|
|
2747
|
+
toolName,
|
|
2748
|
+
argument: argument.slice(0, -2),
|
|
2749
|
+
isWildcard: true
|
|
2540
2750
|
};
|
|
2541
2751
|
}
|
|
2542
|
-
|
|
2543
|
-
|
|
2752
|
+
return { toolName, argument };
|
|
2753
|
+
}
|
|
2754
|
+
function normalizePath(filePath, cwd) {
|
|
2755
|
+
let resolved = filePath;
|
|
2756
|
+
if (resolved.startsWith("~/")) {
|
|
2757
|
+
resolved = path3.join(os3.homedir(), resolved.slice(2));
|
|
2758
|
+
} else if (resolved.startsWith("./")) {
|
|
2759
|
+
resolved = path3.join(cwd, resolved.slice(2));
|
|
2760
|
+
} else if (!path3.isAbsolute(resolved)) {
|
|
2761
|
+
resolved = path3.join(cwd, resolved);
|
|
2762
|
+
}
|
|
2763
|
+
return path3.normalize(resolved).replace(/\\/g, "/");
|
|
2764
|
+
}
|
|
2765
|
+
function matchesGlob(pattern, filePath, cwd) {
|
|
2766
|
+
const normalizedPattern = normalizePath(pattern, cwd);
|
|
2767
|
+
const normalizedPath = normalizePath(filePath, cwd);
|
|
2768
|
+
return minimatch(normalizedPath, normalizedPattern, {
|
|
2769
|
+
dot: true,
|
|
2770
|
+
matchBase: false,
|
|
2771
|
+
nocase: process.platform === "win32"
|
|
2772
|
+
});
|
|
2773
|
+
}
|
|
2774
|
+
function matchesRule(rule, toolName, toolInput, cwd) {
|
|
2775
|
+
const ruleAppliesToTool = rule.toolName === "Bash" && toolName === acpToolNames.bash || rule.toolName === "Edit" && FILE_EDITING_TOOLS.includes(toolName) || rule.toolName === "Read" && FILE_READING_TOOLS.includes(toolName);
|
|
2776
|
+
if (!ruleAppliesToTool) {
|
|
2777
|
+
return false;
|
|
2544
2778
|
}
|
|
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
|
-
|
|
2779
|
+
if (!rule.argument) {
|
|
2780
|
+
return true;
|
|
2781
|
+
}
|
|
2782
|
+
const argAccessor = TOOL_ARG_ACCESSORS[toolName];
|
|
2783
|
+
if (!argAccessor) {
|
|
2784
|
+
return true;
|
|
2785
|
+
}
|
|
2786
|
+
const actualArg = argAccessor(toolInput);
|
|
2787
|
+
if (!actualArg) {
|
|
2788
|
+
return false;
|
|
2789
|
+
}
|
|
2790
|
+
if (toolName === acpToolNames.bash) {
|
|
2791
|
+
if (rule.isWildcard) {
|
|
2792
|
+
if (!actualArg.startsWith(rule.argument)) {
|
|
2793
|
+
return false;
|
|
2794
|
+
}
|
|
2795
|
+
const remainder = actualArg.slice(rule.argument.length);
|
|
2796
|
+
if (containsShellOperator(remainder)) {
|
|
2797
|
+
return false;
|
|
2798
|
+
}
|
|
2799
|
+
return true;
|
|
2800
|
+
}
|
|
2801
|
+
return actualArg === rule.argument;
|
|
2802
|
+
}
|
|
2803
|
+
return matchesGlob(rule.argument, actualArg, cwd);
|
|
2804
|
+
}
|
|
2805
|
+
async function loadSettingsFile(filePath) {
|
|
2806
|
+
if (!filePath) {
|
|
2807
|
+
return {};
|
|
2808
|
+
}
|
|
2809
|
+
try {
|
|
2810
|
+
const content = await fs2.promises.readFile(filePath, "utf-8");
|
|
2811
|
+
return JSON.parse(content);
|
|
2812
|
+
} catch {
|
|
2813
|
+
return {};
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
function getManagedSettingsPath() {
|
|
2817
|
+
switch (process.platform) {
|
|
2818
|
+
case "darwin":
|
|
2819
|
+
return "/Library/Application Support/ClaudeCode/managed-settings.json";
|
|
2820
|
+
case "linux":
|
|
2821
|
+
return "/etc/claude-code/managed-settings.json";
|
|
2822
|
+
case "win32":
|
|
2823
|
+
return "C:\\Program Files\\ClaudeCode\\managed-settings.json";
|
|
2824
|
+
default:
|
|
2825
|
+
return "/etc/claude-code/managed-settings.json";
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
var SettingsManager = class {
|
|
2829
|
+
cwd;
|
|
2830
|
+
userSettings = {};
|
|
2831
|
+
projectSettings = {};
|
|
2832
|
+
localSettings = {};
|
|
2833
|
+
enterpriseSettings = {};
|
|
2834
|
+
mergedSettings = {};
|
|
2835
|
+
initialized = false;
|
|
2836
|
+
constructor(cwd) {
|
|
2837
|
+
this.cwd = cwd;
|
|
2838
|
+
}
|
|
2839
|
+
async initialize() {
|
|
2840
|
+
if (this.initialized) {
|
|
2841
|
+
return;
|
|
2842
|
+
}
|
|
2843
|
+
await this.loadAllSettings();
|
|
2844
|
+
this.initialized = true;
|
|
2845
|
+
}
|
|
2846
|
+
getUserSettingsPath() {
|
|
2847
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || path3.join(os3.homedir(), ".claude");
|
|
2848
|
+
return path3.join(configDir, "settings.json");
|
|
2849
|
+
}
|
|
2850
|
+
getProjectSettingsPath() {
|
|
2851
|
+
return path3.join(this.cwd, ".claude", "settings.json");
|
|
2852
|
+
}
|
|
2853
|
+
getLocalSettingsPath() {
|
|
2854
|
+
return path3.join(this.cwd, ".claude", "settings.local.json");
|
|
2855
|
+
}
|
|
2856
|
+
async loadAllSettings() {
|
|
2857
|
+
const [userSettings, projectSettings, localSettings, enterpriseSettings] = await Promise.all([
|
|
2858
|
+
loadSettingsFile(this.getUserSettingsPath()),
|
|
2859
|
+
loadSettingsFile(this.getProjectSettingsPath()),
|
|
2860
|
+
loadSettingsFile(this.getLocalSettingsPath()),
|
|
2861
|
+
loadSettingsFile(getManagedSettingsPath())
|
|
2862
|
+
]);
|
|
2863
|
+
this.userSettings = userSettings;
|
|
2864
|
+
this.projectSettings = projectSettings;
|
|
2865
|
+
this.localSettings = localSettings;
|
|
2866
|
+
this.enterpriseSettings = enterpriseSettings;
|
|
2867
|
+
this.mergeAllSettings();
|
|
2868
|
+
}
|
|
2869
|
+
mergeAllSettings() {
|
|
2870
|
+
const allSettings = [
|
|
2871
|
+
this.userSettings,
|
|
2872
|
+
this.projectSettings,
|
|
2873
|
+
this.localSettings,
|
|
2874
|
+
this.enterpriseSettings
|
|
2875
|
+
];
|
|
2876
|
+
const permissions = {
|
|
2877
|
+
allow: [],
|
|
2878
|
+
deny: [],
|
|
2879
|
+
ask: []
|
|
2880
|
+
};
|
|
2881
|
+
const merged = { permissions };
|
|
2882
|
+
for (const settings of allSettings) {
|
|
2883
|
+
if (settings.permissions) {
|
|
2884
|
+
if (settings.permissions.allow) {
|
|
2885
|
+
permissions.allow?.push(...settings.permissions.allow);
|
|
2886
|
+
}
|
|
2887
|
+
if (settings.permissions.deny) {
|
|
2888
|
+
permissions.deny?.push(...settings.permissions.deny);
|
|
2889
|
+
}
|
|
2890
|
+
if (settings.permissions.ask) {
|
|
2891
|
+
permissions.ask?.push(...settings.permissions.ask);
|
|
2892
|
+
}
|
|
2893
|
+
if (settings.permissions.additionalDirectories) {
|
|
2894
|
+
permissions.additionalDirectories = [
|
|
2895
|
+
...permissions.additionalDirectories || [],
|
|
2896
|
+
...settings.permissions.additionalDirectories
|
|
2897
|
+
];
|
|
2898
|
+
}
|
|
2899
|
+
if (settings.permissions.defaultMode) {
|
|
2900
|
+
permissions.defaultMode = settings.permissions.defaultMode;
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
if (settings.env) {
|
|
2904
|
+
merged.env = { ...merged.env, ...settings.env };
|
|
2905
|
+
}
|
|
2906
|
+
if (settings.model) {
|
|
2907
|
+
merged.model = settings.model;
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
this.mergedSettings = merged;
|
|
2911
|
+
}
|
|
2912
|
+
checkPermission(toolName, toolInput) {
|
|
2913
|
+
if (!toolName.startsWith(ACP_TOOL_NAME_PREFIX)) {
|
|
2914
|
+
return { decision: "ask" };
|
|
2915
|
+
}
|
|
2916
|
+
const permissions = this.mergedSettings.permissions;
|
|
2917
|
+
if (!permissions) {
|
|
2918
|
+
return { decision: "ask" };
|
|
2919
|
+
}
|
|
2920
|
+
for (const rule of permissions.deny || []) {
|
|
2921
|
+
const parsed = parseRule(rule);
|
|
2922
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
2923
|
+
return { decision: "deny", rule, source: "deny" };
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
for (const rule of permissions.allow || []) {
|
|
2927
|
+
const parsed = parseRule(rule);
|
|
2928
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
2929
|
+
return { decision: "allow", rule, source: "allow" };
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
for (const rule of permissions.ask || []) {
|
|
2933
|
+
const parsed = parseRule(rule);
|
|
2934
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
2935
|
+
return { decision: "ask", rule, source: "ask" };
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
return { decision: "ask" };
|
|
2939
|
+
}
|
|
2940
|
+
getSettings() {
|
|
2941
|
+
return this.mergedSettings;
|
|
2942
|
+
}
|
|
2943
|
+
getCwd() {
|
|
2944
|
+
return this.cwd;
|
|
2945
|
+
}
|
|
2946
|
+
async setCwd(cwd) {
|
|
2947
|
+
if (this.cwd === cwd) {
|
|
2948
|
+
return;
|
|
2949
|
+
}
|
|
2950
|
+
this.dispose();
|
|
2951
|
+
this.cwd = cwd;
|
|
2952
|
+
this.initialized = false;
|
|
2953
|
+
await this.initialize();
|
|
2954
|
+
}
|
|
2955
|
+
dispose() {
|
|
2956
|
+
this.initialized = false;
|
|
2957
|
+
}
|
|
2958
|
+
};
|
|
2959
|
+
|
|
2960
|
+
// src/adapters/claude/claude-agent.ts
|
|
2961
|
+
var SESSION_VALIDATION_TIMEOUT_MS = 1e4;
|
|
2962
|
+
var MAX_TITLE_LENGTH = 256;
|
|
2963
|
+
function sanitizeTitle(text2) {
|
|
2964
|
+
const sanitized = text2.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
2965
|
+
if (sanitized.length <= MAX_TITLE_LENGTH) {
|
|
2966
|
+
return sanitized;
|
|
2967
|
+
}
|
|
2968
|
+
return `${sanitized.slice(0, MAX_TITLE_LENGTH - 1)}\u2026`;
|
|
2969
|
+
}
|
|
2970
|
+
var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
2971
|
+
adapterName = "claude";
|
|
2972
|
+
toolUseCache;
|
|
2973
|
+
backgroundTerminals = {};
|
|
2974
|
+
clientCapabilities;
|
|
2975
|
+
options;
|
|
2976
|
+
constructor(client, options) {
|
|
2977
|
+
super(client);
|
|
2978
|
+
this.options = options;
|
|
2979
|
+
this.toolUseCache = {};
|
|
2980
|
+
this.logger = new Logger({ debug: true, prefix: "[ClaudeAcpAgent]" });
|
|
2981
|
+
}
|
|
2982
|
+
async initialize(request) {
|
|
2983
|
+
this.clientCapabilities = request.clientCapabilities;
|
|
2984
|
+
return {
|
|
2985
|
+
protocolVersion: 1,
|
|
2986
|
+
agentCapabilities: {
|
|
2987
|
+
promptCapabilities: {
|
|
2988
|
+
image: true,
|
|
2989
|
+
embeddedContext: true
|
|
2990
|
+
},
|
|
2991
|
+
mcpCapabilities: {
|
|
2992
|
+
http: true,
|
|
2993
|
+
sse: true
|
|
2994
|
+
},
|
|
2995
|
+
loadSession: true,
|
|
2996
|
+
sessionCapabilities: {
|
|
2997
|
+
list: {},
|
|
2998
|
+
fork: {},
|
|
2999
|
+
resume: {}
|
|
3000
|
+
},
|
|
3001
|
+
_meta: {
|
|
3002
|
+
posthog: {
|
|
3003
|
+
resumeSession: true
|
|
3004
|
+
},
|
|
3005
|
+
claudeCode: {
|
|
3006
|
+
promptQueueing: true
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
},
|
|
3010
|
+
agentInfo: {
|
|
3011
|
+
name: package_default.name,
|
|
3012
|
+
title: "Claude Agent",
|
|
3013
|
+
version: package_default.version
|
|
3014
|
+
},
|
|
3015
|
+
authMethods: []
|
|
3016
|
+
};
|
|
3017
|
+
}
|
|
3018
|
+
async newSession(params) {
|
|
3019
|
+
if (fs3.existsSync(path4.resolve(os4.homedir(), ".claude.json.backup")) && !fs3.existsSync(path4.resolve(os4.homedir(), ".claude.json"))) {
|
|
3020
|
+
throw RequestError2.authRequired();
|
|
3021
|
+
}
|
|
3022
|
+
const response = await this.createSession(params, {
|
|
3023
|
+
// Revisit these meta values once we support resume
|
|
3024
|
+
resume: params._meta?.claudeCode?.options?.resume
|
|
2571
3025
|
});
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
3026
|
+
return response;
|
|
3027
|
+
}
|
|
3028
|
+
async unstable_forkSession(params) {
|
|
3029
|
+
return this.createSession(
|
|
3030
|
+
{
|
|
3031
|
+
cwd: params.cwd,
|
|
3032
|
+
mcpServers: params.mcpServers ?? [],
|
|
3033
|
+
_meta: params._meta
|
|
3034
|
+
},
|
|
3035
|
+
{ resume: params.sessionId, forkSession: true }
|
|
2582
3036
|
);
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
3037
|
+
}
|
|
3038
|
+
async unstable_resumeSession(params) {
|
|
3039
|
+
const response = await this.createSession(
|
|
3040
|
+
{
|
|
3041
|
+
cwd: params.cwd,
|
|
3042
|
+
mcpServers: params.mcpServers ?? [],
|
|
3043
|
+
_meta: params._meta
|
|
3044
|
+
},
|
|
3045
|
+
{
|
|
3046
|
+
resume: params.sessionId
|
|
3047
|
+
}
|
|
3048
|
+
);
|
|
3049
|
+
return response;
|
|
3050
|
+
}
|
|
3051
|
+
async loadSession(params) {
|
|
3052
|
+
const response = await this.createSession(
|
|
3053
|
+
{
|
|
3054
|
+
cwd: params.cwd,
|
|
3055
|
+
mcpServers: params.mcpServers ?? [],
|
|
3056
|
+
_meta: params._meta
|
|
3057
|
+
},
|
|
3058
|
+
{ resume: params.sessionId, skipBackgroundFetches: true }
|
|
3059
|
+
);
|
|
3060
|
+
await this.replaySessionHistory(params.sessionId);
|
|
3061
|
+
this.deferBackgroundFetches(this.session.query);
|
|
3062
|
+
return {
|
|
3063
|
+
modes: response.modes,
|
|
3064
|
+
models: response.models,
|
|
3065
|
+
configOptions: response.configOptions
|
|
3066
|
+
};
|
|
3067
|
+
}
|
|
3068
|
+
async unstable_listSessions(params) {
|
|
3069
|
+
const sdkSessions = await listSessions({ dir: params.cwd ?? void 0 });
|
|
3070
|
+
const sessions = [];
|
|
3071
|
+
for (const session of sdkSessions) {
|
|
3072
|
+
if (!session.cwd) continue;
|
|
3073
|
+
sessions.push({
|
|
3074
|
+
sessionId: session.sessionId,
|
|
3075
|
+
cwd: session.cwd,
|
|
3076
|
+
title: sanitizeTitle(session.customTitle || session.summary || ""),
|
|
3077
|
+
updatedAt: new Date(session.lastModified).toISOString()
|
|
2589
3078
|
});
|
|
2590
3079
|
}
|
|
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
3080
|
return {
|
|
2600
|
-
|
|
2601
|
-
configOptions
|
|
3081
|
+
sessions
|
|
2602
3082
|
};
|
|
2603
3083
|
}
|
|
2604
|
-
async
|
|
2605
|
-
|
|
3084
|
+
async prompt(params) {
|
|
3085
|
+
this.session.cancelled = false;
|
|
3086
|
+
this.session.interruptReason = void 0;
|
|
3087
|
+
this.session.accumulatedUsage = {
|
|
3088
|
+
inputTokens: 0,
|
|
3089
|
+
outputTokens: 0,
|
|
3090
|
+
cachedReadTokens: 0,
|
|
3091
|
+
cachedWriteTokens: 0
|
|
3092
|
+
};
|
|
3093
|
+
const userMessage = promptToClaude(params);
|
|
3094
|
+
if (this.session.promptRunning) {
|
|
3095
|
+
const uuid = randomUUID();
|
|
3096
|
+
userMessage.uuid = uuid;
|
|
3097
|
+
this.session.input.push(userMessage);
|
|
3098
|
+
const order = this.session.nextPendingOrder++;
|
|
3099
|
+
const cancelled = await new Promise((resolve3) => {
|
|
3100
|
+
this.session.pendingMessages.set(uuid, { resolve: resolve3, order });
|
|
3101
|
+
});
|
|
3102
|
+
if (cancelled) {
|
|
3103
|
+
return { stopReason: "cancelled" };
|
|
3104
|
+
}
|
|
3105
|
+
} else {
|
|
3106
|
+
this.session.input.push(userMessage);
|
|
3107
|
+
}
|
|
3108
|
+
await this.broadcastUserMessage(params);
|
|
3109
|
+
this.session.promptRunning = true;
|
|
3110
|
+
let handedOff = false;
|
|
3111
|
+
let lastAssistantTotalUsage = null;
|
|
3112
|
+
const supportsTerminalOutput = this.clientCapabilities?._meta?.terminal_output === true;
|
|
3113
|
+
const context = {
|
|
3114
|
+
session: this.session,
|
|
3115
|
+
sessionId: params.sessionId,
|
|
3116
|
+
client: this.client,
|
|
3117
|
+
toolUseCache: this.toolUseCache,
|
|
3118
|
+
fileContentCache: this.fileContentCache,
|
|
3119
|
+
logger: this.logger,
|
|
3120
|
+
supportsTerminalOutput
|
|
3121
|
+
};
|
|
3122
|
+
try {
|
|
3123
|
+
while (true) {
|
|
3124
|
+
const { value: message, done } = await this.session.query.next();
|
|
3125
|
+
if (done || !message) {
|
|
3126
|
+
if (this.session.cancelled) {
|
|
3127
|
+
return {
|
|
3128
|
+
stopReason: "cancelled",
|
|
3129
|
+
_meta: this.session.interruptReason ? { interruptReason: this.session.interruptReason } : void 0
|
|
3130
|
+
};
|
|
3131
|
+
}
|
|
3132
|
+
break;
|
|
3133
|
+
}
|
|
3134
|
+
switch (message.type) {
|
|
3135
|
+
case "system":
|
|
3136
|
+
if (message.subtype === "compact_boundary") {
|
|
3137
|
+
lastAssistantTotalUsage = 0;
|
|
3138
|
+
}
|
|
3139
|
+
await handleSystemMessage(message, context);
|
|
3140
|
+
break;
|
|
3141
|
+
case "result": {
|
|
3142
|
+
if (this.session.cancelled) {
|
|
3143
|
+
return { stopReason: "cancelled" };
|
|
3144
|
+
}
|
|
3145
|
+
this.session.accumulatedUsage.inputTokens += message.usage.input_tokens;
|
|
3146
|
+
this.session.accumulatedUsage.outputTokens += message.usage.output_tokens;
|
|
3147
|
+
this.session.accumulatedUsage.cachedReadTokens += message.usage.cache_read_input_tokens;
|
|
3148
|
+
this.session.accumulatedUsage.cachedWriteTokens += message.usage.cache_creation_input_tokens;
|
|
3149
|
+
const contextWindows = Object.values(message.modelUsage).map(
|
|
3150
|
+
(m) => m.contextWindow
|
|
3151
|
+
);
|
|
3152
|
+
const contextWindowSize = contextWindows.length > 0 ? Math.min(...contextWindows) : 2e5;
|
|
3153
|
+
if (lastAssistantTotalUsage !== null) {
|
|
3154
|
+
await this.client.sessionUpdate({
|
|
3155
|
+
sessionId: params.sessionId,
|
|
3156
|
+
update: {
|
|
3157
|
+
sessionUpdate: "usage_update",
|
|
3158
|
+
used: lastAssistantTotalUsage,
|
|
3159
|
+
size: contextWindowSize,
|
|
3160
|
+
cost: {
|
|
3161
|
+
amount: message.total_cost_usd,
|
|
3162
|
+
currency: "USD"
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
});
|
|
3166
|
+
}
|
|
3167
|
+
await this.client.extNotification("_posthog/usage_update", {
|
|
3168
|
+
sessionId: params.sessionId,
|
|
3169
|
+
used: {
|
|
3170
|
+
inputTokens: message.usage.input_tokens,
|
|
3171
|
+
outputTokens: message.usage.output_tokens,
|
|
3172
|
+
cachedReadTokens: message.usage.cache_read_input_tokens,
|
|
3173
|
+
cachedWriteTokens: message.usage.cache_creation_input_tokens
|
|
3174
|
+
},
|
|
3175
|
+
cost: message.total_cost_usd
|
|
3176
|
+
});
|
|
3177
|
+
const usage = {
|
|
3178
|
+
inputTokens: this.session.accumulatedUsage.inputTokens,
|
|
3179
|
+
outputTokens: this.session.accumulatedUsage.outputTokens,
|
|
3180
|
+
cachedReadTokens: this.session.accumulatedUsage.cachedReadTokens,
|
|
3181
|
+
cachedWriteTokens: this.session.accumulatedUsage.cachedWriteTokens,
|
|
3182
|
+
totalTokens: this.session.accumulatedUsage.inputTokens + this.session.accumulatedUsage.outputTokens + this.session.accumulatedUsage.cachedReadTokens + this.session.accumulatedUsage.cachedWriteTokens
|
|
3183
|
+
};
|
|
3184
|
+
const result = handleResultMessage(message);
|
|
3185
|
+
if (result.error) throw result.error;
|
|
3186
|
+
switch (message.subtype) {
|
|
3187
|
+
case "error_max_budget_usd":
|
|
3188
|
+
case "error_max_turns":
|
|
3189
|
+
case "error_max_structured_output_retries":
|
|
3190
|
+
return { stopReason: "max_turn_requests", usage };
|
|
3191
|
+
default:
|
|
3192
|
+
return { stopReason: "end_turn", usage };
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
case "stream_event":
|
|
3196
|
+
await handleStreamEvent(message, context);
|
|
3197
|
+
break;
|
|
3198
|
+
case "user":
|
|
3199
|
+
case "assistant": {
|
|
3200
|
+
if (this.session.cancelled) {
|
|
3201
|
+
break;
|
|
3202
|
+
}
|
|
3203
|
+
if (message.type === "user" && "uuid" in message && message.uuid) {
|
|
3204
|
+
const pending = this.session.pendingMessages.get(
|
|
3205
|
+
message.uuid
|
|
3206
|
+
);
|
|
3207
|
+
if (pending) {
|
|
3208
|
+
pending.resolve(false);
|
|
3209
|
+
this.session.pendingMessages.delete(message.uuid);
|
|
3210
|
+
handedOff = true;
|
|
3211
|
+
return { stopReason: "end_turn" };
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
if ("usage" in message.message && message.parent_tool_use_id === null) {
|
|
3215
|
+
const usage = message.message.usage;
|
|
3216
|
+
lastAssistantTotalUsage = usage.input_tokens + usage.output_tokens + usage.cache_read_input_tokens + usage.cache_creation_input_tokens;
|
|
3217
|
+
}
|
|
3218
|
+
const result = await handleUserAssistantMessage(message, context);
|
|
3219
|
+
if (result.error) throw result.error;
|
|
3220
|
+
if (result.shouldStop) {
|
|
3221
|
+
return { stopReason: "end_turn" };
|
|
3222
|
+
}
|
|
3223
|
+
break;
|
|
3224
|
+
}
|
|
3225
|
+
case "tool_progress":
|
|
3226
|
+
case "auth_status":
|
|
3227
|
+
case "tool_use_summary":
|
|
3228
|
+
break;
|
|
3229
|
+
default:
|
|
3230
|
+
unreachable(message, this.logger);
|
|
3231
|
+
break;
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
throw new Error("Session did not end in result");
|
|
3235
|
+
} finally {
|
|
3236
|
+
if (!handedOff) {
|
|
3237
|
+
this.session.promptRunning = false;
|
|
3238
|
+
for (const [key, pending] of this.session.pendingMessages) {
|
|
3239
|
+
pending.resolve(true);
|
|
3240
|
+
this.session.pendingMessages.delete(key);
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
// Called by BaseAcpAgent#cancel() to interrupt the session
|
|
3246
|
+
async interrupt() {
|
|
3247
|
+
this.session.cancelled = true;
|
|
3248
|
+
for (const [, pending] of this.session.pendingMessages) {
|
|
3249
|
+
pending.resolve(true);
|
|
3250
|
+
}
|
|
3251
|
+
this.session.pendingMessages.clear();
|
|
3252
|
+
await this.session.query.interrupt();
|
|
3253
|
+
}
|
|
3254
|
+
async unstable_setSessionModel(params) {
|
|
3255
|
+
const sdkModelId = toSdkModelId(params.modelId);
|
|
3256
|
+
await this.session.query.setModel(sdkModelId);
|
|
3257
|
+
this.session.modelId = params.modelId;
|
|
3258
|
+
await this.updateConfigOption("model", params.modelId);
|
|
3259
|
+
return {};
|
|
3260
|
+
}
|
|
3261
|
+
async setSessionMode(params) {
|
|
3262
|
+
await this.applySessionMode(params.modeId);
|
|
3263
|
+
await this.updateConfigOption("mode", params.modeId);
|
|
3264
|
+
return {};
|
|
3265
|
+
}
|
|
3266
|
+
async setSessionConfigOption(params) {
|
|
3267
|
+
const option = this.session.configOptions.find(
|
|
3268
|
+
(o) => o.id === params.configId
|
|
3269
|
+
);
|
|
3270
|
+
if (!option) {
|
|
3271
|
+
throw new Error(`Unknown config option: ${params.configId}`);
|
|
3272
|
+
}
|
|
3273
|
+
const allValues = "options" in option && Array.isArray(option.options) ? option.options.flatMap(
|
|
3274
|
+
(o) => "options" in o && Array.isArray(o.options) ? o.options : [o]
|
|
3275
|
+
) : [];
|
|
3276
|
+
const validValue = allValues.find((o) => o.value === params.value);
|
|
3277
|
+
if (!validValue) {
|
|
3278
|
+
throw new Error(
|
|
3279
|
+
`Invalid value for config option ${params.configId}: ${params.value}`
|
|
3280
|
+
);
|
|
3281
|
+
}
|
|
3282
|
+
if (params.configId === "mode") {
|
|
3283
|
+
await this.applySessionMode(params.value);
|
|
3284
|
+
await this.client.sessionUpdate({
|
|
3285
|
+
sessionId: this.sessionId,
|
|
3286
|
+
update: {
|
|
3287
|
+
sessionUpdate: "current_mode_update",
|
|
3288
|
+
currentModeId: params.value
|
|
3289
|
+
}
|
|
3290
|
+
});
|
|
3291
|
+
} else if (params.configId === "model") {
|
|
3292
|
+
const sdkModelId = toSdkModelId(params.value);
|
|
3293
|
+
await this.session.query.setModel(sdkModelId);
|
|
3294
|
+
this.session.modelId = params.value;
|
|
3295
|
+
}
|
|
3296
|
+
this.session.configOptions = this.session.configOptions.map(
|
|
3297
|
+
(o) => o.id === params.configId ? { ...o, currentValue: params.value } : o
|
|
3298
|
+
);
|
|
3299
|
+
return { configOptions: this.session.configOptions };
|
|
2606
3300
|
}
|
|
2607
|
-
async
|
|
3301
|
+
async updateConfigOption(configId, value) {
|
|
3302
|
+
this.session.configOptions = this.session.configOptions.map(
|
|
3303
|
+
(o) => o.id === configId ? { ...o, currentValue: value } : o
|
|
3304
|
+
);
|
|
3305
|
+
await this.client.sessionUpdate({
|
|
3306
|
+
sessionId: this.sessionId,
|
|
3307
|
+
update: {
|
|
3308
|
+
sessionUpdate: "config_option_update",
|
|
3309
|
+
configOptions: this.session.configOptions
|
|
3310
|
+
}
|
|
3311
|
+
});
|
|
3312
|
+
}
|
|
3313
|
+
async applySessionMode(modeId) {
|
|
3314
|
+
if (!TWIG_EXECUTION_MODES.includes(modeId)) {
|
|
3315
|
+
throw new Error("Invalid Mode");
|
|
3316
|
+
}
|
|
3317
|
+
const previousMode = this.session.permissionMode;
|
|
3318
|
+
this.session.permissionMode = modeId;
|
|
3319
|
+
try {
|
|
3320
|
+
await this.session.query.setPermissionMode(modeId);
|
|
3321
|
+
} catch (error) {
|
|
3322
|
+
this.session.permissionMode = previousMode;
|
|
3323
|
+
if (error instanceof Error) {
|
|
3324
|
+
if (!error.message) {
|
|
3325
|
+
error.message = "Invalid Mode";
|
|
3326
|
+
}
|
|
3327
|
+
throw error;
|
|
3328
|
+
}
|
|
3329
|
+
throw new Error("Invalid Mode");
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
async createSession(params, creationOpts = {}) {
|
|
3333
|
+
const { cwd } = params;
|
|
3334
|
+
const { resume, forkSession } = creationOpts;
|
|
3335
|
+
const isResume = !!resume;
|
|
2608
3336
|
const meta = params._meta;
|
|
2609
3337
|
const taskId = meta?.persistence?.taskId;
|
|
2610
|
-
|
|
2611
|
-
if (
|
|
2612
|
-
|
|
2613
|
-
}
|
|
2614
|
-
|
|
2615
|
-
|
|
3338
|
+
let sessionId;
|
|
3339
|
+
if (forkSession) {
|
|
3340
|
+
sessionId = uuidv7();
|
|
3341
|
+
} else if (isResume) {
|
|
3342
|
+
sessionId = resume;
|
|
3343
|
+
} else {
|
|
3344
|
+
sessionId = uuidv7();
|
|
2616
3345
|
}
|
|
2617
|
-
|
|
3346
|
+
const input = new Pushable();
|
|
3347
|
+
const settingsManager = new SettingsManager(cwd);
|
|
3348
|
+
await settingsManager.initialize();
|
|
3349
|
+
const mcpServers = parseMcpServers(params);
|
|
3350
|
+
const systemPrompt = buildSystemPrompt(meta?.systemPrompt);
|
|
3351
|
+
this.logger.info(isResume ? "Resuming session" : "Creating new session", {
|
|
2618
3352
|
sessionId,
|
|
2619
3353
|
taskId,
|
|
2620
3354
|
taskRunId: meta?.taskRunId,
|
|
2621
|
-
cwd
|
|
3355
|
+
cwd
|
|
2622
3356
|
});
|
|
2623
|
-
const mcpServers = parseMcpServers(params);
|
|
2624
3357
|
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
2625
|
-
const
|
|
2626
|
-
cwd
|
|
2627
|
-
permissionMode,
|
|
3358
|
+
const options = buildSessionOptions({
|
|
3359
|
+
cwd,
|
|
2628
3360
|
mcpServers,
|
|
2629
|
-
|
|
3361
|
+
permissionMode,
|
|
3362
|
+
canUseTool: this.createCanUseTool(sessionId),
|
|
3363
|
+
logger: this.logger,
|
|
3364
|
+
systemPrompt,
|
|
2630
3365
|
userProvidedOptions: meta?.claudeCode?.options,
|
|
2631
3366
|
sessionId,
|
|
2632
|
-
isResume
|
|
2633
|
-
|
|
3367
|
+
isResume,
|
|
3368
|
+
forkSession,
|
|
3369
|
+
additionalDirectories: meta?.claudeCode?.options?.additionalDirectories,
|
|
3370
|
+
disableBuiltInTools: meta?.disableBuiltInTools,
|
|
3371
|
+
settingsManager,
|
|
3372
|
+
onModeChange: this.createOnModeChange(),
|
|
3373
|
+
onProcessSpawned: this.options?.onProcessSpawned,
|
|
3374
|
+
onProcessExited: this.options?.onProcessExited
|
|
2634
3375
|
});
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
3376
|
+
const abortController = options.abortController;
|
|
3377
|
+
const q = query({ prompt: input, options });
|
|
3378
|
+
const session = {
|
|
3379
|
+
query: q,
|
|
3380
|
+
input,
|
|
3381
|
+
cancelled: false,
|
|
3382
|
+
settingsManager,
|
|
3383
|
+
permissionMode,
|
|
3384
|
+
abortController,
|
|
3385
|
+
accumulatedUsage: {
|
|
3386
|
+
inputTokens: 0,
|
|
3387
|
+
outputTokens: 0,
|
|
3388
|
+
cachedReadTokens: 0,
|
|
3389
|
+
cachedWriteTokens: 0
|
|
3390
|
+
},
|
|
3391
|
+
configOptions: [],
|
|
3392
|
+
promptRunning: false,
|
|
3393
|
+
pendingMessages: /* @__PURE__ */ new Map(),
|
|
3394
|
+
nextPendingOrder: 0,
|
|
3395
|
+
// Custom properties
|
|
3396
|
+
cwd,
|
|
3397
|
+
notificationHistory: [],
|
|
2638
3398
|
taskRunId: meta?.taskRunId
|
|
2639
|
-
}
|
|
2640
|
-
session
|
|
3399
|
+
};
|
|
3400
|
+
this.session = session;
|
|
3401
|
+
this.sessionId = sessionId;
|
|
3402
|
+
this.logger.info(
|
|
3403
|
+
isResume ? "Session query initialized, awaiting resumption" : "Session query initialized, awaiting initialization",
|
|
3404
|
+
{ sessionId, taskId, taskRunId: meta?.taskRunId }
|
|
3405
|
+
);
|
|
2641
3406
|
try {
|
|
2642
3407
|
const result = await withTimeout(
|
|
2643
3408
|
q.initializationResult(),
|
|
@@ -2645,231 +3410,181 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
2645
3410
|
);
|
|
2646
3411
|
if (result.result === "timeout") {
|
|
2647
3412
|
throw new Error(
|
|
2648
|
-
`Session resumption timed out for sessionId=${sessionId}`
|
|
3413
|
+
`Session ${isResume ? forkSession ? "fork" : "resumption" : "initialization"} timed out for sessionId=${sessionId}`
|
|
2649
3414
|
);
|
|
2650
3415
|
}
|
|
2651
3416
|
} catch (err) {
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
3417
|
+
settingsManager.dispose();
|
|
3418
|
+
this.logger.error(
|
|
3419
|
+
isResume ? forkSession ? "Session fork failed" : "Session resumption failed" : "Session initialization failed",
|
|
3420
|
+
{
|
|
3421
|
+
sessionId,
|
|
3422
|
+
taskId,
|
|
3423
|
+
taskRunId: meta?.taskRunId,
|
|
3424
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3425
|
+
}
|
|
3426
|
+
);
|
|
2658
3427
|
throw err;
|
|
2659
3428
|
}
|
|
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");
|
|
3429
|
+
if (meta?.taskRunId) {
|
|
3430
|
+
await this.client.extNotification("_posthog/sdk_session", {
|
|
3431
|
+
taskRunId: meta.taskRunId,
|
|
3432
|
+
sessionId,
|
|
3433
|
+
adapter: "claude"
|
|
3434
|
+
});
|
|
2691
3435
|
}
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
params
|
|
2702
|
-
);
|
|
2703
|
-
return {
|
|
2704
|
-
_meta: {
|
|
2705
|
-
configOptions: result.configOptions
|
|
2706
|
-
}
|
|
2707
|
-
};
|
|
3436
|
+
const settingsModel = settingsManager.getSettings().model;
|
|
3437
|
+
const modelOptions = await this.getModelConfigOptions();
|
|
3438
|
+
const resolvedModelId = settingsModel || modelOptions.currentModelId;
|
|
3439
|
+
session.modelId = resolvedModelId;
|
|
3440
|
+
if (!isResume) {
|
|
3441
|
+
const resolvedSdkModel = toSdkModelId(resolvedModelId);
|
|
3442
|
+
if (resolvedSdkModel !== DEFAULT_MODEL) {
|
|
3443
|
+
await this.session.query.setModel(resolvedSdkModel);
|
|
3444
|
+
}
|
|
2708
3445
|
}
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
cwd,
|
|
2718
|
-
notificationHistory: [],
|
|
2719
|
-
abortController
|
|
3446
|
+
const availableModes2 = getAvailableModes();
|
|
3447
|
+
const modes = {
|
|
3448
|
+
currentModeId: permissionMode,
|
|
3449
|
+
availableModes: availableModes2.map((mode) => ({
|
|
3450
|
+
id: mode.id,
|
|
3451
|
+
name: mode.name,
|
|
3452
|
+
description: mode.description ?? void 0
|
|
3453
|
+
}))
|
|
2720
3454
|
};
|
|
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
|
|
3455
|
+
const models = {
|
|
3456
|
+
currentModelId: resolvedModelId,
|
|
3457
|
+
availableModels: modelOptions.options.map(
|
|
3458
|
+
(opt) => ({
|
|
3459
|
+
modelId: opt.value,
|
|
3460
|
+
name: opt.name,
|
|
3461
|
+
description: opt.description
|
|
3462
|
+
})
|
|
3463
|
+
)
|
|
3464
|
+
};
|
|
3465
|
+
const configOptions = this.buildConfigOptions(permissionMode, modelOptions);
|
|
3466
|
+
session.configOptions = configOptions;
|
|
3467
|
+
if (!creationOpts.skipBackgroundFetches) {
|
|
3468
|
+
this.deferBackgroundFetches(q);
|
|
3469
|
+
}
|
|
3470
|
+
this.logger.info(
|
|
3471
|
+
isResume ? "Session resumed successfully" : "Session created successfully",
|
|
3472
|
+
{
|
|
3473
|
+
sessionId,
|
|
3474
|
+
taskId,
|
|
3475
|
+
taskRunId: meta?.taskRunId
|
|
3476
|
+
}
|
|
2751
3477
|
);
|
|
2752
|
-
return {
|
|
3478
|
+
return { sessionId, modes, models, configOptions };
|
|
2753
3479
|
}
|
|
2754
3480
|
createCanUseTool(sessionId) {
|
|
2755
|
-
return async (toolName, toolInput, { suggestions, toolUseID }) => canUseTool({
|
|
3481
|
+
return async (toolName, toolInput, { suggestions, toolUseID, signal }) => canUseTool({
|
|
2756
3482
|
session: this.session,
|
|
2757
3483
|
toolName,
|
|
2758
3484
|
toolInput,
|
|
2759
3485
|
toolUseID,
|
|
2760
3486
|
suggestions,
|
|
3487
|
+
signal,
|
|
2761
3488
|
client: this.client,
|
|
2762
3489
|
sessionId,
|
|
2763
3490
|
fileContentCache: this.fileContentCache,
|
|
2764
3491
|
logger: this.logger,
|
|
2765
|
-
|
|
3492
|
+
updateConfigOption: (configId, value) => this.updateConfigOption(configId, value)
|
|
2766
3493
|
});
|
|
2767
3494
|
}
|
|
2768
|
-
createOnModeChange(
|
|
3495
|
+
createOnModeChange() {
|
|
2769
3496
|
return async (newMode) => {
|
|
2770
3497
|
if (this.session) {
|
|
2771
3498
|
this.session.permissionMode = newMode;
|
|
2772
3499
|
}
|
|
2773
|
-
await this.
|
|
3500
|
+
await this.updateConfigOption("mode", newMode);
|
|
2774
3501
|
};
|
|
2775
3502
|
}
|
|
2776
|
-
|
|
2777
|
-
const options = [];
|
|
3503
|
+
buildConfigOptions(currentModeId, modelOptions) {
|
|
2778
3504
|
const modeOptions = getAvailableModes().map((mode) => ({
|
|
2779
3505
|
value: mode.id,
|
|
2780
3506
|
name: mode.name,
|
|
2781
3507
|
description: mode.description ?? void 0
|
|
2782
3508
|
}));
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
return options;
|
|
3509
|
+
return [
|
|
3510
|
+
{
|
|
3511
|
+
id: "mode",
|
|
3512
|
+
name: "Approval Preset",
|
|
3513
|
+
type: "select",
|
|
3514
|
+
currentValue: currentModeId,
|
|
3515
|
+
options: modeOptions,
|
|
3516
|
+
category: "mode",
|
|
3517
|
+
description: "Choose an approval and sandboxing preset for your session"
|
|
3518
|
+
},
|
|
3519
|
+
{
|
|
3520
|
+
id: "model",
|
|
3521
|
+
name: "Model",
|
|
3522
|
+
type: "select",
|
|
3523
|
+
currentValue: modelOptions.currentModelId,
|
|
3524
|
+
options: modelOptions.options,
|
|
3525
|
+
category: "model",
|
|
3526
|
+
description: "Choose which model Claude should use"
|
|
3527
|
+
}
|
|
3528
|
+
];
|
|
2804
3529
|
}
|
|
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;
|
|
3530
|
+
async sendAvailableCommandsUpdate() {
|
|
3531
|
+
const commands = await this.session.query.supportedCommands();
|
|
2812
3532
|
await this.client.sessionUpdate({
|
|
2813
|
-
sessionId:
|
|
3533
|
+
sessionId: this.sessionId,
|
|
2814
3534
|
update: {
|
|
2815
|
-
sessionUpdate: "
|
|
2816
|
-
|
|
3535
|
+
sessionUpdate: "available_commands_update",
|
|
3536
|
+
availableCommands: getAvailableSlashCommands(commands)
|
|
2817
3537
|
}
|
|
2818
3538
|
});
|
|
2819
3539
|
}
|
|
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);
|
|
3540
|
+
async replaySessionHistory(sessionId) {
|
|
2840
3541
|
try {
|
|
2841
|
-
await
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
3542
|
+
const messages = await getSessionMessages(sessionId, {
|
|
3543
|
+
dir: this.session.cwd
|
|
3544
|
+
});
|
|
3545
|
+
const replayContext = {
|
|
3546
|
+
session: this.session,
|
|
3547
|
+
sessionId,
|
|
3548
|
+
client: this.client,
|
|
3549
|
+
toolUseCache: this.toolUseCache,
|
|
3550
|
+
fileContentCache: this.fileContentCache,
|
|
3551
|
+
logger: this.logger,
|
|
3552
|
+
registerHooks: false
|
|
3553
|
+
};
|
|
3554
|
+
for (const msg of messages) {
|
|
3555
|
+
const sdkMessage = {
|
|
3556
|
+
type: msg.type,
|
|
3557
|
+
message: msg.message,
|
|
3558
|
+
parent_tool_use_id: msg.parent_tool_use_id
|
|
3559
|
+
};
|
|
3560
|
+
await handleUserAssistantMessage(
|
|
3561
|
+
sdkMessage,
|
|
3562
|
+
replayContext
|
|
3563
|
+
);
|
|
2845
3564
|
}
|
|
2846
|
-
|
|
3565
|
+
} catch (err) {
|
|
3566
|
+
this.logger.warn("Failed to replay session history", {
|
|
3567
|
+
sessionId,
|
|
3568
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3569
|
+
});
|
|
2847
3570
|
}
|
|
2848
3571
|
}
|
|
3572
|
+
// ================================
|
|
3573
|
+
// EXTENSION METHODS
|
|
3574
|
+
// ================================
|
|
2849
3575
|
/**
|
|
2850
3576
|
* Fire-and-forget: fetch slash commands and MCP tool metadata in parallel.
|
|
2851
3577
|
* Both populate caches used later — neither is needed to return configOptions.
|
|
2852
3578
|
*/
|
|
2853
|
-
deferBackgroundFetches(q
|
|
3579
|
+
deferBackgroundFetches(q) {
|
|
2854
3580
|
Promise.all([
|
|
2855
|
-
|
|
3581
|
+
new Promise((resolve3) => setTimeout(resolve3, 10)).then(
|
|
3582
|
+
() => this.sendAvailableCommandsUpdate()
|
|
3583
|
+
),
|
|
2856
3584
|
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);
|
|
3585
|
+
]).catch(
|
|
3586
|
+
(err) => this.logger.error("Background fetch failed", { error: err })
|
|
3587
|
+
);
|
|
2873
3588
|
}
|
|
2874
3589
|
async broadcastUserMessage(params) {
|
|
2875
3590
|
for (const chunk of params.prompt) {
|
|
@@ -2884,71 +3599,6 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
2884
3599
|
this.appendNotification(params.sessionId, notification);
|
|
2885
3600
|
}
|
|
2886
3601
|
}
|
|
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
3602
|
};
|
|
2953
3603
|
|
|
2954
3604
|
// src/adapters/codex/spawn.ts
|
|
@@ -3013,7 +3663,7 @@ function spawnCodexProcess(options) {
|
|
|
3013
3663
|
detached: process.platform !== "win32"
|
|
3014
3664
|
});
|
|
3015
3665
|
child.stderr?.on("data", (data) => {
|
|
3016
|
-
logger.
|
|
3666
|
+
logger.debug("codex-acp stderr:", data.toString());
|
|
3017
3667
|
});
|
|
3018
3668
|
child.on("error", (err) => {
|
|
3019
3669
|
logger.error("codex-acp process error:", err);
|
|
@@ -3575,8 +4225,8 @@ var PostHogAPIClient = class {
|
|
|
3575
4225
|
};
|
|
3576
4226
|
|
|
3577
4227
|
// src/session-log-writer.ts
|
|
3578
|
-
import
|
|
3579
|
-
import
|
|
4228
|
+
import fs4 from "fs";
|
|
4229
|
+
import path5 from "path";
|
|
3580
4230
|
var SessionLogWriter = class _SessionLogWriter {
|
|
3581
4231
|
static FLUSH_DEBOUNCE_MS = 500;
|
|
3582
4232
|
static FLUSH_MAX_INTERVAL_MS = 5e3;
|
|
@@ -3614,13 +4264,13 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
3614
4264
|
this.sessions.set(sessionId, { context });
|
|
3615
4265
|
this.lastFlushAttemptTime.set(sessionId, Date.now());
|
|
3616
4266
|
if (this.localCachePath) {
|
|
3617
|
-
const sessionDir =
|
|
4267
|
+
const sessionDir = path5.join(
|
|
3618
4268
|
this.localCachePath,
|
|
3619
4269
|
"sessions",
|
|
3620
4270
|
context.runId
|
|
3621
4271
|
);
|
|
3622
4272
|
try {
|
|
3623
|
-
|
|
4273
|
+
fs4.mkdirSync(sessionDir, { recursive: true });
|
|
3624
4274
|
} catch (error) {
|
|
3625
4275
|
this.logger.warn("Failed to create local cache directory", {
|
|
3626
4276
|
sessionDir,
|
|
@@ -3817,14 +4467,14 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
3817
4467
|
if (!this.localCachePath) return;
|
|
3818
4468
|
const session = this.sessions.get(sessionId);
|
|
3819
4469
|
if (!session) return;
|
|
3820
|
-
const logPath =
|
|
4470
|
+
const logPath = path5.join(
|
|
3821
4471
|
this.localCachePath,
|
|
3822
4472
|
"sessions",
|
|
3823
4473
|
session.context.runId,
|
|
3824
4474
|
"logs.ndjson"
|
|
3825
4475
|
);
|
|
3826
4476
|
try {
|
|
3827
|
-
|
|
4477
|
+
fs4.appendFileSync(logPath, `${JSON.stringify(entry)}
|
|
3828
4478
|
`);
|
|
3829
4479
|
} catch (error) {
|
|
3830
4480
|
this.logger.warn("Failed to write to local cache", {
|
|
@@ -3946,6 +4596,9 @@ var Agent = class {
|
|
|
3946
4596
|
prUrl
|
|
3947
4597
|
});
|
|
3948
4598
|
}
|
|
4599
|
+
getPosthogAPI() {
|
|
4600
|
+
return this.posthogAPI;
|
|
4601
|
+
}
|
|
3949
4602
|
async flushAllLogs() {
|
|
3950
4603
|
await this.sessionLogWriter?.flushAll();
|
|
3951
4604
|
}
|