@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/server/bin.cjs
CHANGED
|
@@ -509,7 +509,7 @@ var require_has_flag = __commonJS({
|
|
|
509
509
|
var require_supports_color = __commonJS({
|
|
510
510
|
"../../node_modules/supports-color/index.js"(exports2, module2) {
|
|
511
511
|
"use strict";
|
|
512
|
-
var
|
|
512
|
+
var os5 = require("os");
|
|
513
513
|
var tty = require("tty");
|
|
514
514
|
var hasFlag = require_has_flag();
|
|
515
515
|
var { env } = process;
|
|
@@ -557,7 +557,7 @@ var require_supports_color = __commonJS({
|
|
|
557
557
|
return min;
|
|
558
558
|
}
|
|
559
559
|
if (process.platform === "win32") {
|
|
560
|
-
const osRelease =
|
|
560
|
+
const osRelease = os5.release().split(".");
|
|
561
561
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
562
562
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
563
563
|
}
|
|
@@ -805,10 +805,10 @@ var require_src2 = __commonJS({
|
|
|
805
805
|
var fs_1 = require("fs");
|
|
806
806
|
var debug_1 = __importDefault(require_src());
|
|
807
807
|
var log = debug_1.default("@kwsites/file-exists");
|
|
808
|
-
function check(
|
|
809
|
-
log(`checking %s`,
|
|
808
|
+
function check(path9, isFile, isDirectory) {
|
|
809
|
+
log(`checking %s`, path9);
|
|
810
810
|
try {
|
|
811
|
-
const stat = fs_1.statSync(
|
|
811
|
+
const stat = fs_1.statSync(path9);
|
|
812
812
|
if (stat.isFile() && isFile) {
|
|
813
813
|
log(`[OK] path represents a file`);
|
|
814
814
|
return true;
|
|
@@ -828,8 +828,8 @@ var require_src2 = __commonJS({
|
|
|
828
828
|
throw e;
|
|
829
829
|
}
|
|
830
830
|
}
|
|
831
|
-
function exists2(
|
|
832
|
-
return check(
|
|
831
|
+
function exists2(path9, type = exports2.READABLE) {
|
|
832
|
+
return check(path9, (type & exports2.FILE) > 0, (type & exports2.FOLDER) > 0);
|
|
833
833
|
}
|
|
834
834
|
exports2.exists = exists2;
|
|
835
835
|
exports2.FILE = 1;
|
|
@@ -904,7 +904,7 @@ var import_hono = require("hono");
|
|
|
904
904
|
// package.json
|
|
905
905
|
var package_default = {
|
|
906
906
|
name: "@posthog/agent",
|
|
907
|
-
version: "2.1.
|
|
907
|
+
version: "2.1.138",
|
|
908
908
|
repository: "https://github.com/PostHog/twig",
|
|
909
909
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
910
910
|
exports: {
|
|
@@ -944,6 +944,10 @@ var package_default = {
|
|
|
944
944
|
types: "./dist/adapters/claude/conversion/tool-use-to-acp.d.ts",
|
|
945
945
|
import: "./dist/adapters/claude/conversion/tool-use-to-acp.js"
|
|
946
946
|
},
|
|
947
|
+
"./adapters/claude/session/jsonl-hydration": {
|
|
948
|
+
types: "./dist/adapters/claude/session/jsonl-hydration.d.ts",
|
|
949
|
+
import: "./dist/adapters/claude/session/jsonl-hydration.js"
|
|
950
|
+
},
|
|
947
951
|
"./server": {
|
|
948
952
|
types: "./dist/server/agent-server.d.ts",
|
|
949
953
|
import: "./dist/server/agent-server.js"
|
|
@@ -980,7 +984,6 @@ var package_default = {
|
|
|
980
984
|
"@twig/git": "workspace:*",
|
|
981
985
|
"@types/bun": "latest",
|
|
982
986
|
"@types/tar": "^6.1.13",
|
|
983
|
-
minimatch: "^10.0.3",
|
|
984
987
|
msw: "^2.12.7",
|
|
985
988
|
tsup: "^8.5.1",
|
|
986
989
|
tsx: "^4.20.6",
|
|
@@ -1001,6 +1004,7 @@ var package_default = {
|
|
|
1001
1004
|
commander: "^14.0.2",
|
|
1002
1005
|
hono: "^4.11.7",
|
|
1003
1006
|
jsonwebtoken: "^9.0.2",
|
|
1007
|
+
minimatch: "^10.0.3",
|
|
1004
1008
|
tar: "^7.5.0",
|
|
1005
1009
|
uuid: "13.0.0",
|
|
1006
1010
|
"yoga-wasm-web": "^0.3.3",
|
|
@@ -1281,9 +1285,10 @@ function nodeWritableToWebWritable(nodeStream) {
|
|
|
1281
1285
|
}
|
|
1282
1286
|
|
|
1283
1287
|
// src/adapters/claude/claude-agent.ts
|
|
1284
|
-
var
|
|
1285
|
-
var
|
|
1286
|
-
var
|
|
1288
|
+
var import_node_crypto = require("crypto");
|
|
1289
|
+
var fs3 = __toESM(require("fs"), 1);
|
|
1290
|
+
var os4 = __toESM(require("os"), 1);
|
|
1291
|
+
var path4 = __toESM(require("path"), 1);
|
|
1287
1292
|
var import_sdk2 = require("@agentclientprotocol/sdk");
|
|
1288
1293
|
var import_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
|
|
1289
1294
|
var import_uuid = require("uuid");
|
|
@@ -1305,7 +1310,7 @@ function unreachable(value, logger) {
|
|
|
1305
1310
|
try {
|
|
1306
1311
|
valueAsString = JSON.stringify(value);
|
|
1307
1312
|
} catch {
|
|
1308
|
-
valueAsString = value;
|
|
1313
|
+
valueAsString = String(value);
|
|
1309
1314
|
}
|
|
1310
1315
|
logger.error(`Unexpected case: ${valueAsString}`);
|
|
1311
1316
|
}
|
|
@@ -1377,19 +1382,20 @@ var BaseAcpAgent = class {
|
|
|
1377
1382
|
}
|
|
1378
1383
|
async cancel(params) {
|
|
1379
1384
|
if (this.sessionId !== params.sessionId) {
|
|
1380
|
-
throw new Error("Session
|
|
1385
|
+
throw new Error("Session ID mismatch");
|
|
1381
1386
|
}
|
|
1382
1387
|
this.session.cancelled = true;
|
|
1383
1388
|
const meta = params._meta;
|
|
1384
1389
|
if (meta?.interruptReason) {
|
|
1385
1390
|
this.session.interruptReason = meta.interruptReason;
|
|
1386
1391
|
}
|
|
1387
|
-
await this.
|
|
1392
|
+
await this.interrupt();
|
|
1388
1393
|
}
|
|
1389
1394
|
async closeSession() {
|
|
1390
1395
|
try {
|
|
1391
1396
|
this.session.abortController.abort();
|
|
1392
1397
|
await this.cancel({ sessionId: this.sessionId });
|
|
1398
|
+
this.session.settingsManager.dispose();
|
|
1393
1399
|
this.logger.info("Closed session", { sessionId: this.sessionId });
|
|
1394
1400
|
} catch (err) {
|
|
1395
1401
|
this.logger.warn("Failed to close session", {
|
|
@@ -1564,8 +1570,8 @@ var ToolContentBuilder = class {
|
|
|
1564
1570
|
this.items.push({ type: "content", content: image(data, mimeType, uri) });
|
|
1565
1571
|
return this;
|
|
1566
1572
|
}
|
|
1567
|
-
diff(
|
|
1568
|
-
this.items.push({ type: "diff", path:
|
|
1573
|
+
diff(path9, oldText, newText) {
|
|
1574
|
+
this.items.push({ type: "diff", path: path9, oldText, newText });
|
|
1569
1575
|
return this;
|
|
1570
1576
|
}
|
|
1571
1577
|
build() {
|
|
@@ -1585,7 +1591,7 @@ var registerHookCallback = (toolUseID, {
|
|
|
1585
1591
|
onPostToolUseHook
|
|
1586
1592
|
};
|
|
1587
1593
|
};
|
|
1588
|
-
var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
|
|
1594
|
+
var createPostToolUseHook = ({ onModeChange, logger }) => async (input, toolUseID) => {
|
|
1589
1595
|
if (input.hook_event_name === "PostToolUse") {
|
|
1590
1596
|
const toolName = input.tool_name;
|
|
1591
1597
|
if (onModeChange && toolName === "EnterPlanMode") {
|
|
@@ -1600,11 +1606,54 @@ var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
|
|
|
1600
1606
|
input.tool_response
|
|
1601
1607
|
);
|
|
1602
1608
|
delete toolUseCallbacks[toolUseID];
|
|
1609
|
+
} else {
|
|
1610
|
+
logger?.error(
|
|
1611
|
+
`No onPostToolUseHook found for tool use ID: ${toolUseID}`
|
|
1612
|
+
);
|
|
1613
|
+
delete toolUseCallbacks[toolUseID];
|
|
1603
1614
|
}
|
|
1604
1615
|
}
|
|
1605
1616
|
}
|
|
1606
1617
|
return { continue: true };
|
|
1607
1618
|
};
|
|
1619
|
+
var createPreToolUseHook = (settingsManager, logger) => async (input, _toolUseID) => {
|
|
1620
|
+
if (input.hook_event_name !== "PreToolUse") {
|
|
1621
|
+
return { continue: true };
|
|
1622
|
+
}
|
|
1623
|
+
const toolName = input.tool_name;
|
|
1624
|
+
const toolInput = input.tool_input;
|
|
1625
|
+
const permissionCheck = settingsManager.checkPermission(
|
|
1626
|
+
toolName,
|
|
1627
|
+
toolInput
|
|
1628
|
+
);
|
|
1629
|
+
if (permissionCheck.decision !== "ask") {
|
|
1630
|
+
logger.info(
|
|
1631
|
+
`[PreToolUseHook] Tool: ${toolName}, Decision: ${permissionCheck.decision}, Rule: ${permissionCheck.rule}`
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
switch (permissionCheck.decision) {
|
|
1635
|
+
case "allow":
|
|
1636
|
+
return {
|
|
1637
|
+
continue: true,
|
|
1638
|
+
hookSpecificOutput: {
|
|
1639
|
+
hookEventName: "PreToolUse",
|
|
1640
|
+
permissionDecision: "allow",
|
|
1641
|
+
permissionDecisionReason: `Allowed by settings rule: ${permissionCheck.rule}`
|
|
1642
|
+
}
|
|
1643
|
+
};
|
|
1644
|
+
case "deny":
|
|
1645
|
+
return {
|
|
1646
|
+
continue: true,
|
|
1647
|
+
hookSpecificOutput: {
|
|
1648
|
+
hookEventName: "PreToolUse",
|
|
1649
|
+
permissionDecision: "deny",
|
|
1650
|
+
permissionDecisionReason: `Denied by settings rule: ${permissionCheck.rule}`
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1653
|
+
default:
|
|
1654
|
+
return { continue: true };
|
|
1655
|
+
}
|
|
1656
|
+
};
|
|
1608
1657
|
|
|
1609
1658
|
// src/adapters/claude/mcp/tool-metadata.ts
|
|
1610
1659
|
var mcpToolMetadataCache = /* @__PURE__ */ new Map();
|
|
@@ -1681,85 +1730,14 @@ var SYSTEM_REMINDER = `
|
|
|
1681
1730
|
<system-reminder>
|
|
1682
1731
|
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.
|
|
1683
1732
|
</system-reminder>`;
|
|
1684
|
-
function
|
|
1685
|
-
let currentContent = fileContent;
|
|
1686
|
-
const randomHex = Array.from(crypto.getRandomValues(new Uint8Array(5))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1687
|
-
const markerPrefix = `__REPLACE_MARKER_${randomHex}_`;
|
|
1688
|
-
let markerCounter = 0;
|
|
1689
|
-
const markers = [];
|
|
1690
|
-
for (const edit of edits) {
|
|
1691
|
-
if (edit.oldText === "") {
|
|
1692
|
-
throw new Error(
|
|
1693
|
-
`The provided \`old_string\` is empty.
|
|
1694
|
-
|
|
1695
|
-
No edits were applied.`
|
|
1696
|
-
);
|
|
1697
|
-
}
|
|
1698
|
-
if (edit.replaceAll) {
|
|
1699
|
-
const parts = [];
|
|
1700
|
-
let lastIndex = 0;
|
|
1701
|
-
let searchIndex = 0;
|
|
1702
|
-
while (true) {
|
|
1703
|
-
const index = currentContent.indexOf(edit.oldText, searchIndex);
|
|
1704
|
-
if (index === -1) {
|
|
1705
|
-
if (searchIndex === 0) {
|
|
1706
|
-
throw new Error(
|
|
1707
|
-
`The provided \`old_string\` does not appear in the file: "${edit.oldText}".
|
|
1708
|
-
|
|
1709
|
-
No edits were applied.`
|
|
1710
|
-
);
|
|
1711
|
-
}
|
|
1712
|
-
break;
|
|
1713
|
-
}
|
|
1714
|
-
parts.push(currentContent.substring(lastIndex, index));
|
|
1715
|
-
const marker = `${markerPrefix}${markerCounter++}__`;
|
|
1716
|
-
markers.push(marker);
|
|
1717
|
-
parts.push(marker + edit.newText);
|
|
1718
|
-
lastIndex = index + edit.oldText.length;
|
|
1719
|
-
searchIndex = lastIndex;
|
|
1720
|
-
}
|
|
1721
|
-
parts.push(currentContent.substring(lastIndex));
|
|
1722
|
-
currentContent = parts.join("");
|
|
1723
|
-
} else {
|
|
1724
|
-
const index = currentContent.indexOf(edit.oldText);
|
|
1725
|
-
if (index === -1) {
|
|
1726
|
-
throw new Error(
|
|
1727
|
-
`The provided \`old_string\` does not appear in the file: "${edit.oldText}".
|
|
1728
|
-
|
|
1729
|
-
No edits were applied.`
|
|
1730
|
-
);
|
|
1731
|
-
} else {
|
|
1732
|
-
const marker = `${markerPrefix}${markerCounter++}__`;
|
|
1733
|
-
markers.push(marker);
|
|
1734
|
-
currentContent = currentContent.substring(0, index) + marker + edit.newText + currentContent.substring(index + edit.oldText.length);
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
const lineNumbers = [];
|
|
1739
|
-
for (const marker of markers) {
|
|
1740
|
-
const index = currentContent.indexOf(marker);
|
|
1741
|
-
if (index !== -1) {
|
|
1742
|
-
const lineNumber = Math.max(
|
|
1743
|
-
0,
|
|
1744
|
-
currentContent.substring(0, index).split(/\r\n|\r|\n/).length - 1
|
|
1745
|
-
);
|
|
1746
|
-
lineNumbers.push(lineNumber);
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
let finalContent = currentContent;
|
|
1750
|
-
for (const marker of markers) {
|
|
1751
|
-
finalContent = finalContent.replace(marker, "");
|
|
1752
|
-
}
|
|
1753
|
-
const uniqueLineNumbers = [...new Set(lineNumbers)].sort();
|
|
1754
|
-
return { newContent: finalContent, lineNumbers: uniqueLineNumbers };
|
|
1755
|
-
}
|
|
1756
|
-
function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ debug: false, prefix: "[ClaudeTools]" })) {
|
|
1733
|
+
function toolInfoFromToolUse(toolUse, options) {
|
|
1757
1734
|
const name = toolUse.name;
|
|
1758
1735
|
const input = toolUse.input;
|
|
1759
1736
|
switch (name) {
|
|
1760
1737
|
case "Task":
|
|
1738
|
+
case "Agent":
|
|
1761
1739
|
return {
|
|
1762
|
-
title: input?.description ? String(input.description) :
|
|
1740
|
+
title: input?.description ? String(input.description) : name,
|
|
1763
1741
|
kind: "think",
|
|
1764
1742
|
content: input?.prompt ? toolContent().text(String(input.prompt)).build() : []
|
|
1765
1743
|
};
|
|
@@ -1778,6 +1756,13 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1778
1756
|
locations: input?.notebook_path ? [{ path: String(input.notebook_path) }] : []
|
|
1779
1757
|
};
|
|
1780
1758
|
case "Bash":
|
|
1759
|
+
if (options?.supportsTerminalOutput && options?.toolUseId) {
|
|
1760
|
+
return {
|
|
1761
|
+
title: input?.description ? String(input.description) : "Execute command",
|
|
1762
|
+
kind: "execute",
|
|
1763
|
+
content: [{ type: "terminal", terminalId: options.toolUseId }]
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1781
1766
|
return {
|
|
1782
1767
|
title: input?.description ? String(input.description) : "Execute command",
|
|
1783
1768
|
kind: "execute",
|
|
@@ -1798,11 +1783,11 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1798
1783
|
case "Read": {
|
|
1799
1784
|
let limit = "";
|
|
1800
1785
|
const inputLimit = input?.limit;
|
|
1801
|
-
const inputOffset = input?.offset ??
|
|
1786
|
+
const inputOffset = input?.offset ?? 1;
|
|
1802
1787
|
if (inputLimit) {
|
|
1803
|
-
limit = ` (${inputOffset
|
|
1804
|
-
} else if (inputOffset) {
|
|
1805
|
-
limit = ` (from line ${inputOffset
|
|
1788
|
+
limit = ` (${inputOffset} - ${inputOffset + inputLimit - 1})`;
|
|
1789
|
+
} else if (inputOffset > 1) {
|
|
1790
|
+
limit = ` (from line ${inputOffset})`;
|
|
1806
1791
|
}
|
|
1807
1792
|
return {
|
|
1808
1793
|
title: `Read ${input?.file_path ? String(input.file_path) : "File"}${limit}`,
|
|
@@ -1824,39 +1809,21 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1824
1809
|
locations: []
|
|
1825
1810
|
};
|
|
1826
1811
|
case "Edit": {
|
|
1827
|
-
const
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
let affectedLines = [];
|
|
1831
|
-
if (path8 && oldText) {
|
|
1832
|
-
try {
|
|
1833
|
-
const oldContent = cachedFileContent[path8] || "";
|
|
1834
|
-
const newContent = replaceAndCalculateLocation(oldContent, [
|
|
1835
|
-
{
|
|
1836
|
-
oldText,
|
|
1837
|
-
newText,
|
|
1838
|
-
replaceAll: false
|
|
1839
|
-
}
|
|
1840
|
-
]);
|
|
1841
|
-
oldText = oldContent;
|
|
1842
|
-
newText = newContent.newContent;
|
|
1843
|
-
affectedLines = newContent.lineNumbers;
|
|
1844
|
-
} catch (e) {
|
|
1845
|
-
logger.error("Failed to edit file", e);
|
|
1846
|
-
}
|
|
1847
|
-
}
|
|
1812
|
+
const path9 = input?.file_path ? String(input.file_path) : void 0;
|
|
1813
|
+
const oldText = input?.old_string ? String(input.old_string) : null;
|
|
1814
|
+
const newText = input?.new_string ? String(input.new_string) : "";
|
|
1848
1815
|
return {
|
|
1849
|
-
title:
|
|
1816
|
+
title: path9 ? `Edit \`${path9}\`` : "Edit",
|
|
1850
1817
|
kind: "edit",
|
|
1851
|
-
content: input &&
|
|
1818
|
+
content: input && path9 ? [
|
|
1852
1819
|
{
|
|
1853
1820
|
type: "diff",
|
|
1854
|
-
path:
|
|
1821
|
+
path: path9,
|
|
1855
1822
|
oldText,
|
|
1856
1823
|
newText
|
|
1857
1824
|
}
|
|
1858
1825
|
] : [],
|
|
1859
|
-
locations:
|
|
1826
|
+
locations: path9 ? [{ path: path9 }] : []
|
|
1860
1827
|
};
|
|
1861
1828
|
}
|
|
1862
1829
|
case "Write": {
|
|
@@ -1910,10 +1877,10 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1910
1877
|
}
|
|
1911
1878
|
if (input?.output_mode) {
|
|
1912
1879
|
switch (input.output_mode) {
|
|
1913
|
-
case "
|
|
1880
|
+
case "files_with_matches":
|
|
1914
1881
|
label += " -l";
|
|
1915
1882
|
break;
|
|
1916
|
-
case "
|
|
1883
|
+
case "count":
|
|
1917
1884
|
label += " -c";
|
|
1918
1885
|
break;
|
|
1919
1886
|
default:
|
|
@@ -1932,7 +1899,9 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1932
1899
|
if (input?.multiline) {
|
|
1933
1900
|
label += " -P";
|
|
1934
1901
|
}
|
|
1935
|
-
|
|
1902
|
+
if (input?.pattern) {
|
|
1903
|
+
label += ` "${String(input.pattern)}"`;
|
|
1904
|
+
}
|
|
1936
1905
|
if (input?.path) {
|
|
1937
1906
|
label += ` ${String(input.path)}`;
|
|
1938
1907
|
}
|
|
@@ -2026,7 +1995,49 @@ function mcpToolInfo(name, _input) {
|
|
|
2026
1995
|
content: []
|
|
2027
1996
|
};
|
|
2028
1997
|
}
|
|
2029
|
-
function
|
|
1998
|
+
function toolUpdateFromEditToolResponse(toolResponse) {
|
|
1999
|
+
if (!toolResponse || typeof toolResponse !== "object") return null;
|
|
2000
|
+
const response = toolResponse;
|
|
2001
|
+
const patches = response.structuredPatch;
|
|
2002
|
+
if (!Array.isArray(patches) || patches.length === 0) return null;
|
|
2003
|
+
const content = [];
|
|
2004
|
+
const locations = [];
|
|
2005
|
+
for (const patch of patches) {
|
|
2006
|
+
if (!patch.hunks || patch.hunks.length === 0) continue;
|
|
2007
|
+
const filePath = patch.newFileName || patch.oldFileName;
|
|
2008
|
+
const oldLines = [];
|
|
2009
|
+
const newLines = [];
|
|
2010
|
+
for (const hunk of patch.hunks) {
|
|
2011
|
+
for (const line of hunk.lines) {
|
|
2012
|
+
if (line.startsWith("-")) {
|
|
2013
|
+
oldLines.push(line.slice(1));
|
|
2014
|
+
} else if (line.startsWith("+")) {
|
|
2015
|
+
newLines.push(line.slice(1));
|
|
2016
|
+
} else if (line.startsWith(" ")) {
|
|
2017
|
+
oldLines.push(line.slice(1));
|
|
2018
|
+
newLines.push(line.slice(1));
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
content.push({
|
|
2023
|
+
type: "diff",
|
|
2024
|
+
path: filePath,
|
|
2025
|
+
oldText: oldLines.join("\n"),
|
|
2026
|
+
newText: newLines.join("\n")
|
|
2027
|
+
});
|
|
2028
|
+
const firstHunk = patch.hunks[0];
|
|
2029
|
+
locations.push({
|
|
2030
|
+
path: filePath,
|
|
2031
|
+
line: firstHunk.newStart
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
if (content.length === 0) return null;
|
|
2035
|
+
return { content, locations };
|
|
2036
|
+
}
|
|
2037
|
+
function toolUpdateFromToolResult(toolResult, toolUse, options) {
|
|
2038
|
+
if ("is_error" in toolResult && toolResult.is_error && toolResult.content && toolResult.content.length > 0) {
|
|
2039
|
+
return toAcpContentUpdate(toolResult.content, true);
|
|
2040
|
+
}
|
|
2030
2041
|
switch (toolUse?.name) {
|
|
2031
2042
|
case "Read":
|
|
2032
2043
|
if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
|
|
@@ -2043,6 +2054,16 @@ function toolUpdateFromToolResult(toolResult, toolUse) {
|
|
|
2043
2054
|
)
|
|
2044
2055
|
};
|
|
2045
2056
|
}
|
|
2057
|
+
if (itemObj.type === "image" && itemObj.source) {
|
|
2058
|
+
return {
|
|
2059
|
+
type: "content",
|
|
2060
|
+
content: {
|
|
2061
|
+
type: "image",
|
|
2062
|
+
data: itemObj.source.data ?? "",
|
|
2063
|
+
mimeType: itemObj.source.media_type ?? "image/png"
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2046
2067
|
return {
|
|
2047
2068
|
type: "content",
|
|
2048
2069
|
content: item
|
|
@@ -2058,18 +2079,51 @@ function toolUpdateFromToolResult(toolResult, toolUse) {
|
|
|
2058
2079
|
}
|
|
2059
2080
|
return {};
|
|
2060
2081
|
case "Bash": {
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2082
|
+
const result = toolResult.content;
|
|
2083
|
+
const terminalId = "tool_use_id" in toolResult ? String(toolResult.tool_use_id) : "";
|
|
2084
|
+
const isError = "is_error" in toolResult && toolResult.is_error;
|
|
2085
|
+
let output = "";
|
|
2086
|
+
let exitCode = isError ? 1 : 0;
|
|
2087
|
+
if (result && typeof result === "object" && "type" in result && result.type === "bash_code_execution_result") {
|
|
2088
|
+
const bashResult = result;
|
|
2089
|
+
output = [bashResult.stdout, bashResult.stderr].filter(Boolean).join("\n");
|
|
2090
|
+
exitCode = bashResult.return_code;
|
|
2091
|
+
} else if (typeof result === "string") {
|
|
2092
|
+
output = result;
|
|
2093
|
+
} else if (Array.isArray(result) && result.length > 0 && "text" in result[0] && typeof result[0].text === "string") {
|
|
2094
|
+
output = result.map((c) => c.text ?? "").join("\n");
|
|
2095
|
+
}
|
|
2096
|
+
if (options?.supportsTerminalOutput) {
|
|
2097
|
+
return {
|
|
2098
|
+
content: [{ type: "terminal", terminalId }],
|
|
2099
|
+
_meta: {
|
|
2100
|
+
terminal_info: {
|
|
2101
|
+
terminal_id: terminalId
|
|
2102
|
+
},
|
|
2103
|
+
terminal_output: {
|
|
2104
|
+
terminal_id: terminalId,
|
|
2105
|
+
data: output
|
|
2106
|
+
},
|
|
2107
|
+
terminal_exit: {
|
|
2108
|
+
terminal_id: terminalId,
|
|
2109
|
+
exit_code: exitCode,
|
|
2110
|
+
signal: null
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
if (output.trim()) {
|
|
2116
|
+
return {
|
|
2117
|
+
content: toolContent().text(`\`\`\`console
|
|
2118
|
+
${output.trimEnd()}
|
|
2119
|
+
\`\`\``).build()
|
|
2120
|
+
};
|
|
2070
2121
|
}
|
|
2071
2122
|
return {};
|
|
2072
2123
|
}
|
|
2124
|
+
case "Edit":
|
|
2125
|
+
case "Write":
|
|
2126
|
+
return {};
|
|
2073
2127
|
case "ExitPlanMode": {
|
|
2074
2128
|
return { title: "Exited Plan Mode" };
|
|
2075
2129
|
}
|
|
@@ -2229,6 +2283,7 @@ function handleThinkingChunk(chunk, parentToolCallId) {
|
|
|
2229
2283
|
return update;
|
|
2230
2284
|
}
|
|
2231
2285
|
function handleToolUseChunk(chunk, ctx) {
|
|
2286
|
+
const alreadyCached = chunk.id in ctx.toolUseCache;
|
|
2232
2287
|
ctx.toolUseCache[chunk.id] = chunk;
|
|
2233
2288
|
if (chunk.name === "TodoWrite") {
|
|
2234
2289
|
const input = chunk.input;
|
|
@@ -2240,37 +2295,60 @@ function handleToolUseChunk(chunk, ctx) {
|
|
|
2240
2295
|
}
|
|
2241
2296
|
return null;
|
|
2242
2297
|
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2298
|
+
if (!alreadyCached && ctx.registerHooks !== false) {
|
|
2299
|
+
registerHookCallback(chunk.id, {
|
|
2300
|
+
onPostToolUseHook: async (toolUseId, _toolInput, toolResponse) => {
|
|
2301
|
+
const toolUse = ctx.toolUseCache[toolUseId];
|
|
2302
|
+
if (toolUse) {
|
|
2303
|
+
const editUpdate = toolUse.name === "Edit" ? toolUpdateFromEditToolResponse(toolResponse) : null;
|
|
2304
|
+
await ctx.client.sessionUpdate({
|
|
2305
|
+
sessionId: ctx.sessionId,
|
|
2306
|
+
update: {
|
|
2307
|
+
_meta: toolMeta(toolUse.name, toolResponse, ctx.parentToolCallId),
|
|
2308
|
+
toolCallId: toolUseId,
|
|
2309
|
+
sessionUpdate: "tool_call_update",
|
|
2310
|
+
...editUpdate ? editUpdate : {}
|
|
2311
|
+
}
|
|
2312
|
+
});
|
|
2313
|
+
} else {
|
|
2314
|
+
ctx.logger.error(
|
|
2315
|
+
`Got a tool response for tool use that wasn't tracked: ${toolUseId}`
|
|
2316
|
+
);
|
|
2317
|
+
}
|
|
2259
2318
|
}
|
|
2260
|
-
}
|
|
2261
|
-
}
|
|
2319
|
+
});
|
|
2320
|
+
}
|
|
2262
2321
|
let rawInput;
|
|
2263
2322
|
try {
|
|
2264
2323
|
rawInput = JSON.parse(JSON.stringify(chunk.input));
|
|
2265
2324
|
} catch {
|
|
2266
2325
|
}
|
|
2326
|
+
const toolInfo = toolInfoFromToolUse(chunk, {
|
|
2327
|
+
supportsTerminalOutput: ctx.supportsTerminalOutput,
|
|
2328
|
+
toolUseId: chunk.id
|
|
2329
|
+
});
|
|
2330
|
+
const meta = {
|
|
2331
|
+
...toolMeta(chunk.name, void 0, ctx.parentToolCallId)
|
|
2332
|
+
};
|
|
2333
|
+
if (chunk.name === "Bash" && ctx.supportsTerminalOutput && !alreadyCached) {
|
|
2334
|
+
meta.terminal_info = { terminal_id: chunk.id };
|
|
2335
|
+
}
|
|
2336
|
+
if (alreadyCached) {
|
|
2337
|
+
return {
|
|
2338
|
+
_meta: meta,
|
|
2339
|
+
toolCallId: chunk.id,
|
|
2340
|
+
sessionUpdate: "tool_call_update",
|
|
2341
|
+
rawInput,
|
|
2342
|
+
...toolInfo
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2267
2345
|
return {
|
|
2268
|
-
_meta:
|
|
2346
|
+
_meta: meta,
|
|
2269
2347
|
toolCallId: chunk.id,
|
|
2270
2348
|
sessionUpdate: "tool_call",
|
|
2271
2349
|
rawInput,
|
|
2272
2350
|
status: "pending",
|
|
2273
|
-
...
|
|
2351
|
+
...toolInfo
|
|
2274
2352
|
};
|
|
2275
2353
|
}
|
|
2276
2354
|
function handleToolResultChunk(chunk, ctx) {
|
|
@@ -2279,36 +2357,71 @@ function handleToolResultChunk(chunk, ctx) {
|
|
|
2279
2357
|
ctx.logger.error(
|
|
2280
2358
|
`Got a tool result for tool use that wasn't tracked: ${chunk.tool_use_id}`
|
|
2281
2359
|
);
|
|
2282
|
-
return
|
|
2360
|
+
return [];
|
|
2283
2361
|
}
|
|
2284
2362
|
if (toolUse.name === "TodoWrite") {
|
|
2285
|
-
return
|
|
2363
|
+
return [];
|
|
2286
2364
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2365
|
+
const { _meta: resultMeta, ...toolUpdate } = toolUpdateFromToolResult(
|
|
2366
|
+
chunk,
|
|
2367
|
+
toolUse,
|
|
2368
|
+
{
|
|
2369
|
+
supportsTerminalOutput: ctx.supportsTerminalOutput,
|
|
2370
|
+
toolUseId: chunk.tool_use_id
|
|
2371
|
+
}
|
|
2372
|
+
);
|
|
2373
|
+
const updates = [];
|
|
2374
|
+
if (resultMeta?.terminal_output) {
|
|
2375
|
+
const terminalOutputMeta = {
|
|
2376
|
+
terminal_output: resultMeta.terminal_output
|
|
2377
|
+
};
|
|
2378
|
+
if (ctx.parentToolCallId) {
|
|
2379
|
+
terminalOutputMeta.claudeCode = {
|
|
2380
|
+
parentToolCallId: ctx.parentToolCallId
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
updates.push({
|
|
2384
|
+
_meta: terminalOutputMeta,
|
|
2385
|
+
toolCallId: chunk.tool_use_id,
|
|
2386
|
+
sessionUpdate: "tool_call_update"
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
2389
|
+
const meta = {
|
|
2390
|
+
...toolMeta(toolUse.name, void 0, ctx.parentToolCallId),
|
|
2391
|
+
...resultMeta?.terminal_exit ? { terminal_exit: resultMeta.terminal_exit } : {}
|
|
2392
|
+
};
|
|
2393
|
+
updates.push({
|
|
2394
|
+
_meta: meta,
|
|
2289
2395
|
toolCallId: chunk.tool_use_id,
|
|
2290
2396
|
sessionUpdate: "tool_call_update",
|
|
2291
2397
|
status: chunk.is_error ? "failed" : "completed",
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
};
|
|
2398
|
+
rawOutput: chunk.content,
|
|
2399
|
+
...toolUpdate
|
|
2400
|
+
});
|
|
2401
|
+
return updates;
|
|
2297
2402
|
}
|
|
2298
2403
|
function processContentChunk(chunk, role, ctx) {
|
|
2299
2404
|
switch (chunk.type) {
|
|
2300
2405
|
case "text":
|
|
2301
|
-
case "text_delta":
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2406
|
+
case "text_delta": {
|
|
2407
|
+
const update = handleTextChunk(chunk, role, ctx.parentToolCallId);
|
|
2408
|
+
return update ? [update] : [];
|
|
2409
|
+
}
|
|
2410
|
+
case "image": {
|
|
2411
|
+
const update = handleImageChunk(chunk, role);
|
|
2412
|
+
return update ? [update] : [];
|
|
2413
|
+
}
|
|
2305
2414
|
case "thinking":
|
|
2306
|
-
case "thinking_delta":
|
|
2307
|
-
|
|
2415
|
+
case "thinking_delta": {
|
|
2416
|
+
const update = handleThinkingChunk(chunk, ctx.parentToolCallId);
|
|
2417
|
+
return update ? [update] : [];
|
|
2418
|
+
}
|
|
2308
2419
|
case "tool_use":
|
|
2309
2420
|
case "server_tool_use":
|
|
2310
|
-
case "mcp_tool_use":
|
|
2311
|
-
|
|
2421
|
+
case "mcp_tool_use": {
|
|
2422
|
+
const update = handleToolUseChunk(chunk, ctx);
|
|
2423
|
+
return update ? [update] : [];
|
|
2424
|
+
}
|
|
2312
2425
|
case "tool_result":
|
|
2313
2426
|
case "tool_search_tool_result":
|
|
2314
2427
|
case "web_fetch_tool_result":
|
|
@@ -2330,13 +2443,13 @@ function processContentChunk(chunk, role, ctx) {
|
|
|
2330
2443
|
case "container_upload":
|
|
2331
2444
|
case "compaction":
|
|
2332
2445
|
case "compaction_delta":
|
|
2333
|
-
return
|
|
2446
|
+
return [];
|
|
2334
2447
|
default:
|
|
2335
2448
|
unreachable(chunk, ctx.logger);
|
|
2336
|
-
return
|
|
2449
|
+
return [];
|
|
2337
2450
|
}
|
|
2338
2451
|
}
|
|
2339
|
-
function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId) {
|
|
2452
|
+
function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput) {
|
|
2340
2453
|
if (typeof content === "string") {
|
|
2341
2454
|
const update = {
|
|
2342
2455
|
sessionUpdate: messageUpdateType(role),
|
|
@@ -2357,18 +2470,19 @@ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentC
|
|
|
2357
2470
|
fileContentCache,
|
|
2358
2471
|
client,
|
|
2359
2472
|
logger,
|
|
2360
|
-
parentToolCallId
|
|
2473
|
+
parentToolCallId,
|
|
2474
|
+
registerHooks,
|
|
2475
|
+
supportsTerminalOutput
|
|
2361
2476
|
};
|
|
2362
2477
|
const output = [];
|
|
2363
2478
|
for (const chunk of content) {
|
|
2364
|
-
const update
|
|
2365
|
-
if (update) {
|
|
2479
|
+
for (const update of processContentChunk(chunk, role, ctx)) {
|
|
2366
2480
|
output.push({ sessionId, update });
|
|
2367
2481
|
}
|
|
2368
2482
|
}
|
|
2369
2483
|
return output;
|
|
2370
2484
|
}
|
|
2371
|
-
function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId) {
|
|
2485
|
+
function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput) {
|
|
2372
2486
|
const event = message.event;
|
|
2373
2487
|
switch (event.type) {
|
|
2374
2488
|
case "content_block_start":
|
|
@@ -2380,7 +2494,9 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
|
|
|
2380
2494
|
fileContentCache,
|
|
2381
2495
|
client,
|
|
2382
2496
|
logger,
|
|
2383
|
-
parentToolCallId
|
|
2497
|
+
parentToolCallId,
|
|
2498
|
+
registerHooks,
|
|
2499
|
+
supportsTerminalOutput
|
|
2384
2500
|
);
|
|
2385
2501
|
case "content_block_delta":
|
|
2386
2502
|
return toAcpNotifications(
|
|
@@ -2391,7 +2507,9 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
|
|
|
2391
2507
|
fileContentCache,
|
|
2392
2508
|
client,
|
|
2393
2509
|
logger,
|
|
2394
|
-
parentToolCallId
|
|
2510
|
+
parentToolCallId,
|
|
2511
|
+
registerHooks,
|
|
2512
|
+
supportsTerminalOutput
|
|
2395
2513
|
);
|
|
2396
2514
|
case "message_start":
|
|
2397
2515
|
case "message_delta":
|
|
@@ -2450,29 +2568,25 @@ async function handleSystemMessage(message, context) {
|
|
|
2450
2568
|
break;
|
|
2451
2569
|
}
|
|
2452
2570
|
}
|
|
2453
|
-
function handleResultMessage(message
|
|
2454
|
-
const
|
|
2455
|
-
if (session.cancelled) {
|
|
2456
|
-
return {
|
|
2457
|
-
shouldStop: true,
|
|
2458
|
-
stopReason: "cancelled"
|
|
2459
|
-
};
|
|
2460
|
-
}
|
|
2571
|
+
function handleResultMessage(message) {
|
|
2572
|
+
const usage = extractUsageFromResult(message);
|
|
2461
2573
|
switch (message.subtype) {
|
|
2462
2574
|
case "success": {
|
|
2463
2575
|
if (message.result.includes("Please run /login")) {
|
|
2464
2576
|
return {
|
|
2465
2577
|
shouldStop: true,
|
|
2466
|
-
error: import_sdk.RequestError.authRequired()
|
|
2578
|
+
error: import_sdk.RequestError.authRequired(),
|
|
2579
|
+
usage
|
|
2467
2580
|
};
|
|
2468
2581
|
}
|
|
2469
2582
|
if (message.is_error) {
|
|
2470
2583
|
return {
|
|
2471
2584
|
shouldStop: true,
|
|
2472
|
-
error: import_sdk.RequestError.internalError(void 0, message.result)
|
|
2585
|
+
error: import_sdk.RequestError.internalError(void 0, message.result),
|
|
2586
|
+
usage
|
|
2473
2587
|
};
|
|
2474
2588
|
}
|
|
2475
|
-
return { shouldStop: true, stopReason: "end_turn" };
|
|
2589
|
+
return { shouldStop: true, stopReason: "end_turn", usage };
|
|
2476
2590
|
}
|
|
2477
2591
|
case "error_during_execution":
|
|
2478
2592
|
if (message.is_error) {
|
|
@@ -2481,10 +2595,11 @@ function handleResultMessage(message, context) {
|
|
|
2481
2595
|
error: import_sdk.RequestError.internalError(
|
|
2482
2596
|
void 0,
|
|
2483
2597
|
message.errors.join(", ") || message.subtype
|
|
2484
|
-
)
|
|
2598
|
+
),
|
|
2599
|
+
usage
|
|
2485
2600
|
};
|
|
2486
2601
|
}
|
|
2487
|
-
return { shouldStop: true, stopReason: "end_turn" };
|
|
2602
|
+
return { shouldStop: true, stopReason: "end_turn", usage };
|
|
2488
2603
|
case "error_max_budget_usd":
|
|
2489
2604
|
case "error_max_turns":
|
|
2490
2605
|
case "error_max_structured_output_retries":
|
|
@@ -2494,13 +2609,37 @@ function handleResultMessage(message, context) {
|
|
|
2494
2609
|
error: import_sdk.RequestError.internalError(
|
|
2495
2610
|
void 0,
|
|
2496
2611
|
message.errors.join(", ") || message.subtype
|
|
2497
|
-
)
|
|
2612
|
+
),
|
|
2613
|
+
usage
|
|
2498
2614
|
};
|
|
2499
2615
|
}
|
|
2500
|
-
return { shouldStop: true, stopReason: "max_turn_requests" };
|
|
2616
|
+
return { shouldStop: true, stopReason: "max_turn_requests", usage };
|
|
2501
2617
|
default:
|
|
2502
|
-
return { shouldStop: false };
|
|
2618
|
+
return { shouldStop: false, usage };
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
function extractUsageFromResult(message) {
|
|
2622
|
+
const msg = message;
|
|
2623
|
+
const msgUsage = msg.usage;
|
|
2624
|
+
if (!msgUsage) return void 0;
|
|
2625
|
+
const modelUsage = msg.modelUsage;
|
|
2626
|
+
let contextWindowSize;
|
|
2627
|
+
if (modelUsage) {
|
|
2628
|
+
const contextWindows = Object.values(modelUsage).map(
|
|
2629
|
+
(m) => m.contextWindow
|
|
2630
|
+
);
|
|
2631
|
+
if (contextWindows.length > 0) {
|
|
2632
|
+
contextWindowSize = Math.min(...contextWindows);
|
|
2633
|
+
}
|
|
2503
2634
|
}
|
|
2635
|
+
return {
|
|
2636
|
+
inputTokens: msgUsage.input_tokens ?? 0,
|
|
2637
|
+
outputTokens: msgUsage.output_tokens ?? 0,
|
|
2638
|
+
cachedReadTokens: msgUsage.cache_read_input_tokens ?? 0,
|
|
2639
|
+
cachedWriteTokens: msgUsage.cache_creation_input_tokens ?? 0,
|
|
2640
|
+
costUsd: typeof msg.total_cost_usd === "number" ? msg.total_cost_usd : void 0,
|
|
2641
|
+
contextWindowSize
|
|
2642
|
+
};
|
|
2504
2643
|
}
|
|
2505
2644
|
async function handleStreamEvent(message, context) {
|
|
2506
2645
|
const { sessionId, client, toolUseCache, fileContentCache, logger } = context;
|
|
@@ -2512,7 +2651,9 @@ async function handleStreamEvent(message, context) {
|
|
|
2512
2651
|
fileContentCache,
|
|
2513
2652
|
client,
|
|
2514
2653
|
logger,
|
|
2515
|
-
parentToolCallId
|
|
2654
|
+
parentToolCallId,
|
|
2655
|
+
context.registerHooks,
|
|
2656
|
+
context.supportsTerminalOutput
|
|
2516
2657
|
)) {
|
|
2517
2658
|
await client.sessionUpdate(notification);
|
|
2518
2659
|
context.session.notificationHistory.push(notification);
|
|
@@ -2524,14 +2665,15 @@ function hasLocalCommandStdout(content) {
|
|
|
2524
2665
|
function hasLocalCommandStderr(content) {
|
|
2525
2666
|
return typeof content === "string" && content.includes("<local-command-stderr>");
|
|
2526
2667
|
}
|
|
2527
|
-
function isSimpleUserMessage(message) {
|
|
2528
|
-
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");
|
|
2529
|
-
}
|
|
2530
2668
|
function isLoginRequiredMessage(message) {
|
|
2531
2669
|
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;
|
|
2532
2670
|
}
|
|
2671
|
+
function isPlainTextUserMessage(message) {
|
|
2672
|
+
const content = message.message.content;
|
|
2673
|
+
return message.type === "user" && (typeof content === "string" || Array.isArray(content) && content.length === 1 && content[0].type === "text");
|
|
2674
|
+
}
|
|
2533
2675
|
function shouldSkipUserAssistantMessage(message) {
|
|
2534
|
-
return hasLocalCommandStdout(message.message.content) || hasLocalCommandStderr(message.message.content) ||
|
|
2676
|
+
return hasLocalCommandStdout(message.message.content) || hasLocalCommandStderr(message.message.content) || isLoginRequiredMessage(message);
|
|
2535
2677
|
}
|
|
2536
2678
|
function logSpecialMessages(message, logger) {
|
|
2537
2679
|
const content = message.message.content;
|
|
@@ -2552,18 +2694,33 @@ function filterMessageContent(content) {
|
|
|
2552
2694
|
}
|
|
2553
2695
|
async function handleUserAssistantMessage(message, context) {
|
|
2554
2696
|
const { session, sessionId, client, toolUseCache, fileContentCache, logger } = context;
|
|
2555
|
-
if (session.cancelled) {
|
|
2556
|
-
return {};
|
|
2557
|
-
}
|
|
2558
2697
|
if (shouldSkipUserAssistantMessage(message)) {
|
|
2698
|
+
const content2 = message.message.content;
|
|
2699
|
+
if (typeof content2 === "string" && hasLocalCommandStdout(content2) && content2.includes("Context Usage")) {
|
|
2700
|
+
const stripped = content2.replace("<local-command-stdout>", "").replace("</local-command-stdout>", "");
|
|
2701
|
+
for (const notification of toAcpNotifications(
|
|
2702
|
+
stripped,
|
|
2703
|
+
"assistant",
|
|
2704
|
+
sessionId,
|
|
2705
|
+
toolUseCache,
|
|
2706
|
+
fileContentCache,
|
|
2707
|
+
client,
|
|
2708
|
+
logger
|
|
2709
|
+
)) {
|
|
2710
|
+
await client.sessionUpdate(notification);
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2559
2713
|
logSpecialMessages(message, logger);
|
|
2560
2714
|
if (isLoginRequiredMessage(message)) {
|
|
2561
2715
|
return { shouldStop: true, error: import_sdk.RequestError.authRequired() };
|
|
2562
2716
|
}
|
|
2563
2717
|
return {};
|
|
2564
2718
|
}
|
|
2719
|
+
if (isPlainTextUserMessage(message)) {
|
|
2720
|
+
return {};
|
|
2721
|
+
}
|
|
2565
2722
|
const content = message.message.content;
|
|
2566
|
-
const contentToProcess = filterMessageContent(content);
|
|
2723
|
+
const contentToProcess = message.type === "assistant" ? filterMessageContent(content) : content;
|
|
2567
2724
|
const parentToolCallId = "parent_tool_use_id" in message ? message.parent_tool_use_id ?? void 0 : void 0;
|
|
2568
2725
|
for (const notification of toAcpNotifications(
|
|
2569
2726
|
contentToProcess,
|
|
@@ -2573,7 +2730,9 @@ async function handleUserAssistantMessage(message, context) {
|
|
|
2573
2730
|
fileContentCache,
|
|
2574
2731
|
client,
|
|
2575
2732
|
logger,
|
|
2576
|
-
parentToolCallId
|
|
2733
|
+
parentToolCallId,
|
|
2734
|
+
context.registerHooks,
|
|
2735
|
+
context.supportsTerminalOutput
|
|
2577
2736
|
)) {
|
|
2578
2737
|
await client.sessionUpdate(notification);
|
|
2579
2738
|
session.notificationHistory.push(notification);
|
|
@@ -2659,36 +2818,45 @@ function normalizeAskUserQuestionInput(input) {
|
|
|
2659
2818
|
}
|
|
2660
2819
|
|
|
2661
2820
|
// src/execution-mode.ts
|
|
2662
|
-
var
|
|
2821
|
+
var ALLOW_BYPASS = !IS_ROOT;
|
|
2822
|
+
var availableModes = [
|
|
2663
2823
|
{
|
|
2664
2824
|
id: "default",
|
|
2665
|
-
name: "
|
|
2666
|
-
description: "
|
|
2825
|
+
name: "Default",
|
|
2826
|
+
description: "Standard behavior, prompts for dangerous operations"
|
|
2667
2827
|
},
|
|
2668
2828
|
{
|
|
2669
2829
|
id: "acceptEdits",
|
|
2670
2830
|
name: "Accept Edits",
|
|
2671
|
-
description: "
|
|
2831
|
+
description: "Auto-accept file edit operations"
|
|
2672
2832
|
},
|
|
2673
2833
|
{
|
|
2674
2834
|
id: "plan",
|
|
2675
2835
|
name: "Plan Mode",
|
|
2676
|
-
description: "
|
|
2677
|
-
},
|
|
2678
|
-
{
|
|
2679
|
-
id: "bypassPermissions",
|
|
2680
|
-
name: "Bypass Permissions",
|
|
2681
|
-
description: "Skips all permission prompts"
|
|
2836
|
+
description: "Planning mode, no actual tool execution"
|
|
2682
2837
|
}
|
|
2838
|
+
// {
|
|
2839
|
+
// id: "dontAsk",
|
|
2840
|
+
// name: "Don't Ask",
|
|
2841
|
+
// description: "Don't prompt for permissions, deny if not pre-approved",
|
|
2842
|
+
// },
|
|
2683
2843
|
];
|
|
2844
|
+
if (ALLOW_BYPASS) {
|
|
2845
|
+
availableModes.push({
|
|
2846
|
+
id: "bypassPermissions",
|
|
2847
|
+
name: "Bypass Permissions",
|
|
2848
|
+
description: "Bypass all permission checks"
|
|
2849
|
+
});
|
|
2850
|
+
}
|
|
2684
2851
|
var TWIG_EXECUTION_MODES = [
|
|
2685
2852
|
"default",
|
|
2686
2853
|
"acceptEdits",
|
|
2687
2854
|
"plan",
|
|
2855
|
+
// "dontAsk",
|
|
2688
2856
|
"bypassPermissions"
|
|
2689
2857
|
];
|
|
2690
2858
|
function getAvailableModes() {
|
|
2691
|
-
return IS_ROOT ?
|
|
2859
|
+
return IS_ROOT ? availableModes.filter((m) => m.id !== "bypassPermissions") : availableModes;
|
|
2692
2860
|
}
|
|
2693
2861
|
|
|
2694
2862
|
// src/adapters/claude/tools.ts
|
|
@@ -2716,6 +2884,7 @@ var AUTO_ALLOWED_TOOLS = {
|
|
|
2716
2884
|
default: new Set(BASE_ALLOWED_TOOLS),
|
|
2717
2885
|
acceptEdits: /* @__PURE__ */ new Set([...BASE_ALLOWED_TOOLS, ...WRITE_TOOLS]),
|
|
2718
2886
|
plan: new Set(BASE_ALLOWED_TOOLS)
|
|
2887
|
+
// dontAsk: new Set(BASE_ALLOWED_TOOLS),
|
|
2719
2888
|
};
|
|
2720
2889
|
function isToolAllowedForMode(toolName, mode) {
|
|
2721
2890
|
if (mode === "bypassPermissions") {
|
|
@@ -2863,12 +3032,11 @@ async function validatePlanContent(planText, context) {
|
|
|
2863
3032
|
return { valid: true };
|
|
2864
3033
|
}
|
|
2865
3034
|
async function requestPlanApproval(context, updatedInput) {
|
|
2866
|
-
const { client, sessionId, toolUseID
|
|
2867
|
-
const toolInfo = toolInfoFromToolUse(
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
);
|
|
3035
|
+
const { client, sessionId, toolUseID } = context;
|
|
3036
|
+
const toolInfo = toolInfoFromToolUse({
|
|
3037
|
+
name: context.toolName,
|
|
3038
|
+
input: updatedInput
|
|
3039
|
+
});
|
|
2872
3040
|
return await client.requestPermission({
|
|
2873
3041
|
options: buildExitPlanModePermissionOptions(),
|
|
2874
3042
|
sessionId,
|
|
@@ -2887,7 +3055,14 @@ async function applyPlanApproval(response, context, updatedInput) {
|
|
|
2887
3055
|
if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "default" || response.outcome.optionId === "acceptEdits")) {
|
|
2888
3056
|
session.permissionMode = response.outcome.optionId;
|
|
2889
3057
|
await session.query.setPermissionMode(response.outcome.optionId);
|
|
2890
|
-
await context.
|
|
3058
|
+
await context.client.sessionUpdate({
|
|
3059
|
+
sessionId: context.sessionId,
|
|
3060
|
+
update: {
|
|
3061
|
+
sessionUpdate: "current_mode_update",
|
|
3062
|
+
currentModeId: response.outcome.optionId
|
|
3063
|
+
}
|
|
3064
|
+
});
|
|
3065
|
+
await context.updateConfigOption("mode", response.outcome.optionId);
|
|
2891
3066
|
return {
|
|
2892
3067
|
behavior: "allow",
|
|
2893
3068
|
updatedInput,
|
|
@@ -2908,7 +3083,7 @@ async function handleEnterPlanModeTool(context) {
|
|
|
2908
3083
|
const { session, toolInput } = context;
|
|
2909
3084
|
session.permissionMode = "plan";
|
|
2910
3085
|
await session.query.setPermissionMode("plan");
|
|
2911
|
-
await context.
|
|
3086
|
+
await context.updateConfigOption("mode", "plan");
|
|
2912
3087
|
return {
|
|
2913
3088
|
behavior: "allow",
|
|
2914
3089
|
updatedInput: toolInput
|
|
@@ -2926,6 +3101,9 @@ async function handleExitPlanModeTool(context) {
|
|
|
2926
3101
|
return validationResult.error;
|
|
2927
3102
|
}
|
|
2928
3103
|
const response = await requestPlanApproval(context, updatedInput);
|
|
3104
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
3105
|
+
throw new Error("Tool use aborted");
|
|
3106
|
+
}
|
|
2929
3107
|
return await applyPlanApproval(response, context, updatedInput);
|
|
2930
3108
|
}
|
|
2931
3109
|
function buildQuestionOptions(question) {
|
|
@@ -2949,14 +3127,13 @@ async function handleAskUserQuestionTool(context) {
|
|
|
2949
3127
|
interrupt: true
|
|
2950
3128
|
};
|
|
2951
3129
|
}
|
|
2952
|
-
const { client, sessionId, toolUseID, toolInput
|
|
3130
|
+
const { client, sessionId, toolUseID, toolInput } = context;
|
|
2953
3131
|
const firstQuestion = questions[0];
|
|
2954
3132
|
const options = buildQuestionOptions(firstQuestion);
|
|
2955
|
-
const toolInfo = toolInfoFromToolUse(
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
);
|
|
3133
|
+
const toolInfo = toolInfoFromToolUse({
|
|
3134
|
+
name: context.toolName,
|
|
3135
|
+
input: toolInput
|
|
3136
|
+
});
|
|
2960
3137
|
const response = await client.requestPermission({
|
|
2961
3138
|
options,
|
|
2962
3139
|
sessionId,
|
|
@@ -2971,6 +3148,9 @@ async function handleAskUserQuestionTool(context) {
|
|
|
2971
3148
|
}
|
|
2972
3149
|
}
|
|
2973
3150
|
});
|
|
3151
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
3152
|
+
throw new Error("Tool use aborted");
|
|
3153
|
+
}
|
|
2974
3154
|
if (response.outcome?.outcome !== "selected") {
|
|
2975
3155
|
const customMessage = response._meta?.message;
|
|
2976
3156
|
return {
|
|
@@ -3003,14 +3183,9 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
3003
3183
|
toolUseID,
|
|
3004
3184
|
client,
|
|
3005
3185
|
sessionId,
|
|
3006
|
-
fileContentCache,
|
|
3007
3186
|
suggestions
|
|
3008
3187
|
} = context;
|
|
3009
|
-
const toolInfo = toolInfoFromToolUse(
|
|
3010
|
-
{ name: toolName, input: toolInput },
|
|
3011
|
-
fileContentCache,
|
|
3012
|
-
context.logger
|
|
3013
|
-
);
|
|
3188
|
+
const toolInfo = toolInfoFromToolUse({ name: toolName, input: toolInput });
|
|
3014
3189
|
const options = buildPermissionOptions(
|
|
3015
3190
|
toolName,
|
|
3016
3191
|
toolInput,
|
|
@@ -3029,6 +3204,9 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
3029
3204
|
rawInput: toolInput
|
|
3030
3205
|
}
|
|
3031
3206
|
});
|
|
3207
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
3208
|
+
throw new Error("Tool use aborted");
|
|
3209
|
+
}
|
|
3032
3210
|
if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "allow" || response.outcome.optionId === "allow_always")) {
|
|
3033
3211
|
if (response.outcome.optionId === "allow_always") {
|
|
3034
3212
|
return {
|
|
@@ -3105,16 +3283,18 @@ async function canUseTool(context) {
|
|
|
3105
3283
|
var UNSUPPORTED_COMMANDS = [
|
|
3106
3284
|
"context",
|
|
3107
3285
|
"cost",
|
|
3286
|
+
"keybindings-help",
|
|
3108
3287
|
"login",
|
|
3109
3288
|
"logout",
|
|
3110
3289
|
"output-style:new",
|
|
3111
3290
|
"release-notes",
|
|
3112
3291
|
"todos"
|
|
3113
3292
|
];
|
|
3114
|
-
|
|
3115
|
-
const commands = await q.supportedCommands();
|
|
3293
|
+
function getAvailableSlashCommands(commands) {
|
|
3116
3294
|
return commands.map((command) => {
|
|
3117
|
-
const input = command.argumentHint ? {
|
|
3295
|
+
const input = command.argumentHint != null ? {
|
|
3296
|
+
hint: Array.isArray(command.argumentHint) ? command.argumentHint.join(" ") : command.argumentHint
|
|
3297
|
+
} : null;
|
|
3118
3298
|
let name = command.name;
|
|
3119
3299
|
if (command.name.endsWith(" (MCP)")) {
|
|
3120
3300
|
name = `mcp:${name.replace(" (MCP)", "")}`;
|
|
@@ -3212,13 +3392,19 @@ function buildEnvironment() {
|
|
|
3212
3392
|
ENABLE_TOOL_SEARCH: "auto:0"
|
|
3213
3393
|
};
|
|
3214
3394
|
}
|
|
3215
|
-
function buildHooks(userHooks, onModeChange) {
|
|
3395
|
+
function buildHooks(userHooks, onModeChange, settingsManager, logger) {
|
|
3216
3396
|
return {
|
|
3217
3397
|
...userHooks,
|
|
3218
3398
|
PostToolUse: [
|
|
3219
3399
|
...userHooks?.PostToolUse || [],
|
|
3220
3400
|
{
|
|
3221
|
-
hooks: [createPostToolUseHook({ onModeChange })]
|
|
3401
|
+
hooks: [createPostToolUseHook({ onModeChange, logger })]
|
|
3402
|
+
}
|
|
3403
|
+
],
|
|
3404
|
+
PreToolUse: [
|
|
3405
|
+
...userHooks?.PreToolUse || [],
|
|
3406
|
+
{
|
|
3407
|
+
hooks: [createPreToolUseHook(settingsManager, logger)]
|
|
3222
3408
|
}
|
|
3223
3409
|
]
|
|
3224
3410
|
};
|
|
@@ -3312,12 +3498,22 @@ function buildSessionOptions(params) {
|
|
|
3312
3498
|
permissionMode: params.permissionMode,
|
|
3313
3499
|
canUseTool: params.canUseTool,
|
|
3314
3500
|
executable: "node",
|
|
3501
|
+
tools: { type: "preset", preset: "claude_code" },
|
|
3502
|
+
extraArgs: {
|
|
3503
|
+
...params.userProvidedOptions?.extraArgs,
|
|
3504
|
+
"replay-user-messages": ""
|
|
3505
|
+
},
|
|
3315
3506
|
mcpServers: buildMcpServers(
|
|
3316
3507
|
params.userProvidedOptions?.mcpServers,
|
|
3317
3508
|
params.mcpServers
|
|
3318
3509
|
),
|
|
3319
3510
|
env: buildEnvironment(),
|
|
3320
|
-
hooks: buildHooks(
|
|
3511
|
+
hooks: buildHooks(
|
|
3512
|
+
params.userProvidedOptions?.hooks,
|
|
3513
|
+
params.onModeChange,
|
|
3514
|
+
params.settingsManager,
|
|
3515
|
+
params.logger
|
|
3516
|
+
),
|
|
3321
3517
|
abortController: getAbortController(
|
|
3322
3518
|
params.userProvidedOptions?.abortController
|
|
3323
3519
|
),
|
|
@@ -3334,13 +3530,36 @@ function buildSessionOptions(params) {
|
|
|
3334
3530
|
}
|
|
3335
3531
|
if (params.isResume) {
|
|
3336
3532
|
options.resume = params.sessionId;
|
|
3337
|
-
options.forkSession = false;
|
|
3533
|
+
options.forkSession = params.forkSession ?? false;
|
|
3338
3534
|
} else {
|
|
3339
3535
|
options.sessionId = params.sessionId;
|
|
3536
|
+
options.model = DEFAULT_MODEL;
|
|
3340
3537
|
}
|
|
3341
3538
|
if (params.additionalDirectories) {
|
|
3342
3539
|
options.additionalDirectories = params.additionalDirectories;
|
|
3343
3540
|
}
|
|
3541
|
+
if (params.disableBuiltInTools) {
|
|
3542
|
+
const builtInTools = [
|
|
3543
|
+
"Read",
|
|
3544
|
+
"Write",
|
|
3545
|
+
"Edit",
|
|
3546
|
+
"Bash",
|
|
3547
|
+
"Glob",
|
|
3548
|
+
"Grep",
|
|
3549
|
+
"Task",
|
|
3550
|
+
"TodoWrite",
|
|
3551
|
+
"ExitPlanMode",
|
|
3552
|
+
"WebSearch",
|
|
3553
|
+
"WebFetch",
|
|
3554
|
+
"SlashCommand",
|
|
3555
|
+
"Skill",
|
|
3556
|
+
"NotebookEdit"
|
|
3557
|
+
];
|
|
3558
|
+
options.disallowedTools = [
|
|
3559
|
+
...options.disallowedTools ?? [],
|
|
3560
|
+
...builtInTools
|
|
3561
|
+
];
|
|
3562
|
+
}
|
|
3344
3563
|
clearStatsigCache();
|
|
3345
3564
|
return options;
|
|
3346
3565
|
}
|
|
@@ -3353,15 +3572,268 @@ function clearStatsigCache() {
|
|
|
3353
3572
|
});
|
|
3354
3573
|
}
|
|
3355
3574
|
|
|
3575
|
+
// src/adapters/claude/session/settings.ts
|
|
3576
|
+
var fs2 = __toESM(require("fs"), 1);
|
|
3577
|
+
var os3 = __toESM(require("os"), 1);
|
|
3578
|
+
var path3 = __toESM(require("path"), 1);
|
|
3579
|
+
var import_minimatch = require("minimatch");
|
|
3580
|
+
var ACP_TOOL_NAME_PREFIX = "mcp__acp__";
|
|
3581
|
+
var acpToolNames = {
|
|
3582
|
+
read: `${ACP_TOOL_NAME_PREFIX}Read`,
|
|
3583
|
+
edit: `${ACP_TOOL_NAME_PREFIX}Edit`,
|
|
3584
|
+
write: `${ACP_TOOL_NAME_PREFIX}Write`,
|
|
3585
|
+
bash: `${ACP_TOOL_NAME_PREFIX}Bash`
|
|
3586
|
+
};
|
|
3587
|
+
var SHELL_OPERATORS = ["&&", "||", ";", "|", "$(", "`", "\n"];
|
|
3588
|
+
function containsShellOperator(str) {
|
|
3589
|
+
return SHELL_OPERATORS.some((op) => str.includes(op));
|
|
3590
|
+
}
|
|
3591
|
+
var FILE_EDITING_TOOLS = [acpToolNames.edit, acpToolNames.write];
|
|
3592
|
+
var FILE_READING_TOOLS = [acpToolNames.read];
|
|
3593
|
+
var TOOL_ARG_ACCESSORS = {
|
|
3594
|
+
[acpToolNames.read]: (input) => input?.file_path,
|
|
3595
|
+
[acpToolNames.edit]: (input) => input?.file_path,
|
|
3596
|
+
[acpToolNames.write]: (input) => input?.file_path,
|
|
3597
|
+
[acpToolNames.bash]: (input) => input?.command
|
|
3598
|
+
};
|
|
3599
|
+
function parseRule(rule) {
|
|
3600
|
+
const match = rule.match(/^(\w+)(?:\((.+)\))?$/);
|
|
3601
|
+
if (!match) {
|
|
3602
|
+
return { toolName: rule };
|
|
3603
|
+
}
|
|
3604
|
+
const toolName = match[1] ?? rule;
|
|
3605
|
+
const argument = match[2];
|
|
3606
|
+
if (argument?.endsWith(":*")) {
|
|
3607
|
+
return {
|
|
3608
|
+
toolName,
|
|
3609
|
+
argument: argument.slice(0, -2),
|
|
3610
|
+
isWildcard: true
|
|
3611
|
+
};
|
|
3612
|
+
}
|
|
3613
|
+
return { toolName, argument };
|
|
3614
|
+
}
|
|
3615
|
+
function normalizePath(filePath, cwd) {
|
|
3616
|
+
let resolved = filePath;
|
|
3617
|
+
if (resolved.startsWith("~/")) {
|
|
3618
|
+
resolved = path3.join(os3.homedir(), resolved.slice(2));
|
|
3619
|
+
} else if (resolved.startsWith("./")) {
|
|
3620
|
+
resolved = path3.join(cwd, resolved.slice(2));
|
|
3621
|
+
} else if (!path3.isAbsolute(resolved)) {
|
|
3622
|
+
resolved = path3.join(cwd, resolved);
|
|
3623
|
+
}
|
|
3624
|
+
return path3.normalize(resolved).replace(/\\/g, "/");
|
|
3625
|
+
}
|
|
3626
|
+
function matchesGlob(pattern, filePath, cwd) {
|
|
3627
|
+
const normalizedPattern = normalizePath(pattern, cwd);
|
|
3628
|
+
const normalizedPath = normalizePath(filePath, cwd);
|
|
3629
|
+
return (0, import_minimatch.minimatch)(normalizedPath, normalizedPattern, {
|
|
3630
|
+
dot: true,
|
|
3631
|
+
matchBase: false,
|
|
3632
|
+
nocase: process.platform === "win32"
|
|
3633
|
+
});
|
|
3634
|
+
}
|
|
3635
|
+
function matchesRule(rule, toolName, toolInput, cwd) {
|
|
3636
|
+
const ruleAppliesToTool = rule.toolName === "Bash" && toolName === acpToolNames.bash || rule.toolName === "Edit" && FILE_EDITING_TOOLS.includes(toolName) || rule.toolName === "Read" && FILE_READING_TOOLS.includes(toolName);
|
|
3637
|
+
if (!ruleAppliesToTool) {
|
|
3638
|
+
return false;
|
|
3639
|
+
}
|
|
3640
|
+
if (!rule.argument) {
|
|
3641
|
+
return true;
|
|
3642
|
+
}
|
|
3643
|
+
const argAccessor = TOOL_ARG_ACCESSORS[toolName];
|
|
3644
|
+
if (!argAccessor) {
|
|
3645
|
+
return true;
|
|
3646
|
+
}
|
|
3647
|
+
const actualArg = argAccessor(toolInput);
|
|
3648
|
+
if (!actualArg) {
|
|
3649
|
+
return false;
|
|
3650
|
+
}
|
|
3651
|
+
if (toolName === acpToolNames.bash) {
|
|
3652
|
+
if (rule.isWildcard) {
|
|
3653
|
+
if (!actualArg.startsWith(rule.argument)) {
|
|
3654
|
+
return false;
|
|
3655
|
+
}
|
|
3656
|
+
const remainder = actualArg.slice(rule.argument.length);
|
|
3657
|
+
if (containsShellOperator(remainder)) {
|
|
3658
|
+
return false;
|
|
3659
|
+
}
|
|
3660
|
+
return true;
|
|
3661
|
+
}
|
|
3662
|
+
return actualArg === rule.argument;
|
|
3663
|
+
}
|
|
3664
|
+
return matchesGlob(rule.argument, actualArg, cwd);
|
|
3665
|
+
}
|
|
3666
|
+
async function loadSettingsFile(filePath) {
|
|
3667
|
+
if (!filePath) {
|
|
3668
|
+
return {};
|
|
3669
|
+
}
|
|
3670
|
+
try {
|
|
3671
|
+
const content = await fs2.promises.readFile(filePath, "utf-8");
|
|
3672
|
+
return JSON.parse(content);
|
|
3673
|
+
} catch {
|
|
3674
|
+
return {};
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
function getManagedSettingsPath() {
|
|
3678
|
+
switch (process.platform) {
|
|
3679
|
+
case "darwin":
|
|
3680
|
+
return "/Library/Application Support/ClaudeCode/managed-settings.json";
|
|
3681
|
+
case "linux":
|
|
3682
|
+
return "/etc/claude-code/managed-settings.json";
|
|
3683
|
+
case "win32":
|
|
3684
|
+
return "C:\\Program Files\\ClaudeCode\\managed-settings.json";
|
|
3685
|
+
default:
|
|
3686
|
+
return "/etc/claude-code/managed-settings.json";
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
var SettingsManager = class {
|
|
3690
|
+
cwd;
|
|
3691
|
+
userSettings = {};
|
|
3692
|
+
projectSettings = {};
|
|
3693
|
+
localSettings = {};
|
|
3694
|
+
enterpriseSettings = {};
|
|
3695
|
+
mergedSettings = {};
|
|
3696
|
+
initialized = false;
|
|
3697
|
+
constructor(cwd) {
|
|
3698
|
+
this.cwd = cwd;
|
|
3699
|
+
}
|
|
3700
|
+
async initialize() {
|
|
3701
|
+
if (this.initialized) {
|
|
3702
|
+
return;
|
|
3703
|
+
}
|
|
3704
|
+
await this.loadAllSettings();
|
|
3705
|
+
this.initialized = true;
|
|
3706
|
+
}
|
|
3707
|
+
getUserSettingsPath() {
|
|
3708
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || path3.join(os3.homedir(), ".claude");
|
|
3709
|
+
return path3.join(configDir, "settings.json");
|
|
3710
|
+
}
|
|
3711
|
+
getProjectSettingsPath() {
|
|
3712
|
+
return path3.join(this.cwd, ".claude", "settings.json");
|
|
3713
|
+
}
|
|
3714
|
+
getLocalSettingsPath() {
|
|
3715
|
+
return path3.join(this.cwd, ".claude", "settings.local.json");
|
|
3716
|
+
}
|
|
3717
|
+
async loadAllSettings() {
|
|
3718
|
+
const [userSettings, projectSettings, localSettings, enterpriseSettings] = await Promise.all([
|
|
3719
|
+
loadSettingsFile(this.getUserSettingsPath()),
|
|
3720
|
+
loadSettingsFile(this.getProjectSettingsPath()),
|
|
3721
|
+
loadSettingsFile(this.getLocalSettingsPath()),
|
|
3722
|
+
loadSettingsFile(getManagedSettingsPath())
|
|
3723
|
+
]);
|
|
3724
|
+
this.userSettings = userSettings;
|
|
3725
|
+
this.projectSettings = projectSettings;
|
|
3726
|
+
this.localSettings = localSettings;
|
|
3727
|
+
this.enterpriseSettings = enterpriseSettings;
|
|
3728
|
+
this.mergeAllSettings();
|
|
3729
|
+
}
|
|
3730
|
+
mergeAllSettings() {
|
|
3731
|
+
const allSettings = [
|
|
3732
|
+
this.userSettings,
|
|
3733
|
+
this.projectSettings,
|
|
3734
|
+
this.localSettings,
|
|
3735
|
+
this.enterpriseSettings
|
|
3736
|
+
];
|
|
3737
|
+
const permissions = {
|
|
3738
|
+
allow: [],
|
|
3739
|
+
deny: [],
|
|
3740
|
+
ask: []
|
|
3741
|
+
};
|
|
3742
|
+
const merged = { permissions };
|
|
3743
|
+
for (const settings of allSettings) {
|
|
3744
|
+
if (settings.permissions) {
|
|
3745
|
+
if (settings.permissions.allow) {
|
|
3746
|
+
permissions.allow?.push(...settings.permissions.allow);
|
|
3747
|
+
}
|
|
3748
|
+
if (settings.permissions.deny) {
|
|
3749
|
+
permissions.deny?.push(...settings.permissions.deny);
|
|
3750
|
+
}
|
|
3751
|
+
if (settings.permissions.ask) {
|
|
3752
|
+
permissions.ask?.push(...settings.permissions.ask);
|
|
3753
|
+
}
|
|
3754
|
+
if (settings.permissions.additionalDirectories) {
|
|
3755
|
+
permissions.additionalDirectories = [
|
|
3756
|
+
...permissions.additionalDirectories || [],
|
|
3757
|
+
...settings.permissions.additionalDirectories
|
|
3758
|
+
];
|
|
3759
|
+
}
|
|
3760
|
+
if (settings.permissions.defaultMode) {
|
|
3761
|
+
permissions.defaultMode = settings.permissions.defaultMode;
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
if (settings.env) {
|
|
3765
|
+
merged.env = { ...merged.env, ...settings.env };
|
|
3766
|
+
}
|
|
3767
|
+
if (settings.model) {
|
|
3768
|
+
merged.model = settings.model;
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
this.mergedSettings = merged;
|
|
3772
|
+
}
|
|
3773
|
+
checkPermission(toolName, toolInput) {
|
|
3774
|
+
if (!toolName.startsWith(ACP_TOOL_NAME_PREFIX)) {
|
|
3775
|
+
return { decision: "ask" };
|
|
3776
|
+
}
|
|
3777
|
+
const permissions = this.mergedSettings.permissions;
|
|
3778
|
+
if (!permissions) {
|
|
3779
|
+
return { decision: "ask" };
|
|
3780
|
+
}
|
|
3781
|
+
for (const rule of permissions.deny || []) {
|
|
3782
|
+
const parsed = parseRule(rule);
|
|
3783
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
3784
|
+
return { decision: "deny", rule, source: "deny" };
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
for (const rule of permissions.allow || []) {
|
|
3788
|
+
const parsed = parseRule(rule);
|
|
3789
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
3790
|
+
return { decision: "allow", rule, source: "allow" };
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
for (const rule of permissions.ask || []) {
|
|
3794
|
+
const parsed = parseRule(rule);
|
|
3795
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
3796
|
+
return { decision: "ask", rule, source: "ask" };
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3799
|
+
return { decision: "ask" };
|
|
3800
|
+
}
|
|
3801
|
+
getSettings() {
|
|
3802
|
+
return this.mergedSettings;
|
|
3803
|
+
}
|
|
3804
|
+
getCwd() {
|
|
3805
|
+
return this.cwd;
|
|
3806
|
+
}
|
|
3807
|
+
async setCwd(cwd) {
|
|
3808
|
+
if (this.cwd === cwd) {
|
|
3809
|
+
return;
|
|
3810
|
+
}
|
|
3811
|
+
this.dispose();
|
|
3812
|
+
this.cwd = cwd;
|
|
3813
|
+
this.initialized = false;
|
|
3814
|
+
await this.initialize();
|
|
3815
|
+
}
|
|
3816
|
+
dispose() {
|
|
3817
|
+
this.initialized = false;
|
|
3818
|
+
}
|
|
3819
|
+
};
|
|
3820
|
+
|
|
3356
3821
|
// src/adapters/claude/claude-agent.ts
|
|
3357
3822
|
var SESSION_VALIDATION_TIMEOUT_MS = 1e4;
|
|
3823
|
+
var MAX_TITLE_LENGTH = 256;
|
|
3824
|
+
function sanitizeTitle(text2) {
|
|
3825
|
+
const sanitized = text2.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
3826
|
+
if (sanitized.length <= MAX_TITLE_LENGTH) {
|
|
3827
|
+
return sanitized;
|
|
3828
|
+
}
|
|
3829
|
+
return `${sanitized.slice(0, MAX_TITLE_LENGTH - 1)}\u2026`;
|
|
3830
|
+
}
|
|
3358
3831
|
var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
3359
3832
|
adapterName = "claude";
|
|
3360
3833
|
toolUseCache;
|
|
3361
3834
|
backgroundTerminals = {};
|
|
3362
3835
|
clientCapabilities;
|
|
3363
3836
|
options;
|
|
3364
|
-
lastSentConfigOptions;
|
|
3365
3837
|
constructor(client, options) {
|
|
3366
3838
|
super(client);
|
|
3367
3839
|
this.options = options;
|
|
@@ -3382,125 +3854,416 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3382
3854
|
sse: true
|
|
3383
3855
|
},
|
|
3384
3856
|
loadSession: true,
|
|
3857
|
+
sessionCapabilities: {
|
|
3858
|
+
list: {},
|
|
3859
|
+
fork: {},
|
|
3860
|
+
resume: {}
|
|
3861
|
+
},
|
|
3385
3862
|
_meta: {
|
|
3386
3863
|
posthog: {
|
|
3387
3864
|
resumeSession: true
|
|
3865
|
+
},
|
|
3866
|
+
claudeCode: {
|
|
3867
|
+
promptQueueing: true
|
|
3868
|
+
}
|
|
3869
|
+
}
|
|
3870
|
+
},
|
|
3871
|
+
agentInfo: {
|
|
3872
|
+
name: package_default.name,
|
|
3873
|
+
title: "Claude Agent",
|
|
3874
|
+
version: package_default.version
|
|
3875
|
+
},
|
|
3876
|
+
authMethods: []
|
|
3877
|
+
};
|
|
3878
|
+
}
|
|
3879
|
+
async newSession(params) {
|
|
3880
|
+
if (fs3.existsSync(path4.resolve(os4.homedir(), ".claude.json.backup")) && !fs3.existsSync(path4.resolve(os4.homedir(), ".claude.json"))) {
|
|
3881
|
+
throw import_sdk2.RequestError.authRequired();
|
|
3882
|
+
}
|
|
3883
|
+
const response = await this.createSession(params, {
|
|
3884
|
+
// Revisit these meta values once we support resume
|
|
3885
|
+
resume: params._meta?.claudeCode?.options?.resume
|
|
3886
|
+
});
|
|
3887
|
+
return response;
|
|
3888
|
+
}
|
|
3889
|
+
async unstable_forkSession(params) {
|
|
3890
|
+
return this.createSession(
|
|
3891
|
+
{
|
|
3892
|
+
cwd: params.cwd,
|
|
3893
|
+
mcpServers: params.mcpServers ?? [],
|
|
3894
|
+
_meta: params._meta
|
|
3895
|
+
},
|
|
3896
|
+
{ resume: params.sessionId, forkSession: true }
|
|
3897
|
+
);
|
|
3898
|
+
}
|
|
3899
|
+
async unstable_resumeSession(params) {
|
|
3900
|
+
const response = await this.createSession(
|
|
3901
|
+
{
|
|
3902
|
+
cwd: params.cwd,
|
|
3903
|
+
mcpServers: params.mcpServers ?? [],
|
|
3904
|
+
_meta: params._meta
|
|
3905
|
+
},
|
|
3906
|
+
{
|
|
3907
|
+
resume: params.sessionId
|
|
3908
|
+
}
|
|
3909
|
+
);
|
|
3910
|
+
return response;
|
|
3911
|
+
}
|
|
3912
|
+
async loadSession(params) {
|
|
3913
|
+
const response = await this.createSession(
|
|
3914
|
+
{
|
|
3915
|
+
cwd: params.cwd,
|
|
3916
|
+
mcpServers: params.mcpServers ?? [],
|
|
3917
|
+
_meta: params._meta
|
|
3918
|
+
},
|
|
3919
|
+
{ resume: params.sessionId, skipBackgroundFetches: true }
|
|
3920
|
+
);
|
|
3921
|
+
await this.replaySessionHistory(params.sessionId);
|
|
3922
|
+
this.deferBackgroundFetches(this.session.query);
|
|
3923
|
+
return {
|
|
3924
|
+
modes: response.modes,
|
|
3925
|
+
models: response.models,
|
|
3926
|
+
configOptions: response.configOptions
|
|
3927
|
+
};
|
|
3928
|
+
}
|
|
3929
|
+
async unstable_listSessions(params) {
|
|
3930
|
+
const sdkSessions = await (0, import_claude_agent_sdk.listSessions)({ dir: params.cwd ?? void 0 });
|
|
3931
|
+
const sessions = [];
|
|
3932
|
+
for (const session of sdkSessions) {
|
|
3933
|
+
if (!session.cwd) continue;
|
|
3934
|
+
sessions.push({
|
|
3935
|
+
sessionId: session.sessionId,
|
|
3936
|
+
cwd: session.cwd,
|
|
3937
|
+
title: sanitizeTitle(session.customTitle || session.summary || ""),
|
|
3938
|
+
updatedAt: new Date(session.lastModified).toISOString()
|
|
3939
|
+
});
|
|
3940
|
+
}
|
|
3941
|
+
return {
|
|
3942
|
+
sessions
|
|
3943
|
+
};
|
|
3944
|
+
}
|
|
3945
|
+
async prompt(params) {
|
|
3946
|
+
this.session.cancelled = false;
|
|
3947
|
+
this.session.interruptReason = void 0;
|
|
3948
|
+
this.session.accumulatedUsage = {
|
|
3949
|
+
inputTokens: 0,
|
|
3950
|
+
outputTokens: 0,
|
|
3951
|
+
cachedReadTokens: 0,
|
|
3952
|
+
cachedWriteTokens: 0
|
|
3953
|
+
};
|
|
3954
|
+
const userMessage = promptToClaude(params);
|
|
3955
|
+
if (this.session.promptRunning) {
|
|
3956
|
+
const uuid = (0, import_node_crypto.randomUUID)();
|
|
3957
|
+
userMessage.uuid = uuid;
|
|
3958
|
+
this.session.input.push(userMessage);
|
|
3959
|
+
const order = this.session.nextPendingOrder++;
|
|
3960
|
+
const cancelled = await new Promise((resolve4) => {
|
|
3961
|
+
this.session.pendingMessages.set(uuid, { resolve: resolve4, order });
|
|
3962
|
+
});
|
|
3963
|
+
if (cancelled) {
|
|
3964
|
+
return { stopReason: "cancelled" };
|
|
3965
|
+
}
|
|
3966
|
+
} else {
|
|
3967
|
+
this.session.input.push(userMessage);
|
|
3968
|
+
}
|
|
3969
|
+
await this.broadcastUserMessage(params);
|
|
3970
|
+
this.session.promptRunning = true;
|
|
3971
|
+
let handedOff = false;
|
|
3972
|
+
let lastAssistantTotalUsage = null;
|
|
3973
|
+
const supportsTerminalOutput = this.clientCapabilities?._meta?.terminal_output === true;
|
|
3974
|
+
const context = {
|
|
3975
|
+
session: this.session,
|
|
3976
|
+
sessionId: params.sessionId,
|
|
3977
|
+
client: this.client,
|
|
3978
|
+
toolUseCache: this.toolUseCache,
|
|
3979
|
+
fileContentCache: this.fileContentCache,
|
|
3980
|
+
logger: this.logger,
|
|
3981
|
+
supportsTerminalOutput
|
|
3982
|
+
};
|
|
3983
|
+
try {
|
|
3984
|
+
while (true) {
|
|
3985
|
+
const { value: message, done } = await this.session.query.next();
|
|
3986
|
+
if (done || !message) {
|
|
3987
|
+
if (this.session.cancelled) {
|
|
3988
|
+
return {
|
|
3989
|
+
stopReason: "cancelled",
|
|
3990
|
+
_meta: this.session.interruptReason ? { interruptReason: this.session.interruptReason } : void 0
|
|
3991
|
+
};
|
|
3992
|
+
}
|
|
3993
|
+
break;
|
|
3994
|
+
}
|
|
3995
|
+
switch (message.type) {
|
|
3996
|
+
case "system":
|
|
3997
|
+
if (message.subtype === "compact_boundary") {
|
|
3998
|
+
lastAssistantTotalUsage = 0;
|
|
3999
|
+
}
|
|
4000
|
+
await handleSystemMessage(message, context);
|
|
4001
|
+
break;
|
|
4002
|
+
case "result": {
|
|
4003
|
+
if (this.session.cancelled) {
|
|
4004
|
+
return { stopReason: "cancelled" };
|
|
4005
|
+
}
|
|
4006
|
+
this.session.accumulatedUsage.inputTokens += message.usage.input_tokens;
|
|
4007
|
+
this.session.accumulatedUsage.outputTokens += message.usage.output_tokens;
|
|
4008
|
+
this.session.accumulatedUsage.cachedReadTokens += message.usage.cache_read_input_tokens;
|
|
4009
|
+
this.session.accumulatedUsage.cachedWriteTokens += message.usage.cache_creation_input_tokens;
|
|
4010
|
+
const contextWindows = Object.values(message.modelUsage).map(
|
|
4011
|
+
(m) => m.contextWindow
|
|
4012
|
+
);
|
|
4013
|
+
const contextWindowSize = contextWindows.length > 0 ? Math.min(...contextWindows) : 2e5;
|
|
4014
|
+
if (lastAssistantTotalUsage !== null) {
|
|
4015
|
+
await this.client.sessionUpdate({
|
|
4016
|
+
sessionId: params.sessionId,
|
|
4017
|
+
update: {
|
|
4018
|
+
sessionUpdate: "usage_update",
|
|
4019
|
+
used: lastAssistantTotalUsage,
|
|
4020
|
+
size: contextWindowSize,
|
|
4021
|
+
cost: {
|
|
4022
|
+
amount: message.total_cost_usd,
|
|
4023
|
+
currency: "USD"
|
|
4024
|
+
}
|
|
4025
|
+
}
|
|
4026
|
+
});
|
|
4027
|
+
}
|
|
4028
|
+
await this.client.extNotification("_posthog/usage_update", {
|
|
4029
|
+
sessionId: params.sessionId,
|
|
4030
|
+
used: {
|
|
4031
|
+
inputTokens: message.usage.input_tokens,
|
|
4032
|
+
outputTokens: message.usage.output_tokens,
|
|
4033
|
+
cachedReadTokens: message.usage.cache_read_input_tokens,
|
|
4034
|
+
cachedWriteTokens: message.usage.cache_creation_input_tokens
|
|
4035
|
+
},
|
|
4036
|
+
cost: message.total_cost_usd
|
|
4037
|
+
});
|
|
4038
|
+
const usage = {
|
|
4039
|
+
inputTokens: this.session.accumulatedUsage.inputTokens,
|
|
4040
|
+
outputTokens: this.session.accumulatedUsage.outputTokens,
|
|
4041
|
+
cachedReadTokens: this.session.accumulatedUsage.cachedReadTokens,
|
|
4042
|
+
cachedWriteTokens: this.session.accumulatedUsage.cachedWriteTokens,
|
|
4043
|
+
totalTokens: this.session.accumulatedUsage.inputTokens + this.session.accumulatedUsage.outputTokens + this.session.accumulatedUsage.cachedReadTokens + this.session.accumulatedUsage.cachedWriteTokens
|
|
4044
|
+
};
|
|
4045
|
+
const result = handleResultMessage(message);
|
|
4046
|
+
if (result.error) throw result.error;
|
|
4047
|
+
switch (message.subtype) {
|
|
4048
|
+
case "error_max_budget_usd":
|
|
4049
|
+
case "error_max_turns":
|
|
4050
|
+
case "error_max_structured_output_retries":
|
|
4051
|
+
return { stopReason: "max_turn_requests", usage };
|
|
4052
|
+
default:
|
|
4053
|
+
return { stopReason: "end_turn", usage };
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
case "stream_event":
|
|
4057
|
+
await handleStreamEvent(message, context);
|
|
4058
|
+
break;
|
|
4059
|
+
case "user":
|
|
4060
|
+
case "assistant": {
|
|
4061
|
+
if (this.session.cancelled) {
|
|
4062
|
+
break;
|
|
4063
|
+
}
|
|
4064
|
+
if (message.type === "user" && "uuid" in message && message.uuid) {
|
|
4065
|
+
const pending = this.session.pendingMessages.get(
|
|
4066
|
+
message.uuid
|
|
4067
|
+
);
|
|
4068
|
+
if (pending) {
|
|
4069
|
+
pending.resolve(false);
|
|
4070
|
+
this.session.pendingMessages.delete(message.uuid);
|
|
4071
|
+
handedOff = true;
|
|
4072
|
+
return { stopReason: "end_turn" };
|
|
4073
|
+
}
|
|
4074
|
+
}
|
|
4075
|
+
if ("usage" in message.message && message.parent_tool_use_id === null) {
|
|
4076
|
+
const usage = message.message.usage;
|
|
4077
|
+
lastAssistantTotalUsage = usage.input_tokens + usage.output_tokens + usage.cache_read_input_tokens + usage.cache_creation_input_tokens;
|
|
4078
|
+
}
|
|
4079
|
+
const result = await handleUserAssistantMessage(message, context);
|
|
4080
|
+
if (result.error) throw result.error;
|
|
4081
|
+
if (result.shouldStop) {
|
|
4082
|
+
return { stopReason: "end_turn" };
|
|
4083
|
+
}
|
|
4084
|
+
break;
|
|
3388
4085
|
}
|
|
4086
|
+
case "tool_progress":
|
|
4087
|
+
case "auth_status":
|
|
4088
|
+
case "tool_use_summary":
|
|
4089
|
+
break;
|
|
4090
|
+
default:
|
|
4091
|
+
unreachable(message, this.logger);
|
|
4092
|
+
break;
|
|
3389
4093
|
}
|
|
3390
|
-
}
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
id: "claude-login",
|
|
3399
|
-
name: "Log in with Claude Code",
|
|
3400
|
-
description: "Run `claude /login` in the terminal"
|
|
4094
|
+
}
|
|
4095
|
+
throw new Error("Session did not end in result");
|
|
4096
|
+
} finally {
|
|
4097
|
+
if (!handedOff) {
|
|
4098
|
+
this.session.promptRunning = false;
|
|
4099
|
+
for (const [key, pending] of this.session.pendingMessages) {
|
|
4100
|
+
pending.resolve(true);
|
|
4101
|
+
this.session.pendingMessages.delete(key);
|
|
3401
4102
|
}
|
|
3402
|
-
|
|
3403
|
-
}
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
3404
4105
|
}
|
|
3405
|
-
|
|
3406
|
-
|
|
4106
|
+
// Called by BaseAcpAgent#cancel() to interrupt the session
|
|
4107
|
+
async interrupt() {
|
|
4108
|
+
this.session.cancelled = true;
|
|
4109
|
+
for (const [, pending] of this.session.pendingMessages) {
|
|
4110
|
+
pending.resolve(true);
|
|
4111
|
+
}
|
|
4112
|
+
this.session.pendingMessages.clear();
|
|
4113
|
+
await this.session.query.interrupt();
|
|
3407
4114
|
}
|
|
3408
|
-
async
|
|
3409
|
-
|
|
4115
|
+
async unstable_setSessionModel(params) {
|
|
4116
|
+
const sdkModelId = toSdkModelId(params.modelId);
|
|
4117
|
+
await this.session.query.setModel(sdkModelId);
|
|
4118
|
+
this.session.modelId = params.modelId;
|
|
4119
|
+
await this.updateConfigOption("model", params.modelId);
|
|
4120
|
+
return {};
|
|
4121
|
+
}
|
|
4122
|
+
async setSessionMode(params) {
|
|
4123
|
+
await this.applySessionMode(params.modeId);
|
|
4124
|
+
await this.updateConfigOption("mode", params.modeId);
|
|
4125
|
+
return {};
|
|
4126
|
+
}
|
|
4127
|
+
async setSessionConfigOption(params) {
|
|
4128
|
+
const option = this.session.configOptions.find(
|
|
4129
|
+
(o) => o.id === params.configId
|
|
4130
|
+
);
|
|
4131
|
+
if (!option) {
|
|
4132
|
+
throw new Error(`Unknown config option: ${params.configId}`);
|
|
4133
|
+
}
|
|
4134
|
+
const allValues = "options" in option && Array.isArray(option.options) ? option.options.flatMap(
|
|
4135
|
+
(o) => "options" in o && Array.isArray(o.options) ? o.options : [o]
|
|
4136
|
+
) : [];
|
|
4137
|
+
const validValue = allValues.find((o) => o.value === params.value);
|
|
4138
|
+
if (!validValue) {
|
|
4139
|
+
throw new Error(
|
|
4140
|
+
`Invalid value for config option ${params.configId}: ${params.value}`
|
|
4141
|
+
);
|
|
4142
|
+
}
|
|
4143
|
+
if (params.configId === "mode") {
|
|
4144
|
+
await this.applySessionMode(params.value);
|
|
4145
|
+
await this.client.sessionUpdate({
|
|
4146
|
+
sessionId: this.sessionId,
|
|
4147
|
+
update: {
|
|
4148
|
+
sessionUpdate: "current_mode_update",
|
|
4149
|
+
currentModeId: params.value
|
|
4150
|
+
}
|
|
4151
|
+
});
|
|
4152
|
+
} else if (params.configId === "model") {
|
|
4153
|
+
const sdkModelId = toSdkModelId(params.value);
|
|
4154
|
+
await this.session.query.setModel(sdkModelId);
|
|
4155
|
+
this.session.modelId = params.value;
|
|
4156
|
+
}
|
|
4157
|
+
this.session.configOptions = this.session.configOptions.map(
|
|
4158
|
+
(o) => o.id === params.configId ? { ...o, currentValue: params.value } : o
|
|
4159
|
+
);
|
|
4160
|
+
return { configOptions: this.session.configOptions };
|
|
4161
|
+
}
|
|
4162
|
+
async updateConfigOption(configId, value) {
|
|
4163
|
+
this.session.configOptions = this.session.configOptions.map(
|
|
4164
|
+
(o) => o.id === configId ? { ...o, currentValue: value } : o
|
|
4165
|
+
);
|
|
4166
|
+
await this.client.sessionUpdate({
|
|
4167
|
+
sessionId: this.sessionId,
|
|
4168
|
+
update: {
|
|
4169
|
+
sessionUpdate: "config_option_update",
|
|
4170
|
+
configOptions: this.session.configOptions
|
|
4171
|
+
}
|
|
4172
|
+
});
|
|
4173
|
+
}
|
|
4174
|
+
async applySessionMode(modeId) {
|
|
4175
|
+
if (!TWIG_EXECUTION_MODES.includes(modeId)) {
|
|
4176
|
+
throw new Error("Invalid Mode");
|
|
4177
|
+
}
|
|
4178
|
+
const previousMode = this.session.permissionMode;
|
|
4179
|
+
this.session.permissionMode = modeId;
|
|
4180
|
+
try {
|
|
4181
|
+
await this.session.query.setPermissionMode(modeId);
|
|
4182
|
+
} catch (error) {
|
|
4183
|
+
this.session.permissionMode = previousMode;
|
|
4184
|
+
if (error instanceof Error) {
|
|
4185
|
+
if (!error.message) {
|
|
4186
|
+
error.message = "Invalid Mode";
|
|
4187
|
+
}
|
|
4188
|
+
throw error;
|
|
4189
|
+
}
|
|
4190
|
+
throw new Error("Invalid Mode");
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
async createSession(params, creationOpts = {}) {
|
|
4194
|
+
const { cwd } = params;
|
|
4195
|
+
const { resume, forkSession } = creationOpts;
|
|
4196
|
+
const isResume = !!resume;
|
|
3410
4197
|
const meta = params._meta;
|
|
3411
4198
|
const taskId = meta?.persistence?.taskId;
|
|
3412
|
-
|
|
3413
|
-
|
|
4199
|
+
let sessionId;
|
|
4200
|
+
if (forkSession) {
|
|
4201
|
+
sessionId = (0, import_uuid.v7)();
|
|
4202
|
+
} else if (isResume) {
|
|
4203
|
+
sessionId = resume;
|
|
4204
|
+
} else {
|
|
4205
|
+
sessionId = (0, import_uuid.v7)();
|
|
4206
|
+
}
|
|
4207
|
+
const input = new Pushable();
|
|
4208
|
+
const settingsManager = new SettingsManager(cwd);
|
|
4209
|
+
await settingsManager.initialize();
|
|
4210
|
+
const mcpServers = parseMcpServers(params);
|
|
4211
|
+
const systemPrompt = buildSystemPrompt(meta?.systemPrompt);
|
|
4212
|
+
this.logger.info(isResume ? "Resuming session" : "Creating new session", {
|
|
3414
4213
|
sessionId,
|
|
3415
4214
|
taskId,
|
|
3416
4215
|
taskRunId: meta?.taskRunId,
|
|
3417
|
-
cwd
|
|
4216
|
+
cwd
|
|
3418
4217
|
});
|
|
3419
4218
|
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
3420
|
-
const mcpServers = parseMcpServers(params);
|
|
3421
4219
|
const options = buildSessionOptions({
|
|
3422
|
-
cwd
|
|
4220
|
+
cwd,
|
|
3423
4221
|
mcpServers,
|
|
3424
4222
|
permissionMode,
|
|
3425
4223
|
canUseTool: this.createCanUseTool(sessionId),
|
|
3426
4224
|
logger: this.logger,
|
|
3427
|
-
systemPrompt
|
|
4225
|
+
systemPrompt,
|
|
3428
4226
|
userProvidedOptions: meta?.claudeCode?.options,
|
|
3429
4227
|
sessionId,
|
|
3430
|
-
isResume
|
|
3431
|
-
|
|
4228
|
+
isResume,
|
|
4229
|
+
forkSession,
|
|
4230
|
+
additionalDirectories: meta?.claudeCode?.options?.additionalDirectories,
|
|
4231
|
+
disableBuiltInTools: meta?.disableBuiltInTools,
|
|
4232
|
+
settingsManager,
|
|
4233
|
+
onModeChange: this.createOnModeChange(),
|
|
3432
4234
|
onProcessSpawned: this.options?.onProcessSpawned,
|
|
3433
4235
|
onProcessExited: this.options?.onProcessExited
|
|
3434
4236
|
});
|
|
3435
|
-
const
|
|
3436
|
-
options.model = DEFAULT_MODEL;
|
|
4237
|
+
const abortController = options.abortController;
|
|
3437
4238
|
const q = (0, import_claude_agent_sdk.query)({ prompt: input, options });
|
|
3438
|
-
const session =
|
|
3439
|
-
|
|
3440
|
-
q,
|
|
4239
|
+
const session = {
|
|
4240
|
+
query: q,
|
|
3441
4241
|
input,
|
|
4242
|
+
cancelled: false,
|
|
4243
|
+
settingsManager,
|
|
3442
4244
|
permissionMode,
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
const resolvedSdkModel = toSdkModelId(modelOptions.currentModelId);
|
|
3458
|
-
if (resolvedSdkModel !== DEFAULT_MODEL) {
|
|
3459
|
-
await this.trySetModel(q, modelOptions.currentModelId);
|
|
3460
|
-
}
|
|
3461
|
-
const configOptions = await this.buildConfigOptions(modelOptions);
|
|
3462
|
-
return {
|
|
3463
|
-
sessionId,
|
|
3464
|
-
configOptions
|
|
3465
|
-
};
|
|
3466
|
-
}
|
|
3467
|
-
async loadSession(params) {
|
|
3468
|
-
return this.resumeSession(params);
|
|
3469
|
-
}
|
|
3470
|
-
async resumeSession(params) {
|
|
3471
|
-
const meta = params._meta;
|
|
3472
|
-
const taskId = meta?.persistence?.taskId;
|
|
3473
|
-
const sessionId = meta?.sessionId;
|
|
3474
|
-
if (!sessionId) {
|
|
3475
|
-
throw new Error("Cannot resume session without sessionId");
|
|
3476
|
-
}
|
|
3477
|
-
if (this.sessionId === sessionId) {
|
|
3478
|
-
return {};
|
|
3479
|
-
}
|
|
3480
|
-
this.logger.info("Resuming session", {
|
|
3481
|
-
sessionId,
|
|
3482
|
-
taskId,
|
|
3483
|
-
taskRunId: meta?.taskRunId,
|
|
3484
|
-
cwd: params.cwd
|
|
3485
|
-
});
|
|
3486
|
-
const mcpServers = parseMcpServers(params);
|
|
3487
|
-
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
3488
|
-
const { query: q, session } = await this.initializeQuery({
|
|
3489
|
-
cwd: params.cwd,
|
|
3490
|
-
permissionMode,
|
|
3491
|
-
mcpServers,
|
|
3492
|
-
systemPrompt: buildSystemPrompt(meta?.systemPrompt),
|
|
3493
|
-
userProvidedOptions: meta?.claudeCode?.options,
|
|
3494
|
-
sessionId,
|
|
3495
|
-
isResume: true,
|
|
3496
|
-
additionalDirectories: meta?.claudeCode?.options?.additionalDirectories
|
|
3497
|
-
});
|
|
3498
|
-
this.logger.info("Session query initialized, awaiting resumption", {
|
|
3499
|
-
sessionId,
|
|
3500
|
-
taskId,
|
|
4245
|
+
abortController,
|
|
4246
|
+
accumulatedUsage: {
|
|
4247
|
+
inputTokens: 0,
|
|
4248
|
+
outputTokens: 0,
|
|
4249
|
+
cachedReadTokens: 0,
|
|
4250
|
+
cachedWriteTokens: 0
|
|
4251
|
+
},
|
|
4252
|
+
configOptions: [],
|
|
4253
|
+
promptRunning: false,
|
|
4254
|
+
pendingMessages: /* @__PURE__ */ new Map(),
|
|
4255
|
+
nextPendingOrder: 0,
|
|
4256
|
+
// Custom properties
|
|
4257
|
+
cwd,
|
|
4258
|
+
notificationHistory: [],
|
|
3501
4259
|
taskRunId: meta?.taskRunId
|
|
3502
|
-
}
|
|
3503
|
-
session
|
|
4260
|
+
};
|
|
4261
|
+
this.session = session;
|
|
4262
|
+
this.sessionId = sessionId;
|
|
4263
|
+
this.logger.info(
|
|
4264
|
+
isResume ? "Session query initialized, awaiting resumption" : "Session query initialized, awaiting initialization",
|
|
4265
|
+
{ sessionId, taskId, taskRunId: meta?.taskRunId }
|
|
4266
|
+
);
|
|
3504
4267
|
try {
|
|
3505
4268
|
const result = await withTimeout(
|
|
3506
4269
|
q.initializationResult(),
|
|
@@ -3508,231 +4271,181 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3508
4271
|
);
|
|
3509
4272
|
if (result.result === "timeout") {
|
|
3510
4273
|
throw new Error(
|
|
3511
|
-
`Session resumption timed out for sessionId=${sessionId}`
|
|
4274
|
+
`Session ${isResume ? forkSession ? "fork" : "resumption" : "initialization"} timed out for sessionId=${sessionId}`
|
|
3512
4275
|
);
|
|
3513
4276
|
}
|
|
3514
4277
|
} catch (err) {
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
4278
|
+
settingsManager.dispose();
|
|
4279
|
+
this.logger.error(
|
|
4280
|
+
isResume ? forkSession ? "Session fork failed" : "Session resumption failed" : "Session initialization failed",
|
|
4281
|
+
{
|
|
4282
|
+
sessionId,
|
|
4283
|
+
taskId,
|
|
4284
|
+
taskRunId: meta?.taskRunId,
|
|
4285
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4286
|
+
}
|
|
4287
|
+
);
|
|
3521
4288
|
throw err;
|
|
3522
4289
|
}
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
const configOptions = await this.buildConfigOptions();
|
|
3530
|
-
return { configOptions };
|
|
3531
|
-
}
|
|
3532
|
-
async prompt(params) {
|
|
3533
|
-
this.session.cancelled = false;
|
|
3534
|
-
this.session.interruptReason = void 0;
|
|
3535
|
-
await this.broadcastUserMessage(params);
|
|
3536
|
-
this.session.input.push(promptToClaude(params));
|
|
3537
|
-
return this.processMessages(params.sessionId);
|
|
3538
|
-
}
|
|
3539
|
-
async setSessionConfigOption(params) {
|
|
3540
|
-
const configId = params.configId;
|
|
3541
|
-
const value = params.value;
|
|
3542
|
-
if (configId === "mode") {
|
|
3543
|
-
const modeId = value;
|
|
3544
|
-
if (!TWIG_EXECUTION_MODES.includes(modeId)) {
|
|
3545
|
-
throw new Error("Invalid Mode");
|
|
3546
|
-
}
|
|
3547
|
-
this.session.permissionMode = modeId;
|
|
3548
|
-
await this.session.query.setPermissionMode(modeId);
|
|
3549
|
-
} else if (configId === "model") {
|
|
3550
|
-
await this.setModelWithFallback(this.session.query, value);
|
|
3551
|
-
this.session.modelId = value;
|
|
3552
|
-
} else {
|
|
3553
|
-
throw new Error("Unsupported config option");
|
|
3554
|
-
}
|
|
3555
|
-
await this.emitConfigOptionsUpdate();
|
|
3556
|
-
return { configOptions: await this.buildConfigOptions() };
|
|
3557
|
-
}
|
|
3558
|
-
async interruptSession() {
|
|
3559
|
-
await this.session.query.interrupt();
|
|
3560
|
-
}
|
|
3561
|
-
async extMethod(method, params) {
|
|
3562
|
-
if (method === "_posthog/session/resume") {
|
|
3563
|
-
const result = await this.resumeSession(
|
|
3564
|
-
params
|
|
3565
|
-
);
|
|
3566
|
-
return {
|
|
3567
|
-
_meta: {
|
|
3568
|
-
configOptions: result.configOptions
|
|
3569
|
-
}
|
|
3570
|
-
};
|
|
4290
|
+
if (meta?.taskRunId) {
|
|
4291
|
+
await this.client.extNotification("_posthog/sdk_session", {
|
|
4292
|
+
taskRunId: meta.taskRunId,
|
|
4293
|
+
sessionId,
|
|
4294
|
+
adapter: "claude"
|
|
4295
|
+
});
|
|
3571
4296
|
}
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
4297
|
+
const settingsModel = settingsManager.getSettings().model;
|
|
4298
|
+
const modelOptions = await this.getModelConfigOptions();
|
|
4299
|
+
const resolvedModelId = settingsModel || modelOptions.currentModelId;
|
|
4300
|
+
session.modelId = resolvedModelId;
|
|
4301
|
+
if (!isResume) {
|
|
4302
|
+
const resolvedSdkModel = toSdkModelId(resolvedModelId);
|
|
4303
|
+
if (resolvedSdkModel !== DEFAULT_MODEL) {
|
|
4304
|
+
await this.session.query.setModel(resolvedSdkModel);
|
|
4305
|
+
}
|
|
4306
|
+
}
|
|
4307
|
+
const availableModes2 = getAvailableModes();
|
|
4308
|
+
const modes = {
|
|
4309
|
+
currentModeId: permissionMode,
|
|
4310
|
+
availableModes: availableModes2.map((mode) => ({
|
|
4311
|
+
id: mode.id,
|
|
4312
|
+
name: mode.name,
|
|
4313
|
+
description: mode.description ?? void 0
|
|
4314
|
+
}))
|
|
4315
|
+
};
|
|
4316
|
+
const models = {
|
|
4317
|
+
currentModelId: resolvedModelId,
|
|
4318
|
+
availableModels: modelOptions.options.map(
|
|
4319
|
+
(opt) => ({
|
|
4320
|
+
modelId: opt.value,
|
|
4321
|
+
name: opt.name,
|
|
4322
|
+
description: opt.description
|
|
4323
|
+
})
|
|
4324
|
+
)
|
|
3583
4325
|
};
|
|
3584
|
-
this.
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
systemPrompt: config.systemPrompt,
|
|
3597
|
-
userProvidedOptions: config.userProvidedOptions,
|
|
3598
|
-
sessionId: config.sessionId,
|
|
3599
|
-
isResume: config.isResume,
|
|
3600
|
-
additionalDirectories: config.additionalDirectories,
|
|
3601
|
-
onModeChange: this.createOnModeChange(config.sessionId),
|
|
3602
|
-
onProcessSpawned: this.options?.onProcessSpawned,
|
|
3603
|
-
onProcessExited: this.options?.onProcessExited
|
|
3604
|
-
});
|
|
3605
|
-
const q = (0, import_claude_agent_sdk.query)({ prompt: input, options });
|
|
3606
|
-
const abortController = options.abortController;
|
|
3607
|
-
const session = this.createSession(
|
|
3608
|
-
config.sessionId,
|
|
3609
|
-
q,
|
|
3610
|
-
input,
|
|
3611
|
-
config.permissionMode,
|
|
3612
|
-
config.cwd,
|
|
3613
|
-
abortController
|
|
4326
|
+
const configOptions = this.buildConfigOptions(permissionMode, modelOptions);
|
|
4327
|
+
session.configOptions = configOptions;
|
|
4328
|
+
if (!creationOpts.skipBackgroundFetches) {
|
|
4329
|
+
this.deferBackgroundFetches(q);
|
|
4330
|
+
}
|
|
4331
|
+
this.logger.info(
|
|
4332
|
+
isResume ? "Session resumed successfully" : "Session created successfully",
|
|
4333
|
+
{
|
|
4334
|
+
sessionId,
|
|
4335
|
+
taskId,
|
|
4336
|
+
taskRunId: meta?.taskRunId
|
|
4337
|
+
}
|
|
3614
4338
|
);
|
|
3615
|
-
return {
|
|
4339
|
+
return { sessionId, modes, models, configOptions };
|
|
3616
4340
|
}
|
|
3617
4341
|
createCanUseTool(sessionId) {
|
|
3618
|
-
return async (toolName, toolInput, { suggestions, toolUseID }) => canUseTool({
|
|
4342
|
+
return async (toolName, toolInput, { suggestions, toolUseID, signal }) => canUseTool({
|
|
3619
4343
|
session: this.session,
|
|
3620
4344
|
toolName,
|
|
3621
4345
|
toolInput,
|
|
3622
4346
|
toolUseID,
|
|
3623
4347
|
suggestions,
|
|
4348
|
+
signal,
|
|
3624
4349
|
client: this.client,
|
|
3625
4350
|
sessionId,
|
|
3626
4351
|
fileContentCache: this.fileContentCache,
|
|
3627
4352
|
logger: this.logger,
|
|
3628
|
-
|
|
4353
|
+
updateConfigOption: (configId, value) => this.updateConfigOption(configId, value)
|
|
3629
4354
|
});
|
|
3630
4355
|
}
|
|
3631
|
-
createOnModeChange(
|
|
4356
|
+
createOnModeChange() {
|
|
3632
4357
|
return async (newMode) => {
|
|
3633
4358
|
if (this.session) {
|
|
3634
4359
|
this.session.permissionMode = newMode;
|
|
3635
4360
|
}
|
|
3636
|
-
await this.
|
|
4361
|
+
await this.updateConfigOption("mode", newMode);
|
|
3637
4362
|
};
|
|
3638
4363
|
}
|
|
3639
|
-
|
|
3640
|
-
const options = [];
|
|
4364
|
+
buildConfigOptions(currentModeId, modelOptions) {
|
|
3641
4365
|
const modeOptions = getAvailableModes().map((mode) => ({
|
|
3642
4366
|
value: mode.id,
|
|
3643
4367
|
name: mode.name,
|
|
3644
4368
|
description: mode.description ?? void 0
|
|
3645
4369
|
}));
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
return options;
|
|
4370
|
+
return [
|
|
4371
|
+
{
|
|
4372
|
+
id: "mode",
|
|
4373
|
+
name: "Approval Preset",
|
|
4374
|
+
type: "select",
|
|
4375
|
+
currentValue: currentModeId,
|
|
4376
|
+
options: modeOptions,
|
|
4377
|
+
category: "mode",
|
|
4378
|
+
description: "Choose an approval and sandboxing preset for your session"
|
|
4379
|
+
},
|
|
4380
|
+
{
|
|
4381
|
+
id: "model",
|
|
4382
|
+
name: "Model",
|
|
4383
|
+
type: "select",
|
|
4384
|
+
currentValue: modelOptions.currentModelId,
|
|
4385
|
+
options: modelOptions.options,
|
|
4386
|
+
category: "model",
|
|
4387
|
+
description: "Choose which model Claude should use"
|
|
4388
|
+
}
|
|
4389
|
+
];
|
|
3667
4390
|
}
|
|
3668
|
-
async
|
|
3669
|
-
const
|
|
3670
|
-
const serialized = JSON.stringify(configOptions);
|
|
3671
|
-
if (this.lastSentConfigOptions && JSON.stringify(this.lastSentConfigOptions) === serialized) {
|
|
3672
|
-
return;
|
|
3673
|
-
}
|
|
3674
|
-
this.lastSentConfigOptions = configOptions;
|
|
4391
|
+
async sendAvailableCommandsUpdate() {
|
|
4392
|
+
const commands = await this.session.query.supportedCommands();
|
|
3675
4393
|
await this.client.sessionUpdate({
|
|
3676
|
-
sessionId:
|
|
4394
|
+
sessionId: this.sessionId,
|
|
3677
4395
|
update: {
|
|
3678
|
-
sessionUpdate: "
|
|
3679
|
-
|
|
4396
|
+
sessionUpdate: "available_commands_update",
|
|
4397
|
+
availableCommands: getAvailableSlashCommands(commands)
|
|
3680
4398
|
}
|
|
3681
4399
|
});
|
|
3682
4400
|
}
|
|
3683
|
-
|
|
3684
|
-
const backupExists = fs2.existsSync(
|
|
3685
|
-
path3.resolve(os3.homedir(), ".claude.json.backup")
|
|
3686
|
-
);
|
|
3687
|
-
const configExists = fs2.existsSync(
|
|
3688
|
-
path3.resolve(os3.homedir(), ".claude.json")
|
|
3689
|
-
);
|
|
3690
|
-
if (backupExists && !configExists) {
|
|
3691
|
-
throw import_sdk2.RequestError.authRequired();
|
|
3692
|
-
}
|
|
3693
|
-
}
|
|
3694
|
-
async trySetModel(q, modelId) {
|
|
4401
|
+
async replaySessionHistory(sessionId) {
|
|
3695
4402
|
try {
|
|
3696
|
-
await
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
4403
|
+
const messages = await (0, import_claude_agent_sdk.getSessionMessages)(sessionId, {
|
|
4404
|
+
dir: this.session.cwd
|
|
4405
|
+
});
|
|
4406
|
+
const replayContext = {
|
|
4407
|
+
session: this.session,
|
|
4408
|
+
sessionId,
|
|
4409
|
+
client: this.client,
|
|
4410
|
+
toolUseCache: this.toolUseCache,
|
|
4411
|
+
fileContentCache: this.fileContentCache,
|
|
4412
|
+
logger: this.logger,
|
|
4413
|
+
registerHooks: false
|
|
4414
|
+
};
|
|
4415
|
+
for (const msg of messages) {
|
|
4416
|
+
const sdkMessage = {
|
|
4417
|
+
type: msg.type,
|
|
4418
|
+
message: msg.message,
|
|
4419
|
+
parent_tool_use_id: msg.parent_tool_use_id
|
|
4420
|
+
};
|
|
4421
|
+
await handleUserAssistantMessage(
|
|
4422
|
+
sdkMessage,
|
|
4423
|
+
replayContext
|
|
4424
|
+
);
|
|
3708
4425
|
}
|
|
3709
|
-
|
|
4426
|
+
} catch (err) {
|
|
4427
|
+
this.logger.warn("Failed to replay session history", {
|
|
4428
|
+
sessionId,
|
|
4429
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4430
|
+
});
|
|
3710
4431
|
}
|
|
3711
4432
|
}
|
|
4433
|
+
// ================================
|
|
4434
|
+
// EXTENSION METHODS
|
|
4435
|
+
// ================================
|
|
3712
4436
|
/**
|
|
3713
4437
|
* Fire-and-forget: fetch slash commands and MCP tool metadata in parallel.
|
|
3714
4438
|
* Both populate caches used later — neither is needed to return configOptions.
|
|
3715
4439
|
*/
|
|
3716
|
-
deferBackgroundFetches(q
|
|
4440
|
+
deferBackgroundFetches(q) {
|
|
3717
4441
|
Promise.all([
|
|
3718
|
-
|
|
4442
|
+
new Promise((resolve4) => setTimeout(resolve4, 10)).then(
|
|
4443
|
+
() => this.sendAvailableCommandsUpdate()
|
|
4444
|
+
),
|
|
3719
4445
|
fetchMcpToolMetadata(q, this.logger)
|
|
3720
|
-
]).
|
|
3721
|
-
this.
|
|
3722
|
-
|
|
3723
|
-
this.logger.warn("Failed to fetch deferred session data", { err });
|
|
3724
|
-
});
|
|
3725
|
-
}
|
|
3726
|
-
sendAvailableCommandsUpdate(sessionId, availableCommands) {
|
|
3727
|
-
setTimeout(() => {
|
|
3728
|
-
this.client.sessionUpdate({
|
|
3729
|
-
sessionId,
|
|
3730
|
-
update: {
|
|
3731
|
-
sessionUpdate: "available_commands_update",
|
|
3732
|
-
availableCommands
|
|
3733
|
-
}
|
|
3734
|
-
});
|
|
3735
|
-
}, 0);
|
|
4446
|
+
]).catch(
|
|
4447
|
+
(err) => this.logger.error("Background fetch failed", { error: err })
|
|
4448
|
+
);
|
|
3736
4449
|
}
|
|
3737
4450
|
async broadcastUserMessage(params) {
|
|
3738
4451
|
for (const chunk of params.prompt) {
|
|
@@ -3747,71 +4460,6 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3747
4460
|
this.appendNotification(params.sessionId, notification);
|
|
3748
4461
|
}
|
|
3749
4462
|
}
|
|
3750
|
-
async processMessages(sessionId) {
|
|
3751
|
-
const context = {
|
|
3752
|
-
session: this.session,
|
|
3753
|
-
sessionId,
|
|
3754
|
-
client: this.client,
|
|
3755
|
-
toolUseCache: this.toolUseCache,
|
|
3756
|
-
fileContentCache: this.fileContentCache,
|
|
3757
|
-
logger: this.logger
|
|
3758
|
-
};
|
|
3759
|
-
while (true) {
|
|
3760
|
-
const { value: message, done } = await this.session.query.next();
|
|
3761
|
-
if (done || !message) {
|
|
3762
|
-
return this.handleSessionEnd();
|
|
3763
|
-
}
|
|
3764
|
-
const response = await this.handleMessage(message, context);
|
|
3765
|
-
if (response) {
|
|
3766
|
-
return response;
|
|
3767
|
-
}
|
|
3768
|
-
}
|
|
3769
|
-
}
|
|
3770
|
-
handleSessionEnd() {
|
|
3771
|
-
if (this.session.cancelled) {
|
|
3772
|
-
return {
|
|
3773
|
-
stopReason: "cancelled",
|
|
3774
|
-
_meta: this.session.interruptReason ? { interruptReason: this.session.interruptReason } : void 0
|
|
3775
|
-
};
|
|
3776
|
-
}
|
|
3777
|
-
throw new Error("Session did not end in result");
|
|
3778
|
-
}
|
|
3779
|
-
async handleMessage(message, context) {
|
|
3780
|
-
switch (message.type) {
|
|
3781
|
-
case "system":
|
|
3782
|
-
await handleSystemMessage(message, context);
|
|
3783
|
-
return null;
|
|
3784
|
-
case "result": {
|
|
3785
|
-
const result = handleResultMessage(message, context);
|
|
3786
|
-
if (result.error) throw result.error;
|
|
3787
|
-
if (result.shouldStop) {
|
|
3788
|
-
return {
|
|
3789
|
-
stopReason: result.stopReason
|
|
3790
|
-
};
|
|
3791
|
-
}
|
|
3792
|
-
return null;
|
|
3793
|
-
}
|
|
3794
|
-
case "stream_event":
|
|
3795
|
-
await handleStreamEvent(message, context);
|
|
3796
|
-
return null;
|
|
3797
|
-
case "user":
|
|
3798
|
-
case "assistant": {
|
|
3799
|
-
const result = await handleUserAssistantMessage(message, context);
|
|
3800
|
-
if (result.error) throw result.error;
|
|
3801
|
-
if (result.shouldStop) {
|
|
3802
|
-
return { stopReason: "end_turn" };
|
|
3803
|
-
}
|
|
3804
|
-
return null;
|
|
3805
|
-
}
|
|
3806
|
-
case "tool_progress":
|
|
3807
|
-
case "auth_status":
|
|
3808
|
-
case "tool_use_summary":
|
|
3809
|
-
return null;
|
|
3810
|
-
default:
|
|
3811
|
-
unreachable(message, this.logger);
|
|
3812
|
-
return null;
|
|
3813
|
-
}
|
|
3814
|
-
}
|
|
3815
4463
|
};
|
|
3816
4464
|
|
|
3817
4465
|
// src/adapters/codex/spawn.ts
|
|
@@ -3876,7 +4524,7 @@ function spawnCodexProcess(options) {
|
|
|
3876
4524
|
detached: process.platform !== "win32"
|
|
3877
4525
|
});
|
|
3878
4526
|
child.stderr?.on("data", (data) => {
|
|
3879
|
-
logger.
|
|
4527
|
+
logger.debug("codex-acp stderr:", data.toString());
|
|
3880
4528
|
});
|
|
3881
4529
|
child.on("error", (err) => {
|
|
3882
4530
|
logger.error("codex-acp process error:", err);
|
|
@@ -4701,8 +5349,8 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4701
5349
|
};
|
|
4702
5350
|
|
|
4703
5351
|
// ../git/dist/queries.js
|
|
4704
|
-
var
|
|
4705
|
-
var
|
|
5352
|
+
var fs6 = __toESM(require("fs/promises"), 1);
|
|
5353
|
+
var path7 = __toESM(require("path"), 1);
|
|
4706
5354
|
|
|
4707
5355
|
// ../../node_modules/simple-git/dist/esm/index.js
|
|
4708
5356
|
var import_node_buffer = require("buffer");
|
|
@@ -4741,8 +5389,8 @@ function pathspec(...paths) {
|
|
|
4741
5389
|
cache.set(key, paths);
|
|
4742
5390
|
return key;
|
|
4743
5391
|
}
|
|
4744
|
-
function isPathSpec(
|
|
4745
|
-
return
|
|
5392
|
+
function isPathSpec(path9) {
|
|
5393
|
+
return path9 instanceof String && cache.has(path9);
|
|
4746
5394
|
}
|
|
4747
5395
|
function toPaths(pathSpec) {
|
|
4748
5396
|
return cache.get(pathSpec) || [];
|
|
@@ -4831,8 +5479,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
4831
5479
|
function forEachLineWithContent(input, callback) {
|
|
4832
5480
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
4833
5481
|
}
|
|
4834
|
-
function folderExists(
|
|
4835
|
-
return (0, import_file_exists.exists)(
|
|
5482
|
+
function folderExists(path9) {
|
|
5483
|
+
return (0, import_file_exists.exists)(path9, import_file_exists.FOLDER);
|
|
4836
5484
|
}
|
|
4837
5485
|
function append(target, item) {
|
|
4838
5486
|
if (Array.isArray(target)) {
|
|
@@ -5236,8 +5884,8 @@ function checkIsRepoRootTask() {
|
|
|
5236
5884
|
commands,
|
|
5237
5885
|
format: "utf-8",
|
|
5238
5886
|
onError,
|
|
5239
|
-
parser(
|
|
5240
|
-
return /^\.(git)?$/.test(
|
|
5887
|
+
parser(path9) {
|
|
5888
|
+
return /^\.(git)?$/.test(path9.trim());
|
|
5241
5889
|
}
|
|
5242
5890
|
};
|
|
5243
5891
|
}
|
|
@@ -5671,11 +6319,11 @@ function parseGrep(grep) {
|
|
|
5671
6319
|
const paths = /* @__PURE__ */ new Set();
|
|
5672
6320
|
const results = {};
|
|
5673
6321
|
forEachLineWithContent(grep, (input) => {
|
|
5674
|
-
const [
|
|
5675
|
-
paths.add(
|
|
5676
|
-
(results[
|
|
6322
|
+
const [path9, line, preview] = input.split(NULL);
|
|
6323
|
+
paths.add(path9);
|
|
6324
|
+
(results[path9] = results[path9] || []).push({
|
|
5677
6325
|
line: asNumber(line),
|
|
5678
|
-
path:
|
|
6326
|
+
path: path9,
|
|
5679
6327
|
preview
|
|
5680
6328
|
});
|
|
5681
6329
|
});
|
|
@@ -6440,14 +7088,14 @@ var init_hash_object = __esm({
|
|
|
6440
7088
|
init_task();
|
|
6441
7089
|
}
|
|
6442
7090
|
});
|
|
6443
|
-
function parseInit(bare,
|
|
7091
|
+
function parseInit(bare, path9, text2) {
|
|
6444
7092
|
const response = String(text2).trim();
|
|
6445
7093
|
let result;
|
|
6446
7094
|
if (result = initResponseRegex.exec(response)) {
|
|
6447
|
-
return new InitSummary(bare,
|
|
7095
|
+
return new InitSummary(bare, path9, false, result[1]);
|
|
6448
7096
|
}
|
|
6449
7097
|
if (result = reInitResponseRegex.exec(response)) {
|
|
6450
|
-
return new InitSummary(bare,
|
|
7098
|
+
return new InitSummary(bare, path9, true, result[1]);
|
|
6451
7099
|
}
|
|
6452
7100
|
let gitDir = "";
|
|
6453
7101
|
const tokens = response.split(" ");
|
|
@@ -6458,7 +7106,7 @@ function parseInit(bare, path8, text2) {
|
|
|
6458
7106
|
break;
|
|
6459
7107
|
}
|
|
6460
7108
|
}
|
|
6461
|
-
return new InitSummary(bare,
|
|
7109
|
+
return new InitSummary(bare, path9, /^re/i.test(response), gitDir);
|
|
6462
7110
|
}
|
|
6463
7111
|
var InitSummary;
|
|
6464
7112
|
var initResponseRegex;
|
|
@@ -6467,9 +7115,9 @@ var init_InitSummary = __esm({
|
|
|
6467
7115
|
"src/lib/responses/InitSummary.ts"() {
|
|
6468
7116
|
"use strict";
|
|
6469
7117
|
InitSummary = class {
|
|
6470
|
-
constructor(bare,
|
|
7118
|
+
constructor(bare, path9, existing, gitDir) {
|
|
6471
7119
|
this.bare = bare;
|
|
6472
|
-
this.path =
|
|
7120
|
+
this.path = path9;
|
|
6473
7121
|
this.existing = existing;
|
|
6474
7122
|
this.gitDir = gitDir;
|
|
6475
7123
|
}
|
|
@@ -6481,7 +7129,7 @@ var init_InitSummary = __esm({
|
|
|
6481
7129
|
function hasBareCommand(command) {
|
|
6482
7130
|
return command.includes(bareCommand);
|
|
6483
7131
|
}
|
|
6484
|
-
function initTask(bare = false,
|
|
7132
|
+
function initTask(bare = false, path9, customArgs) {
|
|
6485
7133
|
const commands = ["init", ...customArgs];
|
|
6486
7134
|
if (bare && !hasBareCommand(commands)) {
|
|
6487
7135
|
commands.splice(1, 0, bareCommand);
|
|
@@ -6490,7 +7138,7 @@ function initTask(bare = false, path8, customArgs) {
|
|
|
6490
7138
|
commands,
|
|
6491
7139
|
format: "utf-8",
|
|
6492
7140
|
parser(text2) {
|
|
6493
|
-
return parseInit(commands.includes("--bare"),
|
|
7141
|
+
return parseInit(commands.includes("--bare"), path9, text2);
|
|
6494
7142
|
}
|
|
6495
7143
|
};
|
|
6496
7144
|
}
|
|
@@ -7306,12 +7954,12 @@ var init_FileStatusSummary = __esm({
|
|
|
7306
7954
|
"use strict";
|
|
7307
7955
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
7308
7956
|
FileStatusSummary = class {
|
|
7309
|
-
constructor(
|
|
7310
|
-
this.path =
|
|
7957
|
+
constructor(path9, index, working_dir) {
|
|
7958
|
+
this.path = path9;
|
|
7311
7959
|
this.index = index;
|
|
7312
7960
|
this.working_dir = working_dir;
|
|
7313
7961
|
if (index === "R" || working_dir === "R") {
|
|
7314
|
-
const detail = fromPathRegex.exec(
|
|
7962
|
+
const detail = fromPathRegex.exec(path9) || [null, path9, path9];
|
|
7315
7963
|
this.from = detail[2] || "";
|
|
7316
7964
|
this.path = detail[1] || "";
|
|
7317
7965
|
}
|
|
@@ -7342,14 +7990,14 @@ function splitLine(result, lineStr) {
|
|
|
7342
7990
|
default:
|
|
7343
7991
|
return;
|
|
7344
7992
|
}
|
|
7345
|
-
function data(index, workingDir,
|
|
7993
|
+
function data(index, workingDir, path9) {
|
|
7346
7994
|
const raw = `${index}${workingDir}`;
|
|
7347
7995
|
const handler = parsers6.get(raw);
|
|
7348
7996
|
if (handler) {
|
|
7349
|
-
handler(result,
|
|
7997
|
+
handler(result, path9);
|
|
7350
7998
|
}
|
|
7351
7999
|
if (raw !== "##" && raw !== "!!") {
|
|
7352
|
-
result.files.push(new FileStatusSummary(
|
|
8000
|
+
result.files.push(new FileStatusSummary(path9, index, workingDir));
|
|
7353
8001
|
}
|
|
7354
8002
|
}
|
|
7355
8003
|
}
|
|
@@ -7662,9 +8310,9 @@ var init_simple_git_api = __esm({
|
|
|
7662
8310
|
next
|
|
7663
8311
|
);
|
|
7664
8312
|
}
|
|
7665
|
-
hashObject(
|
|
8313
|
+
hashObject(path9, write) {
|
|
7666
8314
|
return this._runTask(
|
|
7667
|
-
hashObjectTask(
|
|
8315
|
+
hashObjectTask(path9, write === true),
|
|
7668
8316
|
trailingFunctionArgument(arguments)
|
|
7669
8317
|
);
|
|
7670
8318
|
}
|
|
@@ -8017,8 +8665,8 @@ var init_branch = __esm({
|
|
|
8017
8665
|
}
|
|
8018
8666
|
});
|
|
8019
8667
|
function toPath(input) {
|
|
8020
|
-
const
|
|
8021
|
-
return
|
|
8668
|
+
const path9 = input.trim().replace(/^["']|["']$/g, "");
|
|
8669
|
+
return path9 && (0, import_node_path3.normalize)(path9);
|
|
8022
8670
|
}
|
|
8023
8671
|
var parseCheckIgnore;
|
|
8024
8672
|
var init_CheckIgnore = __esm({
|
|
@@ -8332,8 +8980,8 @@ __export(sub_module_exports, {
|
|
|
8332
8980
|
subModuleTask: () => subModuleTask,
|
|
8333
8981
|
updateSubModuleTask: () => updateSubModuleTask
|
|
8334
8982
|
});
|
|
8335
|
-
function addSubModuleTask(repo,
|
|
8336
|
-
return subModuleTask(["add", repo,
|
|
8983
|
+
function addSubModuleTask(repo, path9) {
|
|
8984
|
+
return subModuleTask(["add", repo, path9]);
|
|
8337
8985
|
}
|
|
8338
8986
|
function initSubModuleTask(customArgs) {
|
|
8339
8987
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -8663,8 +9311,8 @@ var require_git = __commonJS2({
|
|
|
8663
9311
|
}
|
|
8664
9312
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
8665
9313
|
};
|
|
8666
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
8667
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
9314
|
+
Git2.prototype.submoduleAdd = function(repo, path9, then) {
|
|
9315
|
+
return this._runTask(addSubModuleTask2(repo, path9), trailingFunctionArgument2(arguments));
|
|
8668
9316
|
};
|
|
8669
9317
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
8670
9318
|
return this._runTask(
|
|
@@ -9589,8 +10237,8 @@ var Saga = class {
|
|
|
9589
10237
|
|
|
9590
10238
|
// ../git/dist/sagas/tree.js
|
|
9591
10239
|
var import_node_fs3 = require("fs");
|
|
9592
|
-
var
|
|
9593
|
-
var
|
|
10240
|
+
var fs7 = __toESM(require("fs/promises"), 1);
|
|
10241
|
+
var path8 = __toESM(require("path"), 1);
|
|
9594
10242
|
var tar = __toESM(require("tar"), 1);
|
|
9595
10243
|
|
|
9596
10244
|
// ../git/dist/git-saga.js
|
|
@@ -9616,14 +10264,14 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9616
10264
|
tempIndexPath = null;
|
|
9617
10265
|
async executeGitOperations(input) {
|
|
9618
10266
|
const { baseDir, lastTreeHash, archivePath, signal } = input;
|
|
9619
|
-
const tmpDir =
|
|
10267
|
+
const tmpDir = path8.join(baseDir, ".git", "twig-tmp");
|
|
9620
10268
|
await this.step({
|
|
9621
10269
|
name: "create_tmp_dir",
|
|
9622
|
-
execute: () =>
|
|
10270
|
+
execute: () => fs7.mkdir(tmpDir, { recursive: true }),
|
|
9623
10271
|
rollback: async () => {
|
|
9624
10272
|
}
|
|
9625
10273
|
});
|
|
9626
|
-
this.tempIndexPath =
|
|
10274
|
+
this.tempIndexPath = path8.join(tmpDir, `index-${Date.now()}`);
|
|
9627
10275
|
const tempIndexGit = this.git.env({
|
|
9628
10276
|
...process.env,
|
|
9629
10277
|
GIT_INDEX_FILE: this.tempIndexPath
|
|
@@ -9633,7 +10281,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9633
10281
|
execute: () => tempIndexGit.raw(["read-tree", "HEAD"]),
|
|
9634
10282
|
rollback: async () => {
|
|
9635
10283
|
if (this.tempIndexPath) {
|
|
9636
|
-
await
|
|
10284
|
+
await fs7.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9637
10285
|
});
|
|
9638
10286
|
}
|
|
9639
10287
|
}
|
|
@@ -9642,7 +10290,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9642
10290
|
const treeHash = await this.readOnlyStep("write_tree", () => tempIndexGit.raw(["write-tree"]));
|
|
9643
10291
|
if (lastTreeHash && treeHash === lastTreeHash) {
|
|
9644
10292
|
this.log.debug("No changes since last capture", { treeHash });
|
|
9645
|
-
await
|
|
10293
|
+
await fs7.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9646
10294
|
});
|
|
9647
10295
|
return { snapshot: null, changed: false };
|
|
9648
10296
|
}
|
|
@@ -9654,7 +10302,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9654
10302
|
}
|
|
9655
10303
|
});
|
|
9656
10304
|
const changes = await this.readOnlyStep("get_changes", () => this.getChanges(this.git, baseCommit, treeHash));
|
|
9657
|
-
await
|
|
10305
|
+
await fs7.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9658
10306
|
});
|
|
9659
10307
|
const snapshot = {
|
|
9660
10308
|
treeHash,
|
|
@@ -9678,15 +10326,15 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9678
10326
|
if (filesToArchive.length === 0) {
|
|
9679
10327
|
return void 0;
|
|
9680
10328
|
}
|
|
9681
|
-
const existingFiles = filesToArchive.filter((f) => (0, import_node_fs3.existsSync)(
|
|
10329
|
+
const existingFiles = filesToArchive.filter((f) => (0, import_node_fs3.existsSync)(path8.join(baseDir, f)));
|
|
9682
10330
|
if (existingFiles.length === 0) {
|
|
9683
10331
|
return void 0;
|
|
9684
10332
|
}
|
|
9685
10333
|
await this.step({
|
|
9686
10334
|
name: "create_archive",
|
|
9687
10335
|
execute: async () => {
|
|
9688
|
-
const archiveDir =
|
|
9689
|
-
await
|
|
10336
|
+
const archiveDir = path8.dirname(archivePath);
|
|
10337
|
+
await fs7.mkdir(archiveDir, { recursive: true });
|
|
9690
10338
|
await tar.create({
|
|
9691
10339
|
gzip: true,
|
|
9692
10340
|
file: archivePath,
|
|
@@ -9694,7 +10342,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9694
10342
|
}, existingFiles);
|
|
9695
10343
|
},
|
|
9696
10344
|
rollback: async () => {
|
|
9697
|
-
await
|
|
10345
|
+
await fs7.rm(archivePath, { force: true }).catch(() => {
|
|
9698
10346
|
});
|
|
9699
10347
|
}
|
|
9700
10348
|
});
|
|
@@ -9793,9 +10441,9 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9793
10441
|
const filesToExtract = changes.filter((c) => c.status !== "D").map((c) => c.path);
|
|
9794
10442
|
await this.readOnlyStep("backup_existing_files", async () => {
|
|
9795
10443
|
for (const filePath of filesToExtract) {
|
|
9796
|
-
const fullPath =
|
|
10444
|
+
const fullPath = path8.join(baseDir, filePath);
|
|
9797
10445
|
try {
|
|
9798
|
-
const content = await
|
|
10446
|
+
const content = await fs7.readFile(fullPath);
|
|
9799
10447
|
this.fileBackups.set(filePath, content);
|
|
9800
10448
|
} catch {
|
|
9801
10449
|
}
|
|
@@ -9812,16 +10460,16 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9812
10460
|
},
|
|
9813
10461
|
rollback: async () => {
|
|
9814
10462
|
for (const filePath of this.extractedFiles) {
|
|
9815
|
-
const fullPath =
|
|
10463
|
+
const fullPath = path8.join(baseDir, filePath);
|
|
9816
10464
|
const backup = this.fileBackups.get(filePath);
|
|
9817
10465
|
if (backup) {
|
|
9818
|
-
const dir =
|
|
9819
|
-
await
|
|
10466
|
+
const dir = path8.dirname(fullPath);
|
|
10467
|
+
await fs7.mkdir(dir, { recursive: true }).catch(() => {
|
|
9820
10468
|
});
|
|
9821
|
-
await
|
|
10469
|
+
await fs7.writeFile(fullPath, backup).catch(() => {
|
|
9822
10470
|
});
|
|
9823
10471
|
} else {
|
|
9824
|
-
await
|
|
10472
|
+
await fs7.rm(fullPath, { force: true }).catch(() => {
|
|
9825
10473
|
});
|
|
9826
10474
|
}
|
|
9827
10475
|
}
|
|
@@ -9829,10 +10477,10 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9829
10477
|
});
|
|
9830
10478
|
}
|
|
9831
10479
|
for (const change of changes.filter((c) => c.status === "D")) {
|
|
9832
|
-
const fullPath =
|
|
10480
|
+
const fullPath = path8.join(baseDir, change.path);
|
|
9833
10481
|
const backupContent = await this.readOnlyStep(`backup_${change.path}`, async () => {
|
|
9834
10482
|
try {
|
|
9835
|
-
return await
|
|
10483
|
+
return await fs7.readFile(fullPath);
|
|
9836
10484
|
} catch {
|
|
9837
10485
|
return null;
|
|
9838
10486
|
}
|
|
@@ -9840,15 +10488,15 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9840
10488
|
await this.step({
|
|
9841
10489
|
name: `delete_${change.path}`,
|
|
9842
10490
|
execute: async () => {
|
|
9843
|
-
await
|
|
10491
|
+
await fs7.rm(fullPath, { force: true });
|
|
9844
10492
|
this.log.debug(`Deleted file: ${change.path}`);
|
|
9845
10493
|
},
|
|
9846
10494
|
rollback: async () => {
|
|
9847
10495
|
if (backupContent) {
|
|
9848
|
-
const dir =
|
|
9849
|
-
await
|
|
10496
|
+
const dir = path8.dirname(fullPath);
|
|
10497
|
+
await fs7.mkdir(dir, { recursive: true }).catch(() => {
|
|
9850
10498
|
});
|
|
9851
|
-
await
|
|
10499
|
+
await fs7.writeFile(fullPath, backupContent).catch(() => {
|
|
9852
10500
|
});
|
|
9853
10501
|
}
|
|
9854
10502
|
}
|