@posthog/agent 2.1.125 → 2.1.137
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/claude/conversion/tool-use-to-acp.d.ts +14 -28
- package/dist/adapters/claude/conversion/tool-use-to-acp.js +116 -164
- package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -1
- package/dist/adapters/claude/permissions/permission-options.js +33 -0
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
- package/dist/adapters/claude/tools.js +21 -11
- package/dist/adapters/claude/tools.js.map +1 -1
- package/dist/agent.js +1251 -640
- package/dist/agent.js.map +1 -1
- package/dist/posthog-api.js +2 -2
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.js +1295 -684
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +1278 -669
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +2 -2
- package/src/adapters/base-acp-agent.ts +6 -3
- package/src/adapters/claude/UPSTREAM.md +63 -0
- package/src/adapters/claude/claude-agent.ts +682 -421
- package/src/adapters/claude/conversion/sdk-to-acp.ts +249 -85
- package/src/adapters/claude/conversion/tool-use-to-acp.ts +174 -149
- package/src/adapters/claude/hooks.ts +53 -1
- package/src/adapters/claude/permissions/permission-handlers.ts +39 -21
- package/src/adapters/claude/session/commands.ts +13 -9
- package/src/adapters/claude/session/mcp-config.ts +2 -5
- package/src/adapters/claude/session/options.ts +58 -6
- package/src/adapters/claude/session/settings.ts +326 -0
- package/src/adapters/claude/tools.ts +1 -0
- package/src/adapters/claude/types.ts +38 -0
- package/src/execution-mode.ts +26 -10
- package/src/server/agent-server.test.ts +41 -1
- package/src/session-log-writer.ts +1 -36
- package/src/utils/common.ts +1 -1
package/dist/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.137",
|
|
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: {
|
|
@@ -980,7 +980,6 @@ var package_default = {
|
|
|
980
980
|
"@twig/git": "workspace:*",
|
|
981
981
|
"@types/bun": "latest",
|
|
982
982
|
"@types/tar": "^6.1.13",
|
|
983
|
-
minimatch: "^10.0.3",
|
|
984
983
|
msw: "^2.12.7",
|
|
985
984
|
tsup: "^8.5.1",
|
|
986
985
|
tsx: "^4.20.6",
|
|
@@ -1001,6 +1000,7 @@ var package_default = {
|
|
|
1001
1000
|
commander: "^14.0.2",
|
|
1002
1001
|
hono: "^4.11.7",
|
|
1003
1002
|
jsonwebtoken: "^9.0.2",
|
|
1003
|
+
minimatch: "^10.0.3",
|
|
1004
1004
|
tar: "^7.5.0",
|
|
1005
1005
|
uuid: "13.0.0",
|
|
1006
1006
|
"yoga-wasm-web": "^0.3.3",
|
|
@@ -1281,9 +1281,10 @@ function nodeWritableToWebWritable(nodeStream) {
|
|
|
1281
1281
|
}
|
|
1282
1282
|
|
|
1283
1283
|
// src/adapters/claude/claude-agent.ts
|
|
1284
|
-
var
|
|
1285
|
-
var
|
|
1286
|
-
var
|
|
1284
|
+
var import_node_crypto = require("crypto");
|
|
1285
|
+
var fs3 = __toESM(require("fs"), 1);
|
|
1286
|
+
var os4 = __toESM(require("os"), 1);
|
|
1287
|
+
var path4 = __toESM(require("path"), 1);
|
|
1287
1288
|
var import_sdk2 = require("@agentclientprotocol/sdk");
|
|
1288
1289
|
var import_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
|
|
1289
1290
|
var import_uuid = require("uuid");
|
|
@@ -1305,7 +1306,7 @@ function unreachable(value, logger) {
|
|
|
1305
1306
|
try {
|
|
1306
1307
|
valueAsString = JSON.stringify(value);
|
|
1307
1308
|
} catch {
|
|
1308
|
-
valueAsString = value;
|
|
1309
|
+
valueAsString = String(value);
|
|
1309
1310
|
}
|
|
1310
1311
|
logger.error(`Unexpected case: ${valueAsString}`);
|
|
1311
1312
|
}
|
|
@@ -1377,19 +1378,20 @@ var BaseAcpAgent = class {
|
|
|
1377
1378
|
}
|
|
1378
1379
|
async cancel(params) {
|
|
1379
1380
|
if (this.sessionId !== params.sessionId) {
|
|
1380
|
-
throw new Error("Session
|
|
1381
|
+
throw new Error("Session ID mismatch");
|
|
1381
1382
|
}
|
|
1382
1383
|
this.session.cancelled = true;
|
|
1383
1384
|
const meta = params._meta;
|
|
1384
1385
|
if (meta?.interruptReason) {
|
|
1385
1386
|
this.session.interruptReason = meta.interruptReason;
|
|
1386
1387
|
}
|
|
1387
|
-
await this.
|
|
1388
|
+
await this.interrupt();
|
|
1388
1389
|
}
|
|
1389
1390
|
async closeSession() {
|
|
1390
1391
|
try {
|
|
1391
1392
|
this.session.abortController.abort();
|
|
1392
1393
|
await this.cancel({ sessionId: this.sessionId });
|
|
1394
|
+
this.session.settingsManager.dispose();
|
|
1393
1395
|
this.logger.info("Closed session", { sessionId: this.sessionId });
|
|
1394
1396
|
} catch (err) {
|
|
1395
1397
|
this.logger.warn("Failed to close session", {
|
|
@@ -1564,8 +1566,8 @@ var ToolContentBuilder = class {
|
|
|
1564
1566
|
this.items.push({ type: "content", content: image(data, mimeType, uri) });
|
|
1565
1567
|
return this;
|
|
1566
1568
|
}
|
|
1567
|
-
diff(
|
|
1568
|
-
this.items.push({ type: "diff", path:
|
|
1569
|
+
diff(path9, oldText, newText) {
|
|
1570
|
+
this.items.push({ type: "diff", path: path9, oldText, newText });
|
|
1569
1571
|
return this;
|
|
1570
1572
|
}
|
|
1571
1573
|
build() {
|
|
@@ -1585,7 +1587,7 @@ var registerHookCallback = (toolUseID, {
|
|
|
1585
1587
|
onPostToolUseHook
|
|
1586
1588
|
};
|
|
1587
1589
|
};
|
|
1588
|
-
var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
|
|
1590
|
+
var createPostToolUseHook = ({ onModeChange, logger }) => async (input, toolUseID) => {
|
|
1589
1591
|
if (input.hook_event_name === "PostToolUse") {
|
|
1590
1592
|
const toolName = input.tool_name;
|
|
1591
1593
|
if (onModeChange && toolName === "EnterPlanMode") {
|
|
@@ -1600,11 +1602,54 @@ var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
|
|
|
1600
1602
|
input.tool_response
|
|
1601
1603
|
);
|
|
1602
1604
|
delete toolUseCallbacks[toolUseID];
|
|
1605
|
+
} else {
|
|
1606
|
+
logger?.error(
|
|
1607
|
+
`No onPostToolUseHook found for tool use ID: ${toolUseID}`
|
|
1608
|
+
);
|
|
1609
|
+
delete toolUseCallbacks[toolUseID];
|
|
1603
1610
|
}
|
|
1604
1611
|
}
|
|
1605
1612
|
}
|
|
1606
1613
|
return { continue: true };
|
|
1607
1614
|
};
|
|
1615
|
+
var createPreToolUseHook = (settingsManager, logger) => async (input, _toolUseID) => {
|
|
1616
|
+
if (input.hook_event_name !== "PreToolUse") {
|
|
1617
|
+
return { continue: true };
|
|
1618
|
+
}
|
|
1619
|
+
const toolName = input.tool_name;
|
|
1620
|
+
const toolInput = input.tool_input;
|
|
1621
|
+
const permissionCheck = settingsManager.checkPermission(
|
|
1622
|
+
toolName,
|
|
1623
|
+
toolInput
|
|
1624
|
+
);
|
|
1625
|
+
if (permissionCheck.decision !== "ask") {
|
|
1626
|
+
logger.info(
|
|
1627
|
+
`[PreToolUseHook] Tool: ${toolName}, Decision: ${permissionCheck.decision}, Rule: ${permissionCheck.rule}`
|
|
1628
|
+
);
|
|
1629
|
+
}
|
|
1630
|
+
switch (permissionCheck.decision) {
|
|
1631
|
+
case "allow":
|
|
1632
|
+
return {
|
|
1633
|
+
continue: true,
|
|
1634
|
+
hookSpecificOutput: {
|
|
1635
|
+
hookEventName: "PreToolUse",
|
|
1636
|
+
permissionDecision: "allow",
|
|
1637
|
+
permissionDecisionReason: `Allowed by settings rule: ${permissionCheck.rule}`
|
|
1638
|
+
}
|
|
1639
|
+
};
|
|
1640
|
+
case "deny":
|
|
1641
|
+
return {
|
|
1642
|
+
continue: true,
|
|
1643
|
+
hookSpecificOutput: {
|
|
1644
|
+
hookEventName: "PreToolUse",
|
|
1645
|
+
permissionDecision: "deny",
|
|
1646
|
+
permissionDecisionReason: `Denied by settings rule: ${permissionCheck.rule}`
|
|
1647
|
+
}
|
|
1648
|
+
};
|
|
1649
|
+
default:
|
|
1650
|
+
return { continue: true };
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1608
1653
|
|
|
1609
1654
|
// src/adapters/claude/mcp/tool-metadata.ts
|
|
1610
1655
|
var mcpToolMetadataCache = /* @__PURE__ */ new Map();
|
|
@@ -1681,79 +1726,7 @@ var SYSTEM_REMINDER = `
|
|
|
1681
1726
|
<system-reminder>
|
|
1682
1727
|
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
1728
|
</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]" })) {
|
|
1729
|
+
function toolInfoFromToolUse(toolUse, options) {
|
|
1757
1730
|
const name = toolUse.name;
|
|
1758
1731
|
const input = toolUse.input;
|
|
1759
1732
|
switch (name) {
|
|
@@ -1778,6 +1751,13 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1778
1751
|
locations: input?.notebook_path ? [{ path: String(input.notebook_path) }] : []
|
|
1779
1752
|
};
|
|
1780
1753
|
case "Bash":
|
|
1754
|
+
if (options?.supportsTerminalOutput && options?.toolUseId) {
|
|
1755
|
+
return {
|
|
1756
|
+
title: input?.description ? String(input.description) : "Execute command",
|
|
1757
|
+
kind: "execute",
|
|
1758
|
+
content: [{ type: "terminal", terminalId: options.toolUseId }]
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1781
1761
|
return {
|
|
1782
1762
|
title: input?.description ? String(input.description) : "Execute command",
|
|
1783
1763
|
kind: "execute",
|
|
@@ -1798,11 +1778,11 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1798
1778
|
case "Read": {
|
|
1799
1779
|
let limit = "";
|
|
1800
1780
|
const inputLimit = input?.limit;
|
|
1801
|
-
const inputOffset = input?.offset ??
|
|
1781
|
+
const inputOffset = input?.offset ?? 1;
|
|
1802
1782
|
if (inputLimit) {
|
|
1803
|
-
limit = ` (${inputOffset
|
|
1804
|
-
} else if (inputOffset) {
|
|
1805
|
-
limit = ` (from line ${inputOffset
|
|
1783
|
+
limit = ` (${inputOffset} - ${inputOffset + inputLimit - 1})`;
|
|
1784
|
+
} else if (inputOffset > 1) {
|
|
1785
|
+
limit = ` (from line ${inputOffset})`;
|
|
1806
1786
|
}
|
|
1807
1787
|
return {
|
|
1808
1788
|
title: `Read ${input?.file_path ? String(input.file_path) : "File"}${limit}`,
|
|
@@ -1824,39 +1804,21 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1824
1804
|
locations: []
|
|
1825
1805
|
};
|
|
1826
1806
|
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
|
-
}
|
|
1807
|
+
const path9 = input?.file_path ? String(input.file_path) : void 0;
|
|
1808
|
+
const oldText = input?.old_string ? String(input.old_string) : null;
|
|
1809
|
+
const newText = input?.new_string ? String(input.new_string) : "";
|
|
1848
1810
|
return {
|
|
1849
|
-
title:
|
|
1811
|
+
title: path9 ? `Edit \`${path9}\`` : "Edit",
|
|
1850
1812
|
kind: "edit",
|
|
1851
|
-
content: input &&
|
|
1813
|
+
content: input && path9 ? [
|
|
1852
1814
|
{
|
|
1853
1815
|
type: "diff",
|
|
1854
|
-
path:
|
|
1816
|
+
path: path9,
|
|
1855
1817
|
oldText,
|
|
1856
1818
|
newText
|
|
1857
1819
|
}
|
|
1858
1820
|
] : [],
|
|
1859
|
-
locations:
|
|
1821
|
+
locations: path9 ? [{ path: path9 }] : []
|
|
1860
1822
|
};
|
|
1861
1823
|
}
|
|
1862
1824
|
case "Write": {
|
|
@@ -1910,10 +1872,10 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1910
1872
|
}
|
|
1911
1873
|
if (input?.output_mode) {
|
|
1912
1874
|
switch (input.output_mode) {
|
|
1913
|
-
case "
|
|
1875
|
+
case "files_with_matches":
|
|
1914
1876
|
label += " -l";
|
|
1915
1877
|
break;
|
|
1916
|
-
case "
|
|
1878
|
+
case "count":
|
|
1917
1879
|
label += " -c";
|
|
1918
1880
|
break;
|
|
1919
1881
|
default:
|
|
@@ -1932,7 +1894,9 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1932
1894
|
if (input?.multiline) {
|
|
1933
1895
|
label += " -P";
|
|
1934
1896
|
}
|
|
1935
|
-
|
|
1897
|
+
if (input?.pattern) {
|
|
1898
|
+
label += ` "${String(input.pattern)}"`;
|
|
1899
|
+
}
|
|
1936
1900
|
if (input?.path) {
|
|
1937
1901
|
label += ` ${String(input.path)}`;
|
|
1938
1902
|
}
|
|
@@ -2026,7 +1990,49 @@ function mcpToolInfo(name, _input) {
|
|
|
2026
1990
|
content: []
|
|
2027
1991
|
};
|
|
2028
1992
|
}
|
|
2029
|
-
function
|
|
1993
|
+
function toolUpdateFromEditToolResponse(toolResponse) {
|
|
1994
|
+
if (!toolResponse || typeof toolResponse !== "object") return null;
|
|
1995
|
+
const response = toolResponse;
|
|
1996
|
+
const patches = response.structuredPatch;
|
|
1997
|
+
if (!Array.isArray(patches) || patches.length === 0) return null;
|
|
1998
|
+
const content = [];
|
|
1999
|
+
const locations = [];
|
|
2000
|
+
for (const patch of patches) {
|
|
2001
|
+
if (!patch.hunks || patch.hunks.length === 0) continue;
|
|
2002
|
+
const filePath = patch.newFileName || patch.oldFileName;
|
|
2003
|
+
const oldLines = [];
|
|
2004
|
+
const newLines = [];
|
|
2005
|
+
for (const hunk of patch.hunks) {
|
|
2006
|
+
for (const line of hunk.lines) {
|
|
2007
|
+
if (line.startsWith("-")) {
|
|
2008
|
+
oldLines.push(line.slice(1));
|
|
2009
|
+
} else if (line.startsWith("+")) {
|
|
2010
|
+
newLines.push(line.slice(1));
|
|
2011
|
+
} else if (line.startsWith(" ")) {
|
|
2012
|
+
oldLines.push(line.slice(1));
|
|
2013
|
+
newLines.push(line.slice(1));
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
content.push({
|
|
2018
|
+
type: "diff",
|
|
2019
|
+
path: filePath,
|
|
2020
|
+
oldText: oldLines.join("\n"),
|
|
2021
|
+
newText: newLines.join("\n")
|
|
2022
|
+
});
|
|
2023
|
+
const firstHunk = patch.hunks[0];
|
|
2024
|
+
locations.push({
|
|
2025
|
+
path: filePath,
|
|
2026
|
+
line: firstHunk.newStart
|
|
2027
|
+
});
|
|
2028
|
+
}
|
|
2029
|
+
if (content.length === 0) return null;
|
|
2030
|
+
return { content, locations };
|
|
2031
|
+
}
|
|
2032
|
+
function toolUpdateFromToolResult(toolResult, toolUse, options) {
|
|
2033
|
+
if ("is_error" in toolResult && toolResult.is_error && toolResult.content && toolResult.content.length > 0) {
|
|
2034
|
+
return toAcpContentUpdate(toolResult.content, true);
|
|
2035
|
+
}
|
|
2030
2036
|
switch (toolUse?.name) {
|
|
2031
2037
|
case "Read":
|
|
2032
2038
|
if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
|
|
@@ -2043,6 +2049,16 @@ function toolUpdateFromToolResult(toolResult, toolUse) {
|
|
|
2043
2049
|
)
|
|
2044
2050
|
};
|
|
2045
2051
|
}
|
|
2052
|
+
if (itemObj.type === "image" && itemObj.source) {
|
|
2053
|
+
return {
|
|
2054
|
+
type: "content",
|
|
2055
|
+
content: {
|
|
2056
|
+
type: "image",
|
|
2057
|
+
data: itemObj.source.data ?? "",
|
|
2058
|
+
mimeType: itemObj.source.media_type ?? "image/png"
|
|
2059
|
+
}
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2046
2062
|
return {
|
|
2047
2063
|
type: "content",
|
|
2048
2064
|
content: item
|
|
@@ -2058,18 +2074,51 @@ function toolUpdateFromToolResult(toolResult, toolUse) {
|
|
|
2058
2074
|
}
|
|
2059
2075
|
return {};
|
|
2060
2076
|
case "Bash": {
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2077
|
+
const result = toolResult.content;
|
|
2078
|
+
const terminalId = "tool_use_id" in toolResult ? String(toolResult.tool_use_id) : "";
|
|
2079
|
+
const isError = "is_error" in toolResult && toolResult.is_error;
|
|
2080
|
+
let output = "";
|
|
2081
|
+
let exitCode = isError ? 1 : 0;
|
|
2082
|
+
if (result && typeof result === "object" && "type" in result && result.type === "bash_code_execution_result") {
|
|
2083
|
+
const bashResult = result;
|
|
2084
|
+
output = [bashResult.stdout, bashResult.stderr].filter(Boolean).join("\n");
|
|
2085
|
+
exitCode = bashResult.return_code;
|
|
2086
|
+
} else if (typeof result === "string") {
|
|
2087
|
+
output = result;
|
|
2088
|
+
} else if (Array.isArray(result) && result.length > 0 && "text" in result[0] && typeof result[0].text === "string") {
|
|
2089
|
+
output = result.map((c) => c.text ?? "").join("\n");
|
|
2090
|
+
}
|
|
2091
|
+
if (options?.supportsTerminalOutput) {
|
|
2092
|
+
return {
|
|
2093
|
+
content: [{ type: "terminal", terminalId }],
|
|
2094
|
+
_meta: {
|
|
2095
|
+
terminal_info: {
|
|
2096
|
+
terminal_id: terminalId
|
|
2097
|
+
},
|
|
2098
|
+
terminal_output: {
|
|
2099
|
+
terminal_id: terminalId,
|
|
2100
|
+
data: output
|
|
2101
|
+
},
|
|
2102
|
+
terminal_exit: {
|
|
2103
|
+
terminal_id: terminalId,
|
|
2104
|
+
exit_code: exitCode,
|
|
2105
|
+
signal: null
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2110
|
+
if (output.trim()) {
|
|
2111
|
+
return {
|
|
2112
|
+
content: toolContent().text(`\`\`\`console
|
|
2113
|
+
${output.trimEnd()}
|
|
2114
|
+
\`\`\``).build()
|
|
2115
|
+
};
|
|
2070
2116
|
}
|
|
2071
2117
|
return {};
|
|
2072
2118
|
}
|
|
2119
|
+
case "Edit":
|
|
2120
|
+
case "Write":
|
|
2121
|
+
return {};
|
|
2073
2122
|
case "ExitPlanMode": {
|
|
2074
2123
|
return { title: "Exited Plan Mode" };
|
|
2075
2124
|
}
|
|
@@ -2229,6 +2278,7 @@ function handleThinkingChunk(chunk, parentToolCallId) {
|
|
|
2229
2278
|
return update;
|
|
2230
2279
|
}
|
|
2231
2280
|
function handleToolUseChunk(chunk, ctx) {
|
|
2281
|
+
const alreadyCached = chunk.id in ctx.toolUseCache;
|
|
2232
2282
|
ctx.toolUseCache[chunk.id] = chunk;
|
|
2233
2283
|
if (chunk.name === "TodoWrite") {
|
|
2234
2284
|
const input = chunk.input;
|
|
@@ -2240,37 +2290,60 @@ function handleToolUseChunk(chunk, ctx) {
|
|
|
2240
2290
|
}
|
|
2241
2291
|
return null;
|
|
2242
2292
|
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2293
|
+
if (!alreadyCached && ctx.registerHooks !== false) {
|
|
2294
|
+
registerHookCallback(chunk.id, {
|
|
2295
|
+
onPostToolUseHook: async (toolUseId, _toolInput, toolResponse) => {
|
|
2296
|
+
const toolUse = ctx.toolUseCache[toolUseId];
|
|
2297
|
+
if (toolUse) {
|
|
2298
|
+
const editUpdate = toolUse.name === "Edit" ? toolUpdateFromEditToolResponse(toolResponse) : null;
|
|
2299
|
+
await ctx.client.sessionUpdate({
|
|
2300
|
+
sessionId: ctx.sessionId,
|
|
2301
|
+
update: {
|
|
2302
|
+
_meta: toolMeta(toolUse.name, toolResponse, ctx.parentToolCallId),
|
|
2303
|
+
toolCallId: toolUseId,
|
|
2304
|
+
sessionUpdate: "tool_call_update",
|
|
2305
|
+
...editUpdate ? editUpdate : {}
|
|
2306
|
+
}
|
|
2307
|
+
});
|
|
2308
|
+
} else {
|
|
2309
|
+
ctx.logger.error(
|
|
2310
|
+
`Got a tool response for tool use that wasn't tracked: ${toolUseId}`
|
|
2311
|
+
);
|
|
2312
|
+
}
|
|
2259
2313
|
}
|
|
2260
|
-
}
|
|
2261
|
-
}
|
|
2314
|
+
});
|
|
2315
|
+
}
|
|
2262
2316
|
let rawInput;
|
|
2263
2317
|
try {
|
|
2264
2318
|
rawInput = JSON.parse(JSON.stringify(chunk.input));
|
|
2265
2319
|
} catch {
|
|
2266
2320
|
}
|
|
2321
|
+
const toolInfo = toolInfoFromToolUse(chunk, {
|
|
2322
|
+
supportsTerminalOutput: ctx.supportsTerminalOutput,
|
|
2323
|
+
toolUseId: chunk.id
|
|
2324
|
+
});
|
|
2325
|
+
const meta = {
|
|
2326
|
+
...toolMeta(chunk.name, void 0, ctx.parentToolCallId)
|
|
2327
|
+
};
|
|
2328
|
+
if (chunk.name === "Bash" && ctx.supportsTerminalOutput && !alreadyCached) {
|
|
2329
|
+
meta.terminal_info = { terminal_id: chunk.id };
|
|
2330
|
+
}
|
|
2331
|
+
if (alreadyCached) {
|
|
2332
|
+
return {
|
|
2333
|
+
_meta: meta,
|
|
2334
|
+
toolCallId: chunk.id,
|
|
2335
|
+
sessionUpdate: "tool_call_update",
|
|
2336
|
+
rawInput,
|
|
2337
|
+
...toolInfo
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2267
2340
|
return {
|
|
2268
|
-
_meta:
|
|
2341
|
+
_meta: meta,
|
|
2269
2342
|
toolCallId: chunk.id,
|
|
2270
2343
|
sessionUpdate: "tool_call",
|
|
2271
2344
|
rawInput,
|
|
2272
2345
|
status: "pending",
|
|
2273
|
-
...
|
|
2346
|
+
...toolInfo
|
|
2274
2347
|
};
|
|
2275
2348
|
}
|
|
2276
2349
|
function handleToolResultChunk(chunk, ctx) {
|
|
@@ -2279,36 +2352,71 @@ function handleToolResultChunk(chunk, ctx) {
|
|
|
2279
2352
|
ctx.logger.error(
|
|
2280
2353
|
`Got a tool result for tool use that wasn't tracked: ${chunk.tool_use_id}`
|
|
2281
2354
|
);
|
|
2282
|
-
return
|
|
2355
|
+
return [];
|
|
2283
2356
|
}
|
|
2284
2357
|
if (toolUse.name === "TodoWrite") {
|
|
2285
|
-
return
|
|
2358
|
+
return [];
|
|
2286
2359
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2360
|
+
const { _meta: resultMeta, ...toolUpdate } = toolUpdateFromToolResult(
|
|
2361
|
+
chunk,
|
|
2362
|
+
toolUse,
|
|
2363
|
+
{
|
|
2364
|
+
supportsTerminalOutput: ctx.supportsTerminalOutput,
|
|
2365
|
+
toolUseId: chunk.tool_use_id
|
|
2366
|
+
}
|
|
2367
|
+
);
|
|
2368
|
+
const updates = [];
|
|
2369
|
+
if (resultMeta?.terminal_output) {
|
|
2370
|
+
const terminalOutputMeta = {
|
|
2371
|
+
terminal_output: resultMeta.terminal_output
|
|
2372
|
+
};
|
|
2373
|
+
if (ctx.parentToolCallId) {
|
|
2374
|
+
terminalOutputMeta.claudeCode = {
|
|
2375
|
+
parentToolCallId: ctx.parentToolCallId
|
|
2376
|
+
};
|
|
2377
|
+
}
|
|
2378
|
+
updates.push({
|
|
2379
|
+
_meta: terminalOutputMeta,
|
|
2380
|
+
toolCallId: chunk.tool_use_id,
|
|
2381
|
+
sessionUpdate: "tool_call_update"
|
|
2382
|
+
});
|
|
2383
|
+
}
|
|
2384
|
+
const meta = {
|
|
2385
|
+
...toolMeta(toolUse.name, void 0, ctx.parentToolCallId),
|
|
2386
|
+
...resultMeta?.terminal_exit ? { terminal_exit: resultMeta.terminal_exit } : {}
|
|
2387
|
+
};
|
|
2388
|
+
updates.push({
|
|
2389
|
+
_meta: meta,
|
|
2289
2390
|
toolCallId: chunk.tool_use_id,
|
|
2290
2391
|
sessionUpdate: "tool_call_update",
|
|
2291
2392
|
status: chunk.is_error ? "failed" : "completed",
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
};
|
|
2393
|
+
rawOutput: chunk.content,
|
|
2394
|
+
...toolUpdate
|
|
2395
|
+
});
|
|
2396
|
+
return updates;
|
|
2297
2397
|
}
|
|
2298
2398
|
function processContentChunk(chunk, role, ctx) {
|
|
2299
2399
|
switch (chunk.type) {
|
|
2300
2400
|
case "text":
|
|
2301
|
-
case "text_delta":
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2401
|
+
case "text_delta": {
|
|
2402
|
+
const update = handleTextChunk(chunk, role, ctx.parentToolCallId);
|
|
2403
|
+
return update ? [update] : [];
|
|
2404
|
+
}
|
|
2405
|
+
case "image": {
|
|
2406
|
+
const update = handleImageChunk(chunk, role);
|
|
2407
|
+
return update ? [update] : [];
|
|
2408
|
+
}
|
|
2305
2409
|
case "thinking":
|
|
2306
|
-
case "thinking_delta":
|
|
2307
|
-
|
|
2410
|
+
case "thinking_delta": {
|
|
2411
|
+
const update = handleThinkingChunk(chunk, ctx.parentToolCallId);
|
|
2412
|
+
return update ? [update] : [];
|
|
2413
|
+
}
|
|
2308
2414
|
case "tool_use":
|
|
2309
2415
|
case "server_tool_use":
|
|
2310
|
-
case "mcp_tool_use":
|
|
2311
|
-
|
|
2416
|
+
case "mcp_tool_use": {
|
|
2417
|
+
const update = handleToolUseChunk(chunk, ctx);
|
|
2418
|
+
return update ? [update] : [];
|
|
2419
|
+
}
|
|
2312
2420
|
case "tool_result":
|
|
2313
2421
|
case "tool_search_tool_result":
|
|
2314
2422
|
case "web_fetch_tool_result":
|
|
@@ -2330,13 +2438,13 @@ function processContentChunk(chunk, role, ctx) {
|
|
|
2330
2438
|
case "container_upload":
|
|
2331
2439
|
case "compaction":
|
|
2332
2440
|
case "compaction_delta":
|
|
2333
|
-
return
|
|
2441
|
+
return [];
|
|
2334
2442
|
default:
|
|
2335
2443
|
unreachable(chunk, ctx.logger);
|
|
2336
|
-
return
|
|
2444
|
+
return [];
|
|
2337
2445
|
}
|
|
2338
2446
|
}
|
|
2339
|
-
function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId) {
|
|
2447
|
+
function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput) {
|
|
2340
2448
|
if (typeof content === "string") {
|
|
2341
2449
|
const update = {
|
|
2342
2450
|
sessionUpdate: messageUpdateType(role),
|
|
@@ -2357,18 +2465,19 @@ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentC
|
|
|
2357
2465
|
fileContentCache,
|
|
2358
2466
|
client,
|
|
2359
2467
|
logger,
|
|
2360
|
-
parentToolCallId
|
|
2468
|
+
parentToolCallId,
|
|
2469
|
+
registerHooks,
|
|
2470
|
+
supportsTerminalOutput
|
|
2361
2471
|
};
|
|
2362
2472
|
const output = [];
|
|
2363
2473
|
for (const chunk of content) {
|
|
2364
|
-
const update
|
|
2365
|
-
if (update) {
|
|
2474
|
+
for (const update of processContentChunk(chunk, role, ctx)) {
|
|
2366
2475
|
output.push({ sessionId, update });
|
|
2367
2476
|
}
|
|
2368
2477
|
}
|
|
2369
2478
|
return output;
|
|
2370
2479
|
}
|
|
2371
|
-
function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId) {
|
|
2480
|
+
function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput) {
|
|
2372
2481
|
const event = message.event;
|
|
2373
2482
|
switch (event.type) {
|
|
2374
2483
|
case "content_block_start":
|
|
@@ -2380,7 +2489,9 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
|
|
|
2380
2489
|
fileContentCache,
|
|
2381
2490
|
client,
|
|
2382
2491
|
logger,
|
|
2383
|
-
parentToolCallId
|
|
2492
|
+
parentToolCallId,
|
|
2493
|
+
registerHooks,
|
|
2494
|
+
supportsTerminalOutput
|
|
2384
2495
|
);
|
|
2385
2496
|
case "content_block_delta":
|
|
2386
2497
|
return toAcpNotifications(
|
|
@@ -2391,7 +2502,9 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
|
|
|
2391
2502
|
fileContentCache,
|
|
2392
2503
|
client,
|
|
2393
2504
|
logger,
|
|
2394
|
-
parentToolCallId
|
|
2505
|
+
parentToolCallId,
|
|
2506
|
+
registerHooks,
|
|
2507
|
+
supportsTerminalOutput
|
|
2395
2508
|
);
|
|
2396
2509
|
case "message_start":
|
|
2397
2510
|
case "message_delta":
|
|
@@ -2450,29 +2563,25 @@ async function handleSystemMessage(message, context) {
|
|
|
2450
2563
|
break;
|
|
2451
2564
|
}
|
|
2452
2565
|
}
|
|
2453
|
-
function handleResultMessage(message
|
|
2454
|
-
const
|
|
2455
|
-
if (session.cancelled) {
|
|
2456
|
-
return {
|
|
2457
|
-
shouldStop: true,
|
|
2458
|
-
stopReason: "cancelled"
|
|
2459
|
-
};
|
|
2460
|
-
}
|
|
2566
|
+
function handleResultMessage(message) {
|
|
2567
|
+
const usage = extractUsageFromResult(message);
|
|
2461
2568
|
switch (message.subtype) {
|
|
2462
2569
|
case "success": {
|
|
2463
2570
|
if (message.result.includes("Please run /login")) {
|
|
2464
2571
|
return {
|
|
2465
2572
|
shouldStop: true,
|
|
2466
|
-
error: import_sdk.RequestError.authRequired()
|
|
2573
|
+
error: import_sdk.RequestError.authRequired(),
|
|
2574
|
+
usage
|
|
2467
2575
|
};
|
|
2468
2576
|
}
|
|
2469
2577
|
if (message.is_error) {
|
|
2470
2578
|
return {
|
|
2471
2579
|
shouldStop: true,
|
|
2472
|
-
error: import_sdk.RequestError.internalError(void 0, message.result)
|
|
2580
|
+
error: import_sdk.RequestError.internalError(void 0, message.result),
|
|
2581
|
+
usage
|
|
2473
2582
|
};
|
|
2474
2583
|
}
|
|
2475
|
-
return { shouldStop: true, stopReason: "end_turn" };
|
|
2584
|
+
return { shouldStop: true, stopReason: "end_turn", usage };
|
|
2476
2585
|
}
|
|
2477
2586
|
case "error_during_execution":
|
|
2478
2587
|
if (message.is_error) {
|
|
@@ -2481,10 +2590,11 @@ function handleResultMessage(message, context) {
|
|
|
2481
2590
|
error: import_sdk.RequestError.internalError(
|
|
2482
2591
|
void 0,
|
|
2483
2592
|
message.errors.join(", ") || message.subtype
|
|
2484
|
-
)
|
|
2593
|
+
),
|
|
2594
|
+
usage
|
|
2485
2595
|
};
|
|
2486
2596
|
}
|
|
2487
|
-
return { shouldStop: true, stopReason: "end_turn" };
|
|
2597
|
+
return { shouldStop: true, stopReason: "end_turn", usage };
|
|
2488
2598
|
case "error_max_budget_usd":
|
|
2489
2599
|
case "error_max_turns":
|
|
2490
2600
|
case "error_max_structured_output_retries":
|
|
@@ -2494,13 +2604,37 @@ function handleResultMessage(message, context) {
|
|
|
2494
2604
|
error: import_sdk.RequestError.internalError(
|
|
2495
2605
|
void 0,
|
|
2496
2606
|
message.errors.join(", ") || message.subtype
|
|
2497
|
-
)
|
|
2607
|
+
),
|
|
2608
|
+
usage
|
|
2498
2609
|
};
|
|
2499
2610
|
}
|
|
2500
|
-
return { shouldStop: true, stopReason: "max_turn_requests" };
|
|
2611
|
+
return { shouldStop: true, stopReason: "max_turn_requests", usage };
|
|
2501
2612
|
default:
|
|
2502
|
-
return { shouldStop: false };
|
|
2613
|
+
return { shouldStop: false, usage };
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
function extractUsageFromResult(message) {
|
|
2617
|
+
const msg = message;
|
|
2618
|
+
const msgUsage = msg.usage;
|
|
2619
|
+
if (!msgUsage) return void 0;
|
|
2620
|
+
const modelUsage = msg.modelUsage;
|
|
2621
|
+
let contextWindowSize;
|
|
2622
|
+
if (modelUsage) {
|
|
2623
|
+
const contextWindows = Object.values(modelUsage).map(
|
|
2624
|
+
(m) => m.contextWindow
|
|
2625
|
+
);
|
|
2626
|
+
if (contextWindows.length > 0) {
|
|
2627
|
+
contextWindowSize = Math.min(...contextWindows);
|
|
2628
|
+
}
|
|
2503
2629
|
}
|
|
2630
|
+
return {
|
|
2631
|
+
inputTokens: msgUsage.input_tokens ?? 0,
|
|
2632
|
+
outputTokens: msgUsage.output_tokens ?? 0,
|
|
2633
|
+
cachedReadTokens: msgUsage.cache_read_input_tokens ?? 0,
|
|
2634
|
+
cachedWriteTokens: msgUsage.cache_creation_input_tokens ?? 0,
|
|
2635
|
+
costUsd: typeof msg.total_cost_usd === "number" ? msg.total_cost_usd : void 0,
|
|
2636
|
+
contextWindowSize
|
|
2637
|
+
};
|
|
2504
2638
|
}
|
|
2505
2639
|
async function handleStreamEvent(message, context) {
|
|
2506
2640
|
const { sessionId, client, toolUseCache, fileContentCache, logger } = context;
|
|
@@ -2512,7 +2646,9 @@ async function handleStreamEvent(message, context) {
|
|
|
2512
2646
|
fileContentCache,
|
|
2513
2647
|
client,
|
|
2514
2648
|
logger,
|
|
2515
|
-
parentToolCallId
|
|
2649
|
+
parentToolCallId,
|
|
2650
|
+
context.registerHooks,
|
|
2651
|
+
context.supportsTerminalOutput
|
|
2516
2652
|
)) {
|
|
2517
2653
|
await client.sessionUpdate(notification);
|
|
2518
2654
|
context.session.notificationHistory.push(notification);
|
|
@@ -2524,14 +2660,15 @@ function hasLocalCommandStdout(content) {
|
|
|
2524
2660
|
function hasLocalCommandStderr(content) {
|
|
2525
2661
|
return typeof content === "string" && content.includes("<local-command-stderr>");
|
|
2526
2662
|
}
|
|
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
2663
|
function isLoginRequiredMessage(message) {
|
|
2531
2664
|
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
2665
|
}
|
|
2666
|
+
function isPlainTextUserMessage(message) {
|
|
2667
|
+
const content = message.message.content;
|
|
2668
|
+
return message.type === "user" && (typeof content === "string" || Array.isArray(content) && content.length === 1 && content[0].type === "text");
|
|
2669
|
+
}
|
|
2533
2670
|
function shouldSkipUserAssistantMessage(message) {
|
|
2534
|
-
return hasLocalCommandStdout(message.message.content) || hasLocalCommandStderr(message.message.content) ||
|
|
2671
|
+
return hasLocalCommandStdout(message.message.content) || hasLocalCommandStderr(message.message.content) || isLoginRequiredMessage(message);
|
|
2535
2672
|
}
|
|
2536
2673
|
function logSpecialMessages(message, logger) {
|
|
2537
2674
|
const content = message.message.content;
|
|
@@ -2552,18 +2689,33 @@ function filterMessageContent(content) {
|
|
|
2552
2689
|
}
|
|
2553
2690
|
async function handleUserAssistantMessage(message, context) {
|
|
2554
2691
|
const { session, sessionId, client, toolUseCache, fileContentCache, logger } = context;
|
|
2555
|
-
if (session.cancelled) {
|
|
2556
|
-
return {};
|
|
2557
|
-
}
|
|
2558
2692
|
if (shouldSkipUserAssistantMessage(message)) {
|
|
2693
|
+
const content2 = message.message.content;
|
|
2694
|
+
if (typeof content2 === "string" && hasLocalCommandStdout(content2) && content2.includes("Context Usage")) {
|
|
2695
|
+
const stripped = content2.replace("<local-command-stdout>", "").replace("</local-command-stdout>", "");
|
|
2696
|
+
for (const notification of toAcpNotifications(
|
|
2697
|
+
stripped,
|
|
2698
|
+
"assistant",
|
|
2699
|
+
sessionId,
|
|
2700
|
+
toolUseCache,
|
|
2701
|
+
fileContentCache,
|
|
2702
|
+
client,
|
|
2703
|
+
logger
|
|
2704
|
+
)) {
|
|
2705
|
+
await client.sessionUpdate(notification);
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2559
2708
|
logSpecialMessages(message, logger);
|
|
2560
2709
|
if (isLoginRequiredMessage(message)) {
|
|
2561
2710
|
return { shouldStop: true, error: import_sdk.RequestError.authRequired() };
|
|
2562
2711
|
}
|
|
2563
2712
|
return {};
|
|
2564
2713
|
}
|
|
2714
|
+
if (isPlainTextUserMessage(message)) {
|
|
2715
|
+
return {};
|
|
2716
|
+
}
|
|
2565
2717
|
const content = message.message.content;
|
|
2566
|
-
const contentToProcess = filterMessageContent(content);
|
|
2718
|
+
const contentToProcess = message.type === "assistant" ? filterMessageContent(content) : content;
|
|
2567
2719
|
const parentToolCallId = "parent_tool_use_id" in message ? message.parent_tool_use_id ?? void 0 : void 0;
|
|
2568
2720
|
for (const notification of toAcpNotifications(
|
|
2569
2721
|
contentToProcess,
|
|
@@ -2573,7 +2725,9 @@ async function handleUserAssistantMessage(message, context) {
|
|
|
2573
2725
|
fileContentCache,
|
|
2574
2726
|
client,
|
|
2575
2727
|
logger,
|
|
2576
|
-
parentToolCallId
|
|
2728
|
+
parentToolCallId,
|
|
2729
|
+
context.registerHooks,
|
|
2730
|
+
context.supportsTerminalOutput
|
|
2577
2731
|
)) {
|
|
2578
2732
|
await client.sessionUpdate(notification);
|
|
2579
2733
|
session.notificationHistory.push(notification);
|
|
@@ -2659,36 +2813,45 @@ function normalizeAskUserQuestionInput(input) {
|
|
|
2659
2813
|
}
|
|
2660
2814
|
|
|
2661
2815
|
// src/execution-mode.ts
|
|
2662
|
-
var
|
|
2816
|
+
var ALLOW_BYPASS = !IS_ROOT;
|
|
2817
|
+
var availableModes = [
|
|
2663
2818
|
{
|
|
2664
2819
|
id: "default",
|
|
2665
|
-
name: "
|
|
2666
|
-
description: "
|
|
2820
|
+
name: "Default",
|
|
2821
|
+
description: "Standard behavior, prompts for dangerous operations"
|
|
2667
2822
|
},
|
|
2668
2823
|
{
|
|
2669
2824
|
id: "acceptEdits",
|
|
2670
2825
|
name: "Accept Edits",
|
|
2671
|
-
description: "
|
|
2826
|
+
description: "Auto-accept file edit operations"
|
|
2672
2827
|
},
|
|
2673
2828
|
{
|
|
2674
2829
|
id: "plan",
|
|
2675
2830
|
name: "Plan Mode",
|
|
2676
|
-
description: "
|
|
2677
|
-
},
|
|
2678
|
-
{
|
|
2679
|
-
id: "bypassPermissions",
|
|
2680
|
-
name: "Bypass Permissions",
|
|
2681
|
-
description: "Skips all permission prompts"
|
|
2831
|
+
description: "Planning mode, no actual tool execution"
|
|
2682
2832
|
}
|
|
2833
|
+
// {
|
|
2834
|
+
// id: "dontAsk",
|
|
2835
|
+
// name: "Don't Ask",
|
|
2836
|
+
// description: "Don't prompt for permissions, deny if not pre-approved",
|
|
2837
|
+
// },
|
|
2683
2838
|
];
|
|
2839
|
+
if (ALLOW_BYPASS) {
|
|
2840
|
+
availableModes.push({
|
|
2841
|
+
id: "bypassPermissions",
|
|
2842
|
+
name: "Bypass Permissions",
|
|
2843
|
+
description: "Bypass all permission checks"
|
|
2844
|
+
});
|
|
2845
|
+
}
|
|
2684
2846
|
var TWIG_EXECUTION_MODES = [
|
|
2685
2847
|
"default",
|
|
2686
2848
|
"acceptEdits",
|
|
2687
2849
|
"plan",
|
|
2850
|
+
// "dontAsk",
|
|
2688
2851
|
"bypassPermissions"
|
|
2689
2852
|
];
|
|
2690
2853
|
function getAvailableModes() {
|
|
2691
|
-
return IS_ROOT ?
|
|
2854
|
+
return IS_ROOT ? availableModes.filter((m) => m.id !== "bypassPermissions") : availableModes;
|
|
2692
2855
|
}
|
|
2693
2856
|
|
|
2694
2857
|
// src/adapters/claude/tools.ts
|
|
@@ -2716,6 +2879,7 @@ var AUTO_ALLOWED_TOOLS = {
|
|
|
2716
2879
|
default: new Set(BASE_ALLOWED_TOOLS),
|
|
2717
2880
|
acceptEdits: /* @__PURE__ */ new Set([...BASE_ALLOWED_TOOLS, ...WRITE_TOOLS]),
|
|
2718
2881
|
plan: new Set(BASE_ALLOWED_TOOLS)
|
|
2882
|
+
// dontAsk: new Set(BASE_ALLOWED_TOOLS),
|
|
2719
2883
|
};
|
|
2720
2884
|
function isToolAllowedForMode(toolName, mode) {
|
|
2721
2885
|
if (mode === "bypassPermissions") {
|
|
@@ -2863,12 +3027,11 @@ async function validatePlanContent(planText, context) {
|
|
|
2863
3027
|
return { valid: true };
|
|
2864
3028
|
}
|
|
2865
3029
|
async function requestPlanApproval(context, updatedInput) {
|
|
2866
|
-
const { client, sessionId, toolUseID
|
|
2867
|
-
const toolInfo = toolInfoFromToolUse(
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
);
|
|
3030
|
+
const { client, sessionId, toolUseID } = context;
|
|
3031
|
+
const toolInfo = toolInfoFromToolUse({
|
|
3032
|
+
name: context.toolName,
|
|
3033
|
+
input: updatedInput
|
|
3034
|
+
});
|
|
2872
3035
|
return await client.requestPermission({
|
|
2873
3036
|
options: buildExitPlanModePermissionOptions(),
|
|
2874
3037
|
sessionId,
|
|
@@ -2887,7 +3050,14 @@ async function applyPlanApproval(response, context, updatedInput) {
|
|
|
2887
3050
|
if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "default" || response.outcome.optionId === "acceptEdits")) {
|
|
2888
3051
|
session.permissionMode = response.outcome.optionId;
|
|
2889
3052
|
await session.query.setPermissionMode(response.outcome.optionId);
|
|
2890
|
-
await context.
|
|
3053
|
+
await context.client.sessionUpdate({
|
|
3054
|
+
sessionId: context.sessionId,
|
|
3055
|
+
update: {
|
|
3056
|
+
sessionUpdate: "current_mode_update",
|
|
3057
|
+
currentModeId: response.outcome.optionId
|
|
3058
|
+
}
|
|
3059
|
+
});
|
|
3060
|
+
await context.updateConfigOption("mode", response.outcome.optionId);
|
|
2891
3061
|
return {
|
|
2892
3062
|
behavior: "allow",
|
|
2893
3063
|
updatedInput,
|
|
@@ -2908,7 +3078,7 @@ async function handleEnterPlanModeTool(context) {
|
|
|
2908
3078
|
const { session, toolInput } = context;
|
|
2909
3079
|
session.permissionMode = "plan";
|
|
2910
3080
|
await session.query.setPermissionMode("plan");
|
|
2911
|
-
await context.
|
|
3081
|
+
await context.updateConfigOption("mode", "plan");
|
|
2912
3082
|
return {
|
|
2913
3083
|
behavior: "allow",
|
|
2914
3084
|
updatedInput: toolInput
|
|
@@ -2926,6 +3096,9 @@ async function handleExitPlanModeTool(context) {
|
|
|
2926
3096
|
return validationResult.error;
|
|
2927
3097
|
}
|
|
2928
3098
|
const response = await requestPlanApproval(context, updatedInput);
|
|
3099
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
3100
|
+
throw new Error("Tool use aborted");
|
|
3101
|
+
}
|
|
2929
3102
|
return await applyPlanApproval(response, context, updatedInput);
|
|
2930
3103
|
}
|
|
2931
3104
|
function buildQuestionOptions(question) {
|
|
@@ -2949,14 +3122,13 @@ async function handleAskUserQuestionTool(context) {
|
|
|
2949
3122
|
interrupt: true
|
|
2950
3123
|
};
|
|
2951
3124
|
}
|
|
2952
|
-
const { client, sessionId, toolUseID, toolInput
|
|
3125
|
+
const { client, sessionId, toolUseID, toolInput } = context;
|
|
2953
3126
|
const firstQuestion = questions[0];
|
|
2954
3127
|
const options = buildQuestionOptions(firstQuestion);
|
|
2955
|
-
const toolInfo = toolInfoFromToolUse(
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
);
|
|
3128
|
+
const toolInfo = toolInfoFromToolUse({
|
|
3129
|
+
name: context.toolName,
|
|
3130
|
+
input: toolInput
|
|
3131
|
+
});
|
|
2960
3132
|
const response = await client.requestPermission({
|
|
2961
3133
|
options,
|
|
2962
3134
|
sessionId,
|
|
@@ -2971,6 +3143,9 @@ async function handleAskUserQuestionTool(context) {
|
|
|
2971
3143
|
}
|
|
2972
3144
|
}
|
|
2973
3145
|
});
|
|
3146
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
3147
|
+
throw new Error("Tool use aborted");
|
|
3148
|
+
}
|
|
2974
3149
|
if (response.outcome?.outcome !== "selected") {
|
|
2975
3150
|
const customMessage = response._meta?.message;
|
|
2976
3151
|
return {
|
|
@@ -3003,14 +3178,9 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
3003
3178
|
toolUseID,
|
|
3004
3179
|
client,
|
|
3005
3180
|
sessionId,
|
|
3006
|
-
fileContentCache,
|
|
3007
3181
|
suggestions
|
|
3008
3182
|
} = context;
|
|
3009
|
-
const toolInfo = toolInfoFromToolUse(
|
|
3010
|
-
{ name: toolName, input: toolInput },
|
|
3011
|
-
fileContentCache,
|
|
3012
|
-
context.logger
|
|
3013
|
-
);
|
|
3183
|
+
const toolInfo = toolInfoFromToolUse({ name: toolName, input: toolInput });
|
|
3014
3184
|
const options = buildPermissionOptions(
|
|
3015
3185
|
toolName,
|
|
3016
3186
|
toolInput,
|
|
@@ -3029,6 +3199,9 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
3029
3199
|
rawInput: toolInput
|
|
3030
3200
|
}
|
|
3031
3201
|
});
|
|
3202
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
3203
|
+
throw new Error("Tool use aborted");
|
|
3204
|
+
}
|
|
3032
3205
|
if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "allow" || response.outcome.optionId === "allow_always")) {
|
|
3033
3206
|
if (response.outcome.optionId === "allow_always") {
|
|
3034
3207
|
return {
|
|
@@ -3105,16 +3278,18 @@ async function canUseTool(context) {
|
|
|
3105
3278
|
var UNSUPPORTED_COMMANDS = [
|
|
3106
3279
|
"context",
|
|
3107
3280
|
"cost",
|
|
3281
|
+
"keybindings-help",
|
|
3108
3282
|
"login",
|
|
3109
3283
|
"logout",
|
|
3110
3284
|
"output-style:new",
|
|
3111
3285
|
"release-notes",
|
|
3112
3286
|
"todos"
|
|
3113
3287
|
];
|
|
3114
|
-
|
|
3115
|
-
const commands = await q.supportedCommands();
|
|
3288
|
+
function getAvailableSlashCommands(commands) {
|
|
3116
3289
|
return commands.map((command) => {
|
|
3117
|
-
const input = command.argumentHint ? {
|
|
3290
|
+
const input = command.argumentHint != null ? {
|
|
3291
|
+
hint: Array.isArray(command.argumentHint) ? command.argumentHint.join(" ") : command.argumentHint
|
|
3292
|
+
} : null;
|
|
3118
3293
|
let name = command.name;
|
|
3119
3294
|
if (command.name.endsWith(" (MCP)")) {
|
|
3120
3295
|
name = `mcp:${name.replace(" (MCP)", "")}`;
|
|
@@ -3212,13 +3387,19 @@ function buildEnvironment() {
|
|
|
3212
3387
|
ENABLE_TOOL_SEARCH: "auto:0"
|
|
3213
3388
|
};
|
|
3214
3389
|
}
|
|
3215
|
-
function buildHooks(userHooks, onModeChange) {
|
|
3390
|
+
function buildHooks(userHooks, onModeChange, settingsManager, logger) {
|
|
3216
3391
|
return {
|
|
3217
3392
|
...userHooks,
|
|
3218
3393
|
PostToolUse: [
|
|
3219
3394
|
...userHooks?.PostToolUse || [],
|
|
3220
3395
|
{
|
|
3221
|
-
hooks: [createPostToolUseHook({ onModeChange })]
|
|
3396
|
+
hooks: [createPostToolUseHook({ onModeChange, logger })]
|
|
3397
|
+
}
|
|
3398
|
+
],
|
|
3399
|
+
PreToolUse: [
|
|
3400
|
+
...userHooks?.PreToolUse || [],
|
|
3401
|
+
{
|
|
3402
|
+
hooks: [createPreToolUseHook(settingsManager, logger)]
|
|
3222
3403
|
}
|
|
3223
3404
|
]
|
|
3224
3405
|
};
|
|
@@ -3312,12 +3493,22 @@ function buildSessionOptions(params) {
|
|
|
3312
3493
|
permissionMode: params.permissionMode,
|
|
3313
3494
|
canUseTool: params.canUseTool,
|
|
3314
3495
|
executable: "node",
|
|
3496
|
+
tools: { type: "preset", preset: "claude_code" },
|
|
3497
|
+
extraArgs: {
|
|
3498
|
+
...params.userProvidedOptions?.extraArgs,
|
|
3499
|
+
"replay-user-messages": ""
|
|
3500
|
+
},
|
|
3315
3501
|
mcpServers: buildMcpServers(
|
|
3316
3502
|
params.userProvidedOptions?.mcpServers,
|
|
3317
3503
|
params.mcpServers
|
|
3318
3504
|
),
|
|
3319
3505
|
env: buildEnvironment(),
|
|
3320
|
-
hooks: buildHooks(
|
|
3506
|
+
hooks: buildHooks(
|
|
3507
|
+
params.userProvidedOptions?.hooks,
|
|
3508
|
+
params.onModeChange,
|
|
3509
|
+
params.settingsManager,
|
|
3510
|
+
params.logger
|
|
3511
|
+
),
|
|
3321
3512
|
abortController: getAbortController(
|
|
3322
3513
|
params.userProvidedOptions?.abortController
|
|
3323
3514
|
),
|
|
@@ -3334,13 +3525,36 @@ function buildSessionOptions(params) {
|
|
|
3334
3525
|
}
|
|
3335
3526
|
if (params.isResume) {
|
|
3336
3527
|
options.resume = params.sessionId;
|
|
3337
|
-
options.forkSession = false;
|
|
3528
|
+
options.forkSession = params.forkSession ?? false;
|
|
3338
3529
|
} else {
|
|
3339
3530
|
options.sessionId = params.sessionId;
|
|
3531
|
+
options.model = DEFAULT_MODEL;
|
|
3340
3532
|
}
|
|
3341
3533
|
if (params.additionalDirectories) {
|
|
3342
3534
|
options.additionalDirectories = params.additionalDirectories;
|
|
3343
3535
|
}
|
|
3536
|
+
if (params.disableBuiltInTools) {
|
|
3537
|
+
const builtInTools = [
|
|
3538
|
+
"Read",
|
|
3539
|
+
"Write",
|
|
3540
|
+
"Edit",
|
|
3541
|
+
"Bash",
|
|
3542
|
+
"Glob",
|
|
3543
|
+
"Grep",
|
|
3544
|
+
"Task",
|
|
3545
|
+
"TodoWrite",
|
|
3546
|
+
"ExitPlanMode",
|
|
3547
|
+
"WebSearch",
|
|
3548
|
+
"WebFetch",
|
|
3549
|
+
"SlashCommand",
|
|
3550
|
+
"Skill",
|
|
3551
|
+
"NotebookEdit"
|
|
3552
|
+
];
|
|
3553
|
+
options.disallowedTools = [
|
|
3554
|
+
...options.disallowedTools ?? [],
|
|
3555
|
+
...builtInTools
|
|
3556
|
+
];
|
|
3557
|
+
}
|
|
3344
3558
|
clearStatsigCache();
|
|
3345
3559
|
return options;
|
|
3346
3560
|
}
|
|
@@ -3353,15 +3567,268 @@ function clearStatsigCache() {
|
|
|
3353
3567
|
});
|
|
3354
3568
|
}
|
|
3355
3569
|
|
|
3570
|
+
// src/adapters/claude/session/settings.ts
|
|
3571
|
+
var fs2 = __toESM(require("fs"), 1);
|
|
3572
|
+
var os3 = __toESM(require("os"), 1);
|
|
3573
|
+
var path3 = __toESM(require("path"), 1);
|
|
3574
|
+
var import_minimatch = require("minimatch");
|
|
3575
|
+
var ACP_TOOL_NAME_PREFIX = "mcp__acp__";
|
|
3576
|
+
var acpToolNames = {
|
|
3577
|
+
read: `${ACP_TOOL_NAME_PREFIX}Read`,
|
|
3578
|
+
edit: `${ACP_TOOL_NAME_PREFIX}Edit`,
|
|
3579
|
+
write: `${ACP_TOOL_NAME_PREFIX}Write`,
|
|
3580
|
+
bash: `${ACP_TOOL_NAME_PREFIX}Bash`
|
|
3581
|
+
};
|
|
3582
|
+
var SHELL_OPERATORS = ["&&", "||", ";", "|", "$(", "`", "\n"];
|
|
3583
|
+
function containsShellOperator(str) {
|
|
3584
|
+
return SHELL_OPERATORS.some((op) => str.includes(op));
|
|
3585
|
+
}
|
|
3586
|
+
var FILE_EDITING_TOOLS = [acpToolNames.edit, acpToolNames.write];
|
|
3587
|
+
var FILE_READING_TOOLS = [acpToolNames.read];
|
|
3588
|
+
var TOOL_ARG_ACCESSORS = {
|
|
3589
|
+
[acpToolNames.read]: (input) => input?.file_path,
|
|
3590
|
+
[acpToolNames.edit]: (input) => input?.file_path,
|
|
3591
|
+
[acpToolNames.write]: (input) => input?.file_path,
|
|
3592
|
+
[acpToolNames.bash]: (input) => input?.command
|
|
3593
|
+
};
|
|
3594
|
+
function parseRule(rule) {
|
|
3595
|
+
const match = rule.match(/^(\w+)(?:\((.+)\))?$/);
|
|
3596
|
+
if (!match) {
|
|
3597
|
+
return { toolName: rule };
|
|
3598
|
+
}
|
|
3599
|
+
const toolName = match[1] ?? rule;
|
|
3600
|
+
const argument = match[2];
|
|
3601
|
+
if (argument?.endsWith(":*")) {
|
|
3602
|
+
return {
|
|
3603
|
+
toolName,
|
|
3604
|
+
argument: argument.slice(0, -2),
|
|
3605
|
+
isWildcard: true
|
|
3606
|
+
};
|
|
3607
|
+
}
|
|
3608
|
+
return { toolName, argument };
|
|
3609
|
+
}
|
|
3610
|
+
function normalizePath(filePath, cwd) {
|
|
3611
|
+
let resolved = filePath;
|
|
3612
|
+
if (resolved.startsWith("~/")) {
|
|
3613
|
+
resolved = path3.join(os3.homedir(), resolved.slice(2));
|
|
3614
|
+
} else if (resolved.startsWith("./")) {
|
|
3615
|
+
resolved = path3.join(cwd, resolved.slice(2));
|
|
3616
|
+
} else if (!path3.isAbsolute(resolved)) {
|
|
3617
|
+
resolved = path3.join(cwd, resolved);
|
|
3618
|
+
}
|
|
3619
|
+
return path3.normalize(resolved).replace(/\\/g, "/");
|
|
3620
|
+
}
|
|
3621
|
+
function matchesGlob(pattern, filePath, cwd) {
|
|
3622
|
+
const normalizedPattern = normalizePath(pattern, cwd);
|
|
3623
|
+
const normalizedPath = normalizePath(filePath, cwd);
|
|
3624
|
+
return (0, import_minimatch.minimatch)(normalizedPath, normalizedPattern, {
|
|
3625
|
+
dot: true,
|
|
3626
|
+
matchBase: false,
|
|
3627
|
+
nocase: process.platform === "win32"
|
|
3628
|
+
});
|
|
3629
|
+
}
|
|
3630
|
+
function matchesRule(rule, toolName, toolInput, cwd) {
|
|
3631
|
+
const ruleAppliesToTool = rule.toolName === "Bash" && toolName === acpToolNames.bash || rule.toolName === "Edit" && FILE_EDITING_TOOLS.includes(toolName) || rule.toolName === "Read" && FILE_READING_TOOLS.includes(toolName);
|
|
3632
|
+
if (!ruleAppliesToTool) {
|
|
3633
|
+
return false;
|
|
3634
|
+
}
|
|
3635
|
+
if (!rule.argument) {
|
|
3636
|
+
return true;
|
|
3637
|
+
}
|
|
3638
|
+
const argAccessor = TOOL_ARG_ACCESSORS[toolName];
|
|
3639
|
+
if (!argAccessor) {
|
|
3640
|
+
return true;
|
|
3641
|
+
}
|
|
3642
|
+
const actualArg = argAccessor(toolInput);
|
|
3643
|
+
if (!actualArg) {
|
|
3644
|
+
return false;
|
|
3645
|
+
}
|
|
3646
|
+
if (toolName === acpToolNames.bash) {
|
|
3647
|
+
if (rule.isWildcard) {
|
|
3648
|
+
if (!actualArg.startsWith(rule.argument)) {
|
|
3649
|
+
return false;
|
|
3650
|
+
}
|
|
3651
|
+
const remainder = actualArg.slice(rule.argument.length);
|
|
3652
|
+
if (containsShellOperator(remainder)) {
|
|
3653
|
+
return false;
|
|
3654
|
+
}
|
|
3655
|
+
return true;
|
|
3656
|
+
}
|
|
3657
|
+
return actualArg === rule.argument;
|
|
3658
|
+
}
|
|
3659
|
+
return matchesGlob(rule.argument, actualArg, cwd);
|
|
3660
|
+
}
|
|
3661
|
+
async function loadSettingsFile(filePath) {
|
|
3662
|
+
if (!filePath) {
|
|
3663
|
+
return {};
|
|
3664
|
+
}
|
|
3665
|
+
try {
|
|
3666
|
+
const content = await fs2.promises.readFile(filePath, "utf-8");
|
|
3667
|
+
return JSON.parse(content);
|
|
3668
|
+
} catch {
|
|
3669
|
+
return {};
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
function getManagedSettingsPath() {
|
|
3673
|
+
switch (process.platform) {
|
|
3674
|
+
case "darwin":
|
|
3675
|
+
return "/Library/Application Support/ClaudeCode/managed-settings.json";
|
|
3676
|
+
case "linux":
|
|
3677
|
+
return "/etc/claude-code/managed-settings.json";
|
|
3678
|
+
case "win32":
|
|
3679
|
+
return "C:\\Program Files\\ClaudeCode\\managed-settings.json";
|
|
3680
|
+
default:
|
|
3681
|
+
return "/etc/claude-code/managed-settings.json";
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
var SettingsManager = class {
|
|
3685
|
+
cwd;
|
|
3686
|
+
userSettings = {};
|
|
3687
|
+
projectSettings = {};
|
|
3688
|
+
localSettings = {};
|
|
3689
|
+
enterpriseSettings = {};
|
|
3690
|
+
mergedSettings = {};
|
|
3691
|
+
initialized = false;
|
|
3692
|
+
constructor(cwd) {
|
|
3693
|
+
this.cwd = cwd;
|
|
3694
|
+
}
|
|
3695
|
+
async initialize() {
|
|
3696
|
+
if (this.initialized) {
|
|
3697
|
+
return;
|
|
3698
|
+
}
|
|
3699
|
+
await this.loadAllSettings();
|
|
3700
|
+
this.initialized = true;
|
|
3701
|
+
}
|
|
3702
|
+
getUserSettingsPath() {
|
|
3703
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || path3.join(os3.homedir(), ".claude");
|
|
3704
|
+
return path3.join(configDir, "settings.json");
|
|
3705
|
+
}
|
|
3706
|
+
getProjectSettingsPath() {
|
|
3707
|
+
return path3.join(this.cwd, ".claude", "settings.json");
|
|
3708
|
+
}
|
|
3709
|
+
getLocalSettingsPath() {
|
|
3710
|
+
return path3.join(this.cwd, ".claude", "settings.local.json");
|
|
3711
|
+
}
|
|
3712
|
+
async loadAllSettings() {
|
|
3713
|
+
const [userSettings, projectSettings, localSettings, enterpriseSettings] = await Promise.all([
|
|
3714
|
+
loadSettingsFile(this.getUserSettingsPath()),
|
|
3715
|
+
loadSettingsFile(this.getProjectSettingsPath()),
|
|
3716
|
+
loadSettingsFile(this.getLocalSettingsPath()),
|
|
3717
|
+
loadSettingsFile(getManagedSettingsPath())
|
|
3718
|
+
]);
|
|
3719
|
+
this.userSettings = userSettings;
|
|
3720
|
+
this.projectSettings = projectSettings;
|
|
3721
|
+
this.localSettings = localSettings;
|
|
3722
|
+
this.enterpriseSettings = enterpriseSettings;
|
|
3723
|
+
this.mergeAllSettings();
|
|
3724
|
+
}
|
|
3725
|
+
mergeAllSettings() {
|
|
3726
|
+
const allSettings = [
|
|
3727
|
+
this.userSettings,
|
|
3728
|
+
this.projectSettings,
|
|
3729
|
+
this.localSettings,
|
|
3730
|
+
this.enterpriseSettings
|
|
3731
|
+
];
|
|
3732
|
+
const permissions = {
|
|
3733
|
+
allow: [],
|
|
3734
|
+
deny: [],
|
|
3735
|
+
ask: []
|
|
3736
|
+
};
|
|
3737
|
+
const merged = { permissions };
|
|
3738
|
+
for (const settings of allSettings) {
|
|
3739
|
+
if (settings.permissions) {
|
|
3740
|
+
if (settings.permissions.allow) {
|
|
3741
|
+
permissions.allow?.push(...settings.permissions.allow);
|
|
3742
|
+
}
|
|
3743
|
+
if (settings.permissions.deny) {
|
|
3744
|
+
permissions.deny?.push(...settings.permissions.deny);
|
|
3745
|
+
}
|
|
3746
|
+
if (settings.permissions.ask) {
|
|
3747
|
+
permissions.ask?.push(...settings.permissions.ask);
|
|
3748
|
+
}
|
|
3749
|
+
if (settings.permissions.additionalDirectories) {
|
|
3750
|
+
permissions.additionalDirectories = [
|
|
3751
|
+
...permissions.additionalDirectories || [],
|
|
3752
|
+
...settings.permissions.additionalDirectories
|
|
3753
|
+
];
|
|
3754
|
+
}
|
|
3755
|
+
if (settings.permissions.defaultMode) {
|
|
3756
|
+
permissions.defaultMode = settings.permissions.defaultMode;
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
if (settings.env) {
|
|
3760
|
+
merged.env = { ...merged.env, ...settings.env };
|
|
3761
|
+
}
|
|
3762
|
+
if (settings.model) {
|
|
3763
|
+
merged.model = settings.model;
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
this.mergedSettings = merged;
|
|
3767
|
+
}
|
|
3768
|
+
checkPermission(toolName, toolInput) {
|
|
3769
|
+
if (!toolName.startsWith(ACP_TOOL_NAME_PREFIX)) {
|
|
3770
|
+
return { decision: "ask" };
|
|
3771
|
+
}
|
|
3772
|
+
const permissions = this.mergedSettings.permissions;
|
|
3773
|
+
if (!permissions) {
|
|
3774
|
+
return { decision: "ask" };
|
|
3775
|
+
}
|
|
3776
|
+
for (const rule of permissions.deny || []) {
|
|
3777
|
+
const parsed = parseRule(rule);
|
|
3778
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
3779
|
+
return { decision: "deny", rule, source: "deny" };
|
|
3780
|
+
}
|
|
3781
|
+
}
|
|
3782
|
+
for (const rule of permissions.allow || []) {
|
|
3783
|
+
const parsed = parseRule(rule);
|
|
3784
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
3785
|
+
return { decision: "allow", rule, source: "allow" };
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
for (const rule of permissions.ask || []) {
|
|
3789
|
+
const parsed = parseRule(rule);
|
|
3790
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
3791
|
+
return { decision: "ask", rule, source: "ask" };
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
return { decision: "ask" };
|
|
3795
|
+
}
|
|
3796
|
+
getSettings() {
|
|
3797
|
+
return this.mergedSettings;
|
|
3798
|
+
}
|
|
3799
|
+
getCwd() {
|
|
3800
|
+
return this.cwd;
|
|
3801
|
+
}
|
|
3802
|
+
async setCwd(cwd) {
|
|
3803
|
+
if (this.cwd === cwd) {
|
|
3804
|
+
return;
|
|
3805
|
+
}
|
|
3806
|
+
this.dispose();
|
|
3807
|
+
this.cwd = cwd;
|
|
3808
|
+
this.initialized = false;
|
|
3809
|
+
await this.initialize();
|
|
3810
|
+
}
|
|
3811
|
+
dispose() {
|
|
3812
|
+
this.initialized = false;
|
|
3813
|
+
}
|
|
3814
|
+
};
|
|
3815
|
+
|
|
3356
3816
|
// src/adapters/claude/claude-agent.ts
|
|
3357
3817
|
var SESSION_VALIDATION_TIMEOUT_MS = 1e4;
|
|
3818
|
+
var MAX_TITLE_LENGTH = 256;
|
|
3819
|
+
function sanitizeTitle(text2) {
|
|
3820
|
+
const sanitized = text2.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
3821
|
+
if (sanitized.length <= MAX_TITLE_LENGTH) {
|
|
3822
|
+
return sanitized;
|
|
3823
|
+
}
|
|
3824
|
+
return `${sanitized.slice(0, MAX_TITLE_LENGTH - 1)}\u2026`;
|
|
3825
|
+
}
|
|
3358
3826
|
var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
3359
3827
|
adapterName = "claude";
|
|
3360
3828
|
toolUseCache;
|
|
3361
3829
|
backgroundTerminals = {};
|
|
3362
3830
|
clientCapabilities;
|
|
3363
3831
|
options;
|
|
3364
|
-
lastSentConfigOptions;
|
|
3365
3832
|
constructor(client, options) {
|
|
3366
3833
|
super(client);
|
|
3367
3834
|
this.options = options;
|
|
@@ -3382,125 +3849,416 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3382
3849
|
sse: true
|
|
3383
3850
|
},
|
|
3384
3851
|
loadSession: true,
|
|
3852
|
+
sessionCapabilities: {
|
|
3853
|
+
list: {},
|
|
3854
|
+
fork: {},
|
|
3855
|
+
resume: {}
|
|
3856
|
+
},
|
|
3385
3857
|
_meta: {
|
|
3386
3858
|
posthog: {
|
|
3387
3859
|
resumeSession: true
|
|
3860
|
+
},
|
|
3861
|
+
claudeCode: {
|
|
3862
|
+
promptQueueing: true
|
|
3863
|
+
}
|
|
3864
|
+
}
|
|
3865
|
+
},
|
|
3866
|
+
agentInfo: {
|
|
3867
|
+
name: package_default.name,
|
|
3868
|
+
title: "Claude Agent",
|
|
3869
|
+
version: package_default.version
|
|
3870
|
+
},
|
|
3871
|
+
authMethods: []
|
|
3872
|
+
};
|
|
3873
|
+
}
|
|
3874
|
+
async newSession(params) {
|
|
3875
|
+
if (fs3.existsSync(path4.resolve(os4.homedir(), ".claude.json.backup")) && !fs3.existsSync(path4.resolve(os4.homedir(), ".claude.json"))) {
|
|
3876
|
+
throw import_sdk2.RequestError.authRequired();
|
|
3877
|
+
}
|
|
3878
|
+
const response = await this.createSession(params, {
|
|
3879
|
+
// Revisit these meta values once we support resume
|
|
3880
|
+
resume: params._meta?.claudeCode?.options?.resume
|
|
3881
|
+
});
|
|
3882
|
+
return response;
|
|
3883
|
+
}
|
|
3884
|
+
async unstable_forkSession(params) {
|
|
3885
|
+
return this.createSession(
|
|
3886
|
+
{
|
|
3887
|
+
cwd: params.cwd,
|
|
3888
|
+
mcpServers: params.mcpServers ?? [],
|
|
3889
|
+
_meta: params._meta
|
|
3890
|
+
},
|
|
3891
|
+
{ resume: params.sessionId, forkSession: true }
|
|
3892
|
+
);
|
|
3893
|
+
}
|
|
3894
|
+
async unstable_resumeSession(params) {
|
|
3895
|
+
const response = await this.createSession(
|
|
3896
|
+
{
|
|
3897
|
+
cwd: params.cwd,
|
|
3898
|
+
mcpServers: params.mcpServers ?? [],
|
|
3899
|
+
_meta: params._meta
|
|
3900
|
+
},
|
|
3901
|
+
{
|
|
3902
|
+
resume: params.sessionId
|
|
3903
|
+
}
|
|
3904
|
+
);
|
|
3905
|
+
return response;
|
|
3906
|
+
}
|
|
3907
|
+
async loadSession(params) {
|
|
3908
|
+
const response = await this.createSession(
|
|
3909
|
+
{
|
|
3910
|
+
cwd: params.cwd,
|
|
3911
|
+
mcpServers: params.mcpServers ?? [],
|
|
3912
|
+
_meta: params._meta
|
|
3913
|
+
},
|
|
3914
|
+
{ resume: params.sessionId, skipBackgroundFetches: true }
|
|
3915
|
+
);
|
|
3916
|
+
await this.replaySessionHistory(params.sessionId);
|
|
3917
|
+
this.deferBackgroundFetches(this.session.query);
|
|
3918
|
+
return {
|
|
3919
|
+
modes: response.modes,
|
|
3920
|
+
models: response.models,
|
|
3921
|
+
configOptions: response.configOptions
|
|
3922
|
+
};
|
|
3923
|
+
}
|
|
3924
|
+
async unstable_listSessions(params) {
|
|
3925
|
+
const sdkSessions = await (0, import_claude_agent_sdk.listSessions)({ dir: params.cwd ?? void 0 });
|
|
3926
|
+
const sessions = [];
|
|
3927
|
+
for (const session of sdkSessions) {
|
|
3928
|
+
if (!session.cwd) continue;
|
|
3929
|
+
sessions.push({
|
|
3930
|
+
sessionId: session.sessionId,
|
|
3931
|
+
cwd: session.cwd,
|
|
3932
|
+
title: sanitizeTitle(session.customTitle || session.summary || ""),
|
|
3933
|
+
updatedAt: new Date(session.lastModified).toISOString()
|
|
3934
|
+
});
|
|
3935
|
+
}
|
|
3936
|
+
return {
|
|
3937
|
+
sessions
|
|
3938
|
+
};
|
|
3939
|
+
}
|
|
3940
|
+
async prompt(params) {
|
|
3941
|
+
this.session.cancelled = false;
|
|
3942
|
+
this.session.interruptReason = void 0;
|
|
3943
|
+
this.session.accumulatedUsage = {
|
|
3944
|
+
inputTokens: 0,
|
|
3945
|
+
outputTokens: 0,
|
|
3946
|
+
cachedReadTokens: 0,
|
|
3947
|
+
cachedWriteTokens: 0
|
|
3948
|
+
};
|
|
3949
|
+
const userMessage = promptToClaude(params);
|
|
3950
|
+
if (this.session.promptRunning) {
|
|
3951
|
+
const uuid = (0, import_node_crypto.randomUUID)();
|
|
3952
|
+
userMessage.uuid = uuid;
|
|
3953
|
+
this.session.input.push(userMessage);
|
|
3954
|
+
const order = this.session.nextPendingOrder++;
|
|
3955
|
+
const cancelled = await new Promise((resolve4) => {
|
|
3956
|
+
this.session.pendingMessages.set(uuid, { resolve: resolve4, order });
|
|
3957
|
+
});
|
|
3958
|
+
if (cancelled) {
|
|
3959
|
+
return { stopReason: "cancelled" };
|
|
3960
|
+
}
|
|
3961
|
+
} else {
|
|
3962
|
+
this.session.input.push(userMessage);
|
|
3963
|
+
}
|
|
3964
|
+
await this.broadcastUserMessage(params);
|
|
3965
|
+
this.session.promptRunning = true;
|
|
3966
|
+
let handedOff = false;
|
|
3967
|
+
let lastAssistantTotalUsage = null;
|
|
3968
|
+
const supportsTerminalOutput = this.clientCapabilities?._meta?.terminal_output === true;
|
|
3969
|
+
const context = {
|
|
3970
|
+
session: this.session,
|
|
3971
|
+
sessionId: params.sessionId,
|
|
3972
|
+
client: this.client,
|
|
3973
|
+
toolUseCache: this.toolUseCache,
|
|
3974
|
+
fileContentCache: this.fileContentCache,
|
|
3975
|
+
logger: this.logger,
|
|
3976
|
+
supportsTerminalOutput
|
|
3977
|
+
};
|
|
3978
|
+
try {
|
|
3979
|
+
while (true) {
|
|
3980
|
+
const { value: message, done } = await this.session.query.next();
|
|
3981
|
+
if (done || !message) {
|
|
3982
|
+
if (this.session.cancelled) {
|
|
3983
|
+
return {
|
|
3984
|
+
stopReason: "cancelled",
|
|
3985
|
+
_meta: this.session.interruptReason ? { interruptReason: this.session.interruptReason } : void 0
|
|
3986
|
+
};
|
|
3987
|
+
}
|
|
3988
|
+
break;
|
|
3989
|
+
}
|
|
3990
|
+
switch (message.type) {
|
|
3991
|
+
case "system":
|
|
3992
|
+
if (message.subtype === "compact_boundary") {
|
|
3993
|
+
lastAssistantTotalUsage = 0;
|
|
3994
|
+
}
|
|
3995
|
+
await handleSystemMessage(message, context);
|
|
3996
|
+
break;
|
|
3997
|
+
case "result": {
|
|
3998
|
+
if (this.session.cancelled) {
|
|
3999
|
+
return { stopReason: "cancelled" };
|
|
4000
|
+
}
|
|
4001
|
+
this.session.accumulatedUsage.inputTokens += message.usage.input_tokens;
|
|
4002
|
+
this.session.accumulatedUsage.outputTokens += message.usage.output_tokens;
|
|
4003
|
+
this.session.accumulatedUsage.cachedReadTokens += message.usage.cache_read_input_tokens;
|
|
4004
|
+
this.session.accumulatedUsage.cachedWriteTokens += message.usage.cache_creation_input_tokens;
|
|
4005
|
+
const contextWindows = Object.values(message.modelUsage).map(
|
|
4006
|
+
(m) => m.contextWindow
|
|
4007
|
+
);
|
|
4008
|
+
const contextWindowSize = contextWindows.length > 0 ? Math.min(...contextWindows) : 2e5;
|
|
4009
|
+
if (lastAssistantTotalUsage !== null) {
|
|
4010
|
+
await this.client.sessionUpdate({
|
|
4011
|
+
sessionId: params.sessionId,
|
|
4012
|
+
update: {
|
|
4013
|
+
sessionUpdate: "usage_update",
|
|
4014
|
+
used: lastAssistantTotalUsage,
|
|
4015
|
+
size: contextWindowSize,
|
|
4016
|
+
cost: {
|
|
4017
|
+
amount: message.total_cost_usd,
|
|
4018
|
+
currency: "USD"
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
});
|
|
4022
|
+
}
|
|
4023
|
+
await this.client.extNotification("_posthog/usage_update", {
|
|
4024
|
+
sessionId: params.sessionId,
|
|
4025
|
+
used: {
|
|
4026
|
+
inputTokens: message.usage.input_tokens,
|
|
4027
|
+
outputTokens: message.usage.output_tokens,
|
|
4028
|
+
cachedReadTokens: message.usage.cache_read_input_tokens,
|
|
4029
|
+
cachedWriteTokens: message.usage.cache_creation_input_tokens
|
|
4030
|
+
},
|
|
4031
|
+
cost: message.total_cost_usd
|
|
4032
|
+
});
|
|
4033
|
+
const usage = {
|
|
4034
|
+
inputTokens: this.session.accumulatedUsage.inputTokens,
|
|
4035
|
+
outputTokens: this.session.accumulatedUsage.outputTokens,
|
|
4036
|
+
cachedReadTokens: this.session.accumulatedUsage.cachedReadTokens,
|
|
4037
|
+
cachedWriteTokens: this.session.accumulatedUsage.cachedWriteTokens,
|
|
4038
|
+
totalTokens: this.session.accumulatedUsage.inputTokens + this.session.accumulatedUsage.outputTokens + this.session.accumulatedUsage.cachedReadTokens + this.session.accumulatedUsage.cachedWriteTokens
|
|
4039
|
+
};
|
|
4040
|
+
const result = handleResultMessage(message);
|
|
4041
|
+
if (result.error) throw result.error;
|
|
4042
|
+
switch (message.subtype) {
|
|
4043
|
+
case "error_max_budget_usd":
|
|
4044
|
+
case "error_max_turns":
|
|
4045
|
+
case "error_max_structured_output_retries":
|
|
4046
|
+
return { stopReason: "max_turn_requests", usage };
|
|
4047
|
+
default:
|
|
4048
|
+
return { stopReason: "end_turn", usage };
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
case "stream_event":
|
|
4052
|
+
await handleStreamEvent(message, context);
|
|
4053
|
+
break;
|
|
4054
|
+
case "user":
|
|
4055
|
+
case "assistant": {
|
|
4056
|
+
if (this.session.cancelled) {
|
|
4057
|
+
break;
|
|
4058
|
+
}
|
|
4059
|
+
if (message.type === "user" && "uuid" in message && message.uuid) {
|
|
4060
|
+
const pending = this.session.pendingMessages.get(
|
|
4061
|
+
message.uuid
|
|
4062
|
+
);
|
|
4063
|
+
if (pending) {
|
|
4064
|
+
pending.resolve(false);
|
|
4065
|
+
this.session.pendingMessages.delete(message.uuid);
|
|
4066
|
+
handedOff = true;
|
|
4067
|
+
return { stopReason: "end_turn" };
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
4070
|
+
if ("usage" in message.message && message.parent_tool_use_id === null) {
|
|
4071
|
+
const usage = message.message.usage;
|
|
4072
|
+
lastAssistantTotalUsage = usage.input_tokens + usage.output_tokens + usage.cache_read_input_tokens + usage.cache_creation_input_tokens;
|
|
4073
|
+
}
|
|
4074
|
+
const result = await handleUserAssistantMessage(message, context);
|
|
4075
|
+
if (result.error) throw result.error;
|
|
4076
|
+
if (result.shouldStop) {
|
|
4077
|
+
return { stopReason: "end_turn" };
|
|
4078
|
+
}
|
|
4079
|
+
break;
|
|
3388
4080
|
}
|
|
4081
|
+
case "tool_progress":
|
|
4082
|
+
case "auth_status":
|
|
4083
|
+
case "tool_use_summary":
|
|
4084
|
+
break;
|
|
4085
|
+
default:
|
|
4086
|
+
unreachable(message, this.logger);
|
|
4087
|
+
break;
|
|
3389
4088
|
}
|
|
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"
|
|
4089
|
+
}
|
|
4090
|
+
throw new Error("Session did not end in result");
|
|
4091
|
+
} finally {
|
|
4092
|
+
if (!handedOff) {
|
|
4093
|
+
this.session.promptRunning = false;
|
|
4094
|
+
for (const [key, pending] of this.session.pendingMessages) {
|
|
4095
|
+
pending.resolve(true);
|
|
4096
|
+
this.session.pendingMessages.delete(key);
|
|
3401
4097
|
}
|
|
3402
|
-
|
|
3403
|
-
}
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
3404
4100
|
}
|
|
3405
|
-
|
|
3406
|
-
|
|
4101
|
+
// Called by BaseAcpAgent#cancel() to interrupt the session
|
|
4102
|
+
async interrupt() {
|
|
4103
|
+
this.session.cancelled = true;
|
|
4104
|
+
for (const [, pending] of this.session.pendingMessages) {
|
|
4105
|
+
pending.resolve(true);
|
|
4106
|
+
}
|
|
4107
|
+
this.session.pendingMessages.clear();
|
|
4108
|
+
await this.session.query.interrupt();
|
|
3407
4109
|
}
|
|
3408
|
-
async
|
|
3409
|
-
|
|
4110
|
+
async unstable_setSessionModel(params) {
|
|
4111
|
+
const sdkModelId = toSdkModelId(params.modelId);
|
|
4112
|
+
await this.session.query.setModel(sdkModelId);
|
|
4113
|
+
this.session.modelId = params.modelId;
|
|
4114
|
+
await this.updateConfigOption("model", params.modelId);
|
|
4115
|
+
return {};
|
|
4116
|
+
}
|
|
4117
|
+
async setSessionMode(params) {
|
|
4118
|
+
await this.applySessionMode(params.modeId);
|
|
4119
|
+
await this.updateConfigOption("mode", params.modeId);
|
|
4120
|
+
return {};
|
|
4121
|
+
}
|
|
4122
|
+
async setSessionConfigOption(params) {
|
|
4123
|
+
const option = this.session.configOptions.find(
|
|
4124
|
+
(o) => o.id === params.configId
|
|
4125
|
+
);
|
|
4126
|
+
if (!option) {
|
|
4127
|
+
throw new Error(`Unknown config option: ${params.configId}`);
|
|
4128
|
+
}
|
|
4129
|
+
const allValues = "options" in option && Array.isArray(option.options) ? option.options.flatMap(
|
|
4130
|
+
(o) => "options" in o && Array.isArray(o.options) ? o.options : [o]
|
|
4131
|
+
) : [];
|
|
4132
|
+
const validValue = allValues.find((o) => o.value === params.value);
|
|
4133
|
+
if (!validValue) {
|
|
4134
|
+
throw new Error(
|
|
4135
|
+
`Invalid value for config option ${params.configId}: ${params.value}`
|
|
4136
|
+
);
|
|
4137
|
+
}
|
|
4138
|
+
if (params.configId === "mode") {
|
|
4139
|
+
await this.applySessionMode(params.value);
|
|
4140
|
+
await this.client.sessionUpdate({
|
|
4141
|
+
sessionId: this.sessionId,
|
|
4142
|
+
update: {
|
|
4143
|
+
sessionUpdate: "current_mode_update",
|
|
4144
|
+
currentModeId: params.value
|
|
4145
|
+
}
|
|
4146
|
+
});
|
|
4147
|
+
} else if (params.configId === "model") {
|
|
4148
|
+
const sdkModelId = toSdkModelId(params.value);
|
|
4149
|
+
await this.session.query.setModel(sdkModelId);
|
|
4150
|
+
this.session.modelId = params.value;
|
|
4151
|
+
}
|
|
4152
|
+
this.session.configOptions = this.session.configOptions.map(
|
|
4153
|
+
(o) => o.id === params.configId ? { ...o, currentValue: params.value } : o
|
|
4154
|
+
);
|
|
4155
|
+
return { configOptions: this.session.configOptions };
|
|
4156
|
+
}
|
|
4157
|
+
async updateConfigOption(configId, value) {
|
|
4158
|
+
this.session.configOptions = this.session.configOptions.map(
|
|
4159
|
+
(o) => o.id === configId ? { ...o, currentValue: value } : o
|
|
4160
|
+
);
|
|
4161
|
+
await this.client.sessionUpdate({
|
|
4162
|
+
sessionId: this.sessionId,
|
|
4163
|
+
update: {
|
|
4164
|
+
sessionUpdate: "config_option_update",
|
|
4165
|
+
configOptions: this.session.configOptions
|
|
4166
|
+
}
|
|
4167
|
+
});
|
|
4168
|
+
}
|
|
4169
|
+
async applySessionMode(modeId) {
|
|
4170
|
+
if (!TWIG_EXECUTION_MODES.includes(modeId)) {
|
|
4171
|
+
throw new Error("Invalid Mode");
|
|
4172
|
+
}
|
|
4173
|
+
const previousMode = this.session.permissionMode;
|
|
4174
|
+
this.session.permissionMode = modeId;
|
|
4175
|
+
try {
|
|
4176
|
+
await this.session.query.setPermissionMode(modeId);
|
|
4177
|
+
} catch (error) {
|
|
4178
|
+
this.session.permissionMode = previousMode;
|
|
4179
|
+
if (error instanceof Error) {
|
|
4180
|
+
if (!error.message) {
|
|
4181
|
+
error.message = "Invalid Mode";
|
|
4182
|
+
}
|
|
4183
|
+
throw error;
|
|
4184
|
+
}
|
|
4185
|
+
throw new Error("Invalid Mode");
|
|
4186
|
+
}
|
|
4187
|
+
}
|
|
4188
|
+
async createSession(params, creationOpts = {}) {
|
|
4189
|
+
const { cwd } = params;
|
|
4190
|
+
const { resume, forkSession } = creationOpts;
|
|
4191
|
+
const isResume = !!resume;
|
|
3410
4192
|
const meta = params._meta;
|
|
3411
4193
|
const taskId = meta?.persistence?.taskId;
|
|
3412
|
-
|
|
3413
|
-
|
|
4194
|
+
let sessionId;
|
|
4195
|
+
if (forkSession) {
|
|
4196
|
+
sessionId = (0, import_uuid.v7)();
|
|
4197
|
+
} else if (isResume) {
|
|
4198
|
+
sessionId = resume;
|
|
4199
|
+
} else {
|
|
4200
|
+
sessionId = (0, import_uuid.v7)();
|
|
4201
|
+
}
|
|
4202
|
+
const input = new Pushable();
|
|
4203
|
+
const settingsManager = new SettingsManager(cwd);
|
|
4204
|
+
await settingsManager.initialize();
|
|
4205
|
+
const mcpServers = parseMcpServers(params);
|
|
4206
|
+
const systemPrompt = buildSystemPrompt(meta?.systemPrompt);
|
|
4207
|
+
this.logger.info(isResume ? "Resuming session" : "Creating new session", {
|
|
3414
4208
|
sessionId,
|
|
3415
4209
|
taskId,
|
|
3416
4210
|
taskRunId: meta?.taskRunId,
|
|
3417
|
-
cwd
|
|
4211
|
+
cwd
|
|
3418
4212
|
});
|
|
3419
4213
|
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
3420
|
-
const mcpServers = parseMcpServers(params);
|
|
3421
4214
|
const options = buildSessionOptions({
|
|
3422
|
-
cwd
|
|
4215
|
+
cwd,
|
|
3423
4216
|
mcpServers,
|
|
3424
4217
|
permissionMode,
|
|
3425
4218
|
canUseTool: this.createCanUseTool(sessionId),
|
|
3426
4219
|
logger: this.logger,
|
|
3427
|
-
systemPrompt
|
|
4220
|
+
systemPrompt,
|
|
3428
4221
|
userProvidedOptions: meta?.claudeCode?.options,
|
|
3429
4222
|
sessionId,
|
|
3430
|
-
isResume
|
|
3431
|
-
|
|
4223
|
+
isResume,
|
|
4224
|
+
forkSession,
|
|
4225
|
+
additionalDirectories: meta?.claudeCode?.options?.additionalDirectories,
|
|
4226
|
+
disableBuiltInTools: meta?.disableBuiltInTools,
|
|
4227
|
+
settingsManager,
|
|
4228
|
+
onModeChange: this.createOnModeChange(),
|
|
3432
4229
|
onProcessSpawned: this.options?.onProcessSpawned,
|
|
3433
4230
|
onProcessExited: this.options?.onProcessExited
|
|
3434
4231
|
});
|
|
3435
|
-
const
|
|
3436
|
-
options.model = DEFAULT_MODEL;
|
|
4232
|
+
const abortController = options.abortController;
|
|
3437
4233
|
const q = (0, import_claude_agent_sdk.query)({ prompt: input, options });
|
|
3438
|
-
const session =
|
|
3439
|
-
|
|
3440
|
-
q,
|
|
4234
|
+
const session = {
|
|
4235
|
+
query: q,
|
|
3441
4236
|
input,
|
|
4237
|
+
cancelled: false,
|
|
4238
|
+
settingsManager,
|
|
3442
4239
|
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,
|
|
4240
|
+
abortController,
|
|
4241
|
+
accumulatedUsage: {
|
|
4242
|
+
inputTokens: 0,
|
|
4243
|
+
outputTokens: 0,
|
|
4244
|
+
cachedReadTokens: 0,
|
|
4245
|
+
cachedWriteTokens: 0
|
|
4246
|
+
},
|
|
4247
|
+
configOptions: [],
|
|
4248
|
+
promptRunning: false,
|
|
4249
|
+
pendingMessages: /* @__PURE__ */ new Map(),
|
|
4250
|
+
nextPendingOrder: 0,
|
|
4251
|
+
// Custom properties
|
|
4252
|
+
cwd,
|
|
4253
|
+
notificationHistory: [],
|
|
3501
4254
|
taskRunId: meta?.taskRunId
|
|
3502
|
-
}
|
|
3503
|
-
session
|
|
4255
|
+
};
|
|
4256
|
+
this.session = session;
|
|
4257
|
+
this.sessionId = sessionId;
|
|
4258
|
+
this.logger.info(
|
|
4259
|
+
isResume ? "Session query initialized, awaiting resumption" : "Session query initialized, awaiting initialization",
|
|
4260
|
+
{ sessionId, taskId, taskRunId: meta?.taskRunId }
|
|
4261
|
+
);
|
|
3504
4262
|
try {
|
|
3505
4263
|
const result = await withTimeout(
|
|
3506
4264
|
q.initializationResult(),
|
|
@@ -3508,231 +4266,181 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3508
4266
|
);
|
|
3509
4267
|
if (result.result === "timeout") {
|
|
3510
4268
|
throw new Error(
|
|
3511
|
-
`Session resumption timed out for sessionId=${sessionId}`
|
|
4269
|
+
`Session ${isResume ? forkSession ? "fork" : "resumption" : "initialization"} timed out for sessionId=${sessionId}`
|
|
3512
4270
|
);
|
|
3513
4271
|
}
|
|
3514
4272
|
} catch (err) {
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
4273
|
+
settingsManager.dispose();
|
|
4274
|
+
this.logger.error(
|
|
4275
|
+
isResume ? forkSession ? "Session fork failed" : "Session resumption failed" : "Session initialization failed",
|
|
4276
|
+
{
|
|
4277
|
+
sessionId,
|
|
4278
|
+
taskId,
|
|
4279
|
+
taskRunId: meta?.taskRunId,
|
|
4280
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4281
|
+
}
|
|
4282
|
+
);
|
|
3521
4283
|
throw err;
|
|
3522
4284
|
}
|
|
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
|
-
};
|
|
4285
|
+
if (meta?.taskRunId) {
|
|
4286
|
+
await this.client.extNotification("_posthog/sdk_session", {
|
|
4287
|
+
taskRunId: meta.taskRunId,
|
|
4288
|
+
sessionId,
|
|
4289
|
+
adapter: "claude"
|
|
4290
|
+
});
|
|
3571
4291
|
}
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
4292
|
+
const settingsModel = settingsManager.getSettings().model;
|
|
4293
|
+
const modelOptions = await this.getModelConfigOptions();
|
|
4294
|
+
const resolvedModelId = settingsModel || modelOptions.currentModelId;
|
|
4295
|
+
session.modelId = resolvedModelId;
|
|
4296
|
+
if (!isResume) {
|
|
4297
|
+
const resolvedSdkModel = toSdkModelId(resolvedModelId);
|
|
4298
|
+
if (resolvedSdkModel !== DEFAULT_MODEL) {
|
|
4299
|
+
await this.session.query.setModel(resolvedSdkModel);
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
const availableModes2 = getAvailableModes();
|
|
4303
|
+
const modes = {
|
|
4304
|
+
currentModeId: permissionMode,
|
|
4305
|
+
availableModes: availableModes2.map((mode) => ({
|
|
4306
|
+
id: mode.id,
|
|
4307
|
+
name: mode.name,
|
|
4308
|
+
description: mode.description ?? void 0
|
|
4309
|
+
}))
|
|
4310
|
+
};
|
|
4311
|
+
const models = {
|
|
4312
|
+
currentModelId: resolvedModelId,
|
|
4313
|
+
availableModels: modelOptions.options.map(
|
|
4314
|
+
(opt) => ({
|
|
4315
|
+
modelId: opt.value,
|
|
4316
|
+
name: opt.name,
|
|
4317
|
+
description: opt.description
|
|
4318
|
+
})
|
|
4319
|
+
)
|
|
3583
4320
|
};
|
|
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
|
|
4321
|
+
const configOptions = this.buildConfigOptions(permissionMode, modelOptions);
|
|
4322
|
+
session.configOptions = configOptions;
|
|
4323
|
+
if (!creationOpts.skipBackgroundFetches) {
|
|
4324
|
+
this.deferBackgroundFetches(q);
|
|
4325
|
+
}
|
|
4326
|
+
this.logger.info(
|
|
4327
|
+
isResume ? "Session resumed successfully" : "Session created successfully",
|
|
4328
|
+
{
|
|
4329
|
+
sessionId,
|
|
4330
|
+
taskId,
|
|
4331
|
+
taskRunId: meta?.taskRunId
|
|
4332
|
+
}
|
|
3614
4333
|
);
|
|
3615
|
-
return {
|
|
4334
|
+
return { sessionId, modes, models, configOptions };
|
|
3616
4335
|
}
|
|
3617
4336
|
createCanUseTool(sessionId) {
|
|
3618
|
-
return async (toolName, toolInput, { suggestions, toolUseID }) => canUseTool({
|
|
4337
|
+
return async (toolName, toolInput, { suggestions, toolUseID, signal }) => canUseTool({
|
|
3619
4338
|
session: this.session,
|
|
3620
4339
|
toolName,
|
|
3621
4340
|
toolInput,
|
|
3622
4341
|
toolUseID,
|
|
3623
4342
|
suggestions,
|
|
4343
|
+
signal,
|
|
3624
4344
|
client: this.client,
|
|
3625
4345
|
sessionId,
|
|
3626
4346
|
fileContentCache: this.fileContentCache,
|
|
3627
4347
|
logger: this.logger,
|
|
3628
|
-
|
|
4348
|
+
updateConfigOption: (configId, value) => this.updateConfigOption(configId, value)
|
|
3629
4349
|
});
|
|
3630
4350
|
}
|
|
3631
|
-
createOnModeChange(
|
|
4351
|
+
createOnModeChange() {
|
|
3632
4352
|
return async (newMode) => {
|
|
3633
4353
|
if (this.session) {
|
|
3634
4354
|
this.session.permissionMode = newMode;
|
|
3635
4355
|
}
|
|
3636
|
-
await this.
|
|
4356
|
+
await this.updateConfigOption("mode", newMode);
|
|
3637
4357
|
};
|
|
3638
4358
|
}
|
|
3639
|
-
|
|
3640
|
-
const options = [];
|
|
4359
|
+
buildConfigOptions(currentModeId, modelOptions) {
|
|
3641
4360
|
const modeOptions = getAvailableModes().map((mode) => ({
|
|
3642
4361
|
value: mode.id,
|
|
3643
4362
|
name: mode.name,
|
|
3644
4363
|
description: mode.description ?? void 0
|
|
3645
4364
|
}));
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
return options;
|
|
4365
|
+
return [
|
|
4366
|
+
{
|
|
4367
|
+
id: "mode",
|
|
4368
|
+
name: "Approval Preset",
|
|
4369
|
+
type: "select",
|
|
4370
|
+
currentValue: currentModeId,
|
|
4371
|
+
options: modeOptions,
|
|
4372
|
+
category: "mode",
|
|
4373
|
+
description: "Choose an approval and sandboxing preset for your session"
|
|
4374
|
+
},
|
|
4375
|
+
{
|
|
4376
|
+
id: "model",
|
|
4377
|
+
name: "Model",
|
|
4378
|
+
type: "select",
|
|
4379
|
+
currentValue: modelOptions.currentModelId,
|
|
4380
|
+
options: modelOptions.options,
|
|
4381
|
+
category: "model",
|
|
4382
|
+
description: "Choose which model Claude should use"
|
|
4383
|
+
}
|
|
4384
|
+
];
|
|
3667
4385
|
}
|
|
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;
|
|
4386
|
+
async sendAvailableCommandsUpdate() {
|
|
4387
|
+
const commands = await this.session.query.supportedCommands();
|
|
3675
4388
|
await this.client.sessionUpdate({
|
|
3676
|
-
sessionId:
|
|
4389
|
+
sessionId: this.sessionId,
|
|
3677
4390
|
update: {
|
|
3678
|
-
sessionUpdate: "
|
|
3679
|
-
|
|
4391
|
+
sessionUpdate: "available_commands_update",
|
|
4392
|
+
availableCommands: getAvailableSlashCommands(commands)
|
|
3680
4393
|
}
|
|
3681
4394
|
});
|
|
3682
4395
|
}
|
|
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) {
|
|
3695
|
-
try {
|
|
3696
|
-
await this.setModelWithFallback(q, modelId);
|
|
3697
|
-
} catch (err) {
|
|
3698
|
-
this.logger.warn("Failed to set model", { modelId, error: err });
|
|
3699
|
-
}
|
|
3700
|
-
}
|
|
3701
|
-
async setModelWithFallback(q, modelId) {
|
|
3702
|
-
const sdkModelId = toSdkModelId(modelId);
|
|
4396
|
+
async replaySessionHistory(sessionId) {
|
|
3703
4397
|
try {
|
|
3704
|
-
await
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
4398
|
+
const messages = await (0, import_claude_agent_sdk.getSessionMessages)(sessionId, {
|
|
4399
|
+
dir: this.session.cwd
|
|
4400
|
+
});
|
|
4401
|
+
const replayContext = {
|
|
4402
|
+
session: this.session,
|
|
4403
|
+
sessionId,
|
|
4404
|
+
client: this.client,
|
|
4405
|
+
toolUseCache: this.toolUseCache,
|
|
4406
|
+
fileContentCache: this.fileContentCache,
|
|
4407
|
+
logger: this.logger,
|
|
4408
|
+
registerHooks: false
|
|
4409
|
+
};
|
|
4410
|
+
for (const msg of messages) {
|
|
4411
|
+
const sdkMessage = {
|
|
4412
|
+
type: msg.type,
|
|
4413
|
+
message: msg.message,
|
|
4414
|
+
parent_tool_use_id: msg.parent_tool_use_id
|
|
4415
|
+
};
|
|
4416
|
+
await handleUserAssistantMessage(
|
|
4417
|
+
sdkMessage,
|
|
4418
|
+
replayContext
|
|
4419
|
+
);
|
|
3708
4420
|
}
|
|
3709
|
-
|
|
4421
|
+
} catch (err) {
|
|
4422
|
+
this.logger.warn("Failed to replay session history", {
|
|
4423
|
+
sessionId,
|
|
4424
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4425
|
+
});
|
|
3710
4426
|
}
|
|
3711
4427
|
}
|
|
4428
|
+
// ================================
|
|
4429
|
+
// EXTENSION METHODS
|
|
4430
|
+
// ================================
|
|
3712
4431
|
/**
|
|
3713
4432
|
* Fire-and-forget: fetch slash commands and MCP tool metadata in parallel.
|
|
3714
4433
|
* Both populate caches used later — neither is needed to return configOptions.
|
|
3715
4434
|
*/
|
|
3716
|
-
deferBackgroundFetches(q
|
|
4435
|
+
deferBackgroundFetches(q) {
|
|
3717
4436
|
Promise.all([
|
|
3718
|
-
|
|
4437
|
+
new Promise((resolve4) => setTimeout(resolve4, 10)).then(
|
|
4438
|
+
() => this.sendAvailableCommandsUpdate()
|
|
4439
|
+
),
|
|
3719
4440
|
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);
|
|
4441
|
+
]).catch(
|
|
4442
|
+
(err) => this.logger.error("Background fetch failed", { error: err })
|
|
4443
|
+
);
|
|
3736
4444
|
}
|
|
3737
4445
|
async broadcastUserMessage(params) {
|
|
3738
4446
|
for (const chunk of params.prompt) {
|
|
@@ -3747,71 +4455,6 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3747
4455
|
this.appendNotification(params.sessionId, notification);
|
|
3748
4456
|
}
|
|
3749
4457
|
}
|
|
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
4458
|
};
|
|
3816
4459
|
|
|
3817
4460
|
// src/adapters/codex/spawn.ts
|
|
@@ -4451,7 +5094,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4451
5094
|
lastFlushAttemptTime = /* @__PURE__ */ new Map();
|
|
4452
5095
|
retryCounts = /* @__PURE__ */ new Map();
|
|
4453
5096
|
sessions = /* @__PURE__ */ new Map();
|
|
4454
|
-
messageCounts = /* @__PURE__ */ new Map();
|
|
4455
5097
|
logger;
|
|
4456
5098
|
localCachePath;
|
|
4457
5099
|
constructor(options = {}) {
|
|
@@ -4461,19 +5103,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4461
5103
|
}
|
|
4462
5104
|
async flushAll() {
|
|
4463
5105
|
const sessionIds = [...this.sessions.keys()];
|
|
4464
|
-
const pendingCounts = sessionIds.map((id) => {
|
|
4465
|
-
const session = this.sessions.get(id);
|
|
4466
|
-
return {
|
|
4467
|
-
taskId: session?.context.taskId,
|
|
4468
|
-
runId: session?.context.runId,
|
|
4469
|
-
pending: this.pendingEntries.get(id)?.length ?? 0,
|
|
4470
|
-
messages: this.messageCounts.get(id) ?? 0
|
|
4471
|
-
};
|
|
4472
|
-
});
|
|
4473
|
-
this.logger.info("flushAll called", {
|
|
4474
|
-
sessions: sessionIds.length,
|
|
4475
|
-
pending: pendingCounts
|
|
4476
|
-
});
|
|
4477
5106
|
const flushPromises = [];
|
|
4478
5107
|
for (const sessionId of sessionIds) {
|
|
4479
5108
|
flushPromises.push(this.flush(sessionId));
|
|
@@ -4517,15 +5146,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4517
5146
|
});
|
|
4518
5147
|
return;
|
|
4519
5148
|
}
|
|
4520
|
-
const count = (this.messageCounts.get(sessionId) ?? 0) + 1;
|
|
4521
|
-
this.messageCounts.set(sessionId, count);
|
|
4522
|
-
if (count % 10 === 1) {
|
|
4523
|
-
this.logger.info("Messages received", {
|
|
4524
|
-
count,
|
|
4525
|
-
taskId: session.context.taskId,
|
|
4526
|
-
runId: session.context.runId
|
|
4527
|
-
});
|
|
4528
|
-
}
|
|
4529
5149
|
try {
|
|
4530
5150
|
const message = JSON.parse(line);
|
|
4531
5151
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4574,12 +5194,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4574
5194
|
this.emitCoalescedMessage(sessionId, session);
|
|
4575
5195
|
const pending = this.pendingEntries.get(sessionId);
|
|
4576
5196
|
if (!this.posthogAPI || !pending?.length) {
|
|
4577
|
-
this.logger.info("flush: nothing to persist", {
|
|
4578
|
-
taskId: session.context.taskId,
|
|
4579
|
-
runId: session.context.runId,
|
|
4580
|
-
hasPosthogAPI: !!this.posthogAPI,
|
|
4581
|
-
pendingCount: pending?.length ?? 0
|
|
4582
|
-
});
|
|
4583
5197
|
return;
|
|
4584
5198
|
}
|
|
4585
5199
|
this.pendingEntries.delete(sessionId);
|
|
@@ -4596,11 +5210,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4596
5210
|
pending
|
|
4597
5211
|
);
|
|
4598
5212
|
this.retryCounts.set(sessionId, 0);
|
|
4599
|
-
this.logger.info("Flushed session logs", {
|
|
4600
|
-
taskId: session.context.taskId,
|
|
4601
|
-
runId: session.context.runId,
|
|
4602
|
-
entryCount: pending.length
|
|
4603
|
-
});
|
|
4604
5213
|
} catch (error) {
|
|
4605
5214
|
const retryCount = (this.retryCounts.get(sessionId) ?? 0) + 1;
|
|
4606
5215
|
this.retryCounts.set(sessionId, retryCount);
|
|
@@ -4735,8 +5344,8 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4735
5344
|
};
|
|
4736
5345
|
|
|
4737
5346
|
// ../git/dist/queries.js
|
|
4738
|
-
var
|
|
4739
|
-
var
|
|
5347
|
+
var fs6 = __toESM(require("fs/promises"), 1);
|
|
5348
|
+
var path7 = __toESM(require("path"), 1);
|
|
4740
5349
|
|
|
4741
5350
|
// ../../node_modules/simple-git/dist/esm/index.js
|
|
4742
5351
|
var import_node_buffer = require("buffer");
|
|
@@ -4775,8 +5384,8 @@ function pathspec(...paths) {
|
|
|
4775
5384
|
cache.set(key, paths);
|
|
4776
5385
|
return key;
|
|
4777
5386
|
}
|
|
4778
|
-
function isPathSpec(
|
|
4779
|
-
return
|
|
5387
|
+
function isPathSpec(path9) {
|
|
5388
|
+
return path9 instanceof String && cache.has(path9);
|
|
4780
5389
|
}
|
|
4781
5390
|
function toPaths(pathSpec) {
|
|
4782
5391
|
return cache.get(pathSpec) || [];
|
|
@@ -4865,8 +5474,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
4865
5474
|
function forEachLineWithContent(input, callback) {
|
|
4866
5475
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
4867
5476
|
}
|
|
4868
|
-
function folderExists(
|
|
4869
|
-
return (0, import_file_exists.exists)(
|
|
5477
|
+
function folderExists(path9) {
|
|
5478
|
+
return (0, import_file_exists.exists)(path9, import_file_exists.FOLDER);
|
|
4870
5479
|
}
|
|
4871
5480
|
function append(target, item) {
|
|
4872
5481
|
if (Array.isArray(target)) {
|
|
@@ -5270,8 +5879,8 @@ function checkIsRepoRootTask() {
|
|
|
5270
5879
|
commands,
|
|
5271
5880
|
format: "utf-8",
|
|
5272
5881
|
onError,
|
|
5273
|
-
parser(
|
|
5274
|
-
return /^\.(git)?$/.test(
|
|
5882
|
+
parser(path9) {
|
|
5883
|
+
return /^\.(git)?$/.test(path9.trim());
|
|
5275
5884
|
}
|
|
5276
5885
|
};
|
|
5277
5886
|
}
|
|
@@ -5705,11 +6314,11 @@ function parseGrep(grep) {
|
|
|
5705
6314
|
const paths = /* @__PURE__ */ new Set();
|
|
5706
6315
|
const results = {};
|
|
5707
6316
|
forEachLineWithContent(grep, (input) => {
|
|
5708
|
-
const [
|
|
5709
|
-
paths.add(
|
|
5710
|
-
(results[
|
|
6317
|
+
const [path9, line, preview] = input.split(NULL);
|
|
6318
|
+
paths.add(path9);
|
|
6319
|
+
(results[path9] = results[path9] || []).push({
|
|
5711
6320
|
line: asNumber(line),
|
|
5712
|
-
path:
|
|
6321
|
+
path: path9,
|
|
5713
6322
|
preview
|
|
5714
6323
|
});
|
|
5715
6324
|
});
|
|
@@ -6474,14 +7083,14 @@ var init_hash_object = __esm({
|
|
|
6474
7083
|
init_task();
|
|
6475
7084
|
}
|
|
6476
7085
|
});
|
|
6477
|
-
function parseInit(bare,
|
|
7086
|
+
function parseInit(bare, path9, text2) {
|
|
6478
7087
|
const response = String(text2).trim();
|
|
6479
7088
|
let result;
|
|
6480
7089
|
if (result = initResponseRegex.exec(response)) {
|
|
6481
|
-
return new InitSummary(bare,
|
|
7090
|
+
return new InitSummary(bare, path9, false, result[1]);
|
|
6482
7091
|
}
|
|
6483
7092
|
if (result = reInitResponseRegex.exec(response)) {
|
|
6484
|
-
return new InitSummary(bare,
|
|
7093
|
+
return new InitSummary(bare, path9, true, result[1]);
|
|
6485
7094
|
}
|
|
6486
7095
|
let gitDir = "";
|
|
6487
7096
|
const tokens = response.split(" ");
|
|
@@ -6492,7 +7101,7 @@ function parseInit(bare, path8, text2) {
|
|
|
6492
7101
|
break;
|
|
6493
7102
|
}
|
|
6494
7103
|
}
|
|
6495
|
-
return new InitSummary(bare,
|
|
7104
|
+
return new InitSummary(bare, path9, /^re/i.test(response), gitDir);
|
|
6496
7105
|
}
|
|
6497
7106
|
var InitSummary;
|
|
6498
7107
|
var initResponseRegex;
|
|
@@ -6501,9 +7110,9 @@ var init_InitSummary = __esm({
|
|
|
6501
7110
|
"src/lib/responses/InitSummary.ts"() {
|
|
6502
7111
|
"use strict";
|
|
6503
7112
|
InitSummary = class {
|
|
6504
|
-
constructor(bare,
|
|
7113
|
+
constructor(bare, path9, existing, gitDir) {
|
|
6505
7114
|
this.bare = bare;
|
|
6506
|
-
this.path =
|
|
7115
|
+
this.path = path9;
|
|
6507
7116
|
this.existing = existing;
|
|
6508
7117
|
this.gitDir = gitDir;
|
|
6509
7118
|
}
|
|
@@ -6515,7 +7124,7 @@ var init_InitSummary = __esm({
|
|
|
6515
7124
|
function hasBareCommand(command) {
|
|
6516
7125
|
return command.includes(bareCommand);
|
|
6517
7126
|
}
|
|
6518
|
-
function initTask(bare = false,
|
|
7127
|
+
function initTask(bare = false, path9, customArgs) {
|
|
6519
7128
|
const commands = ["init", ...customArgs];
|
|
6520
7129
|
if (bare && !hasBareCommand(commands)) {
|
|
6521
7130
|
commands.splice(1, 0, bareCommand);
|
|
@@ -6524,7 +7133,7 @@ function initTask(bare = false, path8, customArgs) {
|
|
|
6524
7133
|
commands,
|
|
6525
7134
|
format: "utf-8",
|
|
6526
7135
|
parser(text2) {
|
|
6527
|
-
return parseInit(commands.includes("--bare"),
|
|
7136
|
+
return parseInit(commands.includes("--bare"), path9, text2);
|
|
6528
7137
|
}
|
|
6529
7138
|
};
|
|
6530
7139
|
}
|
|
@@ -7340,12 +7949,12 @@ var init_FileStatusSummary = __esm({
|
|
|
7340
7949
|
"use strict";
|
|
7341
7950
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
7342
7951
|
FileStatusSummary = class {
|
|
7343
|
-
constructor(
|
|
7344
|
-
this.path =
|
|
7952
|
+
constructor(path9, index, working_dir) {
|
|
7953
|
+
this.path = path9;
|
|
7345
7954
|
this.index = index;
|
|
7346
7955
|
this.working_dir = working_dir;
|
|
7347
7956
|
if (index === "R" || working_dir === "R") {
|
|
7348
|
-
const detail = fromPathRegex.exec(
|
|
7957
|
+
const detail = fromPathRegex.exec(path9) || [null, path9, path9];
|
|
7349
7958
|
this.from = detail[2] || "";
|
|
7350
7959
|
this.path = detail[1] || "";
|
|
7351
7960
|
}
|
|
@@ -7376,14 +7985,14 @@ function splitLine(result, lineStr) {
|
|
|
7376
7985
|
default:
|
|
7377
7986
|
return;
|
|
7378
7987
|
}
|
|
7379
|
-
function data(index, workingDir,
|
|
7988
|
+
function data(index, workingDir, path9) {
|
|
7380
7989
|
const raw = `${index}${workingDir}`;
|
|
7381
7990
|
const handler = parsers6.get(raw);
|
|
7382
7991
|
if (handler) {
|
|
7383
|
-
handler(result,
|
|
7992
|
+
handler(result, path9);
|
|
7384
7993
|
}
|
|
7385
7994
|
if (raw !== "##" && raw !== "!!") {
|
|
7386
|
-
result.files.push(new FileStatusSummary(
|
|
7995
|
+
result.files.push(new FileStatusSummary(path9, index, workingDir));
|
|
7387
7996
|
}
|
|
7388
7997
|
}
|
|
7389
7998
|
}
|
|
@@ -7696,9 +8305,9 @@ var init_simple_git_api = __esm({
|
|
|
7696
8305
|
next
|
|
7697
8306
|
);
|
|
7698
8307
|
}
|
|
7699
|
-
hashObject(
|
|
8308
|
+
hashObject(path9, write) {
|
|
7700
8309
|
return this._runTask(
|
|
7701
|
-
hashObjectTask(
|
|
8310
|
+
hashObjectTask(path9, write === true),
|
|
7702
8311
|
trailingFunctionArgument(arguments)
|
|
7703
8312
|
);
|
|
7704
8313
|
}
|
|
@@ -8051,8 +8660,8 @@ var init_branch = __esm({
|
|
|
8051
8660
|
}
|
|
8052
8661
|
});
|
|
8053
8662
|
function toPath(input) {
|
|
8054
|
-
const
|
|
8055
|
-
return
|
|
8663
|
+
const path9 = input.trim().replace(/^["']|["']$/g, "");
|
|
8664
|
+
return path9 && (0, import_node_path3.normalize)(path9);
|
|
8056
8665
|
}
|
|
8057
8666
|
var parseCheckIgnore;
|
|
8058
8667
|
var init_CheckIgnore = __esm({
|
|
@@ -8366,8 +8975,8 @@ __export(sub_module_exports, {
|
|
|
8366
8975
|
subModuleTask: () => subModuleTask,
|
|
8367
8976
|
updateSubModuleTask: () => updateSubModuleTask
|
|
8368
8977
|
});
|
|
8369
|
-
function addSubModuleTask(repo,
|
|
8370
|
-
return subModuleTask(["add", repo,
|
|
8978
|
+
function addSubModuleTask(repo, path9) {
|
|
8979
|
+
return subModuleTask(["add", repo, path9]);
|
|
8371
8980
|
}
|
|
8372
8981
|
function initSubModuleTask(customArgs) {
|
|
8373
8982
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -8697,8 +9306,8 @@ var require_git = __commonJS2({
|
|
|
8697
9306
|
}
|
|
8698
9307
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
8699
9308
|
};
|
|
8700
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
8701
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
9309
|
+
Git2.prototype.submoduleAdd = function(repo, path9, then) {
|
|
9310
|
+
return this._runTask(addSubModuleTask2(repo, path9), trailingFunctionArgument2(arguments));
|
|
8702
9311
|
};
|
|
8703
9312
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
8704
9313
|
return this._runTask(
|
|
@@ -9623,8 +10232,8 @@ var Saga = class {
|
|
|
9623
10232
|
|
|
9624
10233
|
// ../git/dist/sagas/tree.js
|
|
9625
10234
|
var import_node_fs3 = require("fs");
|
|
9626
|
-
var
|
|
9627
|
-
var
|
|
10235
|
+
var fs7 = __toESM(require("fs/promises"), 1);
|
|
10236
|
+
var path8 = __toESM(require("path"), 1);
|
|
9628
10237
|
var tar = __toESM(require("tar"), 1);
|
|
9629
10238
|
|
|
9630
10239
|
// ../git/dist/git-saga.js
|
|
@@ -9650,14 +10259,14 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9650
10259
|
tempIndexPath = null;
|
|
9651
10260
|
async executeGitOperations(input) {
|
|
9652
10261
|
const { baseDir, lastTreeHash, archivePath, signal } = input;
|
|
9653
|
-
const tmpDir =
|
|
10262
|
+
const tmpDir = path8.join(baseDir, ".git", "twig-tmp");
|
|
9654
10263
|
await this.step({
|
|
9655
10264
|
name: "create_tmp_dir",
|
|
9656
|
-
execute: () =>
|
|
10265
|
+
execute: () => fs7.mkdir(tmpDir, { recursive: true }),
|
|
9657
10266
|
rollback: async () => {
|
|
9658
10267
|
}
|
|
9659
10268
|
});
|
|
9660
|
-
this.tempIndexPath =
|
|
10269
|
+
this.tempIndexPath = path8.join(tmpDir, `index-${Date.now()}`);
|
|
9661
10270
|
const tempIndexGit = this.git.env({
|
|
9662
10271
|
...process.env,
|
|
9663
10272
|
GIT_INDEX_FILE: this.tempIndexPath
|
|
@@ -9667,7 +10276,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9667
10276
|
execute: () => tempIndexGit.raw(["read-tree", "HEAD"]),
|
|
9668
10277
|
rollback: async () => {
|
|
9669
10278
|
if (this.tempIndexPath) {
|
|
9670
|
-
await
|
|
10279
|
+
await fs7.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9671
10280
|
});
|
|
9672
10281
|
}
|
|
9673
10282
|
}
|
|
@@ -9676,7 +10285,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9676
10285
|
const treeHash = await this.readOnlyStep("write_tree", () => tempIndexGit.raw(["write-tree"]));
|
|
9677
10286
|
if (lastTreeHash && treeHash === lastTreeHash) {
|
|
9678
10287
|
this.log.debug("No changes since last capture", { treeHash });
|
|
9679
|
-
await
|
|
10288
|
+
await fs7.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9680
10289
|
});
|
|
9681
10290
|
return { snapshot: null, changed: false };
|
|
9682
10291
|
}
|
|
@@ -9688,7 +10297,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9688
10297
|
}
|
|
9689
10298
|
});
|
|
9690
10299
|
const changes = await this.readOnlyStep("get_changes", () => this.getChanges(this.git, baseCommit, treeHash));
|
|
9691
|
-
await
|
|
10300
|
+
await fs7.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9692
10301
|
});
|
|
9693
10302
|
const snapshot = {
|
|
9694
10303
|
treeHash,
|
|
@@ -9712,15 +10321,15 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9712
10321
|
if (filesToArchive.length === 0) {
|
|
9713
10322
|
return void 0;
|
|
9714
10323
|
}
|
|
9715
|
-
const existingFiles = filesToArchive.filter((f) => (0, import_node_fs3.existsSync)(
|
|
10324
|
+
const existingFiles = filesToArchive.filter((f) => (0, import_node_fs3.existsSync)(path8.join(baseDir, f)));
|
|
9716
10325
|
if (existingFiles.length === 0) {
|
|
9717
10326
|
return void 0;
|
|
9718
10327
|
}
|
|
9719
10328
|
await this.step({
|
|
9720
10329
|
name: "create_archive",
|
|
9721
10330
|
execute: async () => {
|
|
9722
|
-
const archiveDir =
|
|
9723
|
-
await
|
|
10331
|
+
const archiveDir = path8.dirname(archivePath);
|
|
10332
|
+
await fs7.mkdir(archiveDir, { recursive: true });
|
|
9724
10333
|
await tar.create({
|
|
9725
10334
|
gzip: true,
|
|
9726
10335
|
file: archivePath,
|
|
@@ -9728,7 +10337,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9728
10337
|
}, existingFiles);
|
|
9729
10338
|
},
|
|
9730
10339
|
rollback: async () => {
|
|
9731
|
-
await
|
|
10340
|
+
await fs7.rm(archivePath, { force: true }).catch(() => {
|
|
9732
10341
|
});
|
|
9733
10342
|
}
|
|
9734
10343
|
});
|
|
@@ -9827,9 +10436,9 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9827
10436
|
const filesToExtract = changes.filter((c) => c.status !== "D").map((c) => c.path);
|
|
9828
10437
|
await this.readOnlyStep("backup_existing_files", async () => {
|
|
9829
10438
|
for (const filePath of filesToExtract) {
|
|
9830
|
-
const fullPath =
|
|
10439
|
+
const fullPath = path8.join(baseDir, filePath);
|
|
9831
10440
|
try {
|
|
9832
|
-
const content = await
|
|
10441
|
+
const content = await fs7.readFile(fullPath);
|
|
9833
10442
|
this.fileBackups.set(filePath, content);
|
|
9834
10443
|
} catch {
|
|
9835
10444
|
}
|
|
@@ -9846,16 +10455,16 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9846
10455
|
},
|
|
9847
10456
|
rollback: async () => {
|
|
9848
10457
|
for (const filePath of this.extractedFiles) {
|
|
9849
|
-
const fullPath =
|
|
10458
|
+
const fullPath = path8.join(baseDir, filePath);
|
|
9850
10459
|
const backup = this.fileBackups.get(filePath);
|
|
9851
10460
|
if (backup) {
|
|
9852
|
-
const dir =
|
|
9853
|
-
await
|
|
10461
|
+
const dir = path8.dirname(fullPath);
|
|
10462
|
+
await fs7.mkdir(dir, { recursive: true }).catch(() => {
|
|
9854
10463
|
});
|
|
9855
|
-
await
|
|
10464
|
+
await fs7.writeFile(fullPath, backup).catch(() => {
|
|
9856
10465
|
});
|
|
9857
10466
|
} else {
|
|
9858
|
-
await
|
|
10467
|
+
await fs7.rm(fullPath, { force: true }).catch(() => {
|
|
9859
10468
|
});
|
|
9860
10469
|
}
|
|
9861
10470
|
}
|
|
@@ -9863,10 +10472,10 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9863
10472
|
});
|
|
9864
10473
|
}
|
|
9865
10474
|
for (const change of changes.filter((c) => c.status === "D")) {
|
|
9866
|
-
const fullPath =
|
|
10475
|
+
const fullPath = path8.join(baseDir, change.path);
|
|
9867
10476
|
const backupContent = await this.readOnlyStep(`backup_${change.path}`, async () => {
|
|
9868
10477
|
try {
|
|
9869
|
-
return await
|
|
10478
|
+
return await fs7.readFile(fullPath);
|
|
9870
10479
|
} catch {
|
|
9871
10480
|
return null;
|
|
9872
10481
|
}
|
|
@@ -9874,15 +10483,15 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9874
10483
|
await this.step({
|
|
9875
10484
|
name: `delete_${change.path}`,
|
|
9876
10485
|
execute: async () => {
|
|
9877
|
-
await
|
|
10486
|
+
await fs7.rm(fullPath, { force: true });
|
|
9878
10487
|
this.log.debug(`Deleted file: ${change.path}`);
|
|
9879
10488
|
},
|
|
9880
10489
|
rollback: async () => {
|
|
9881
10490
|
if (backupContent) {
|
|
9882
|
-
const dir =
|
|
9883
|
-
await
|
|
10491
|
+
const dir = path8.dirname(fullPath);
|
|
10492
|
+
await fs7.mkdir(dir, { recursive: true }).catch(() => {
|
|
9884
10493
|
});
|
|
9885
|
-
await
|
|
10494
|
+
await fs7.writeFile(fullPath, backupContent).catch(() => {
|
|
9886
10495
|
});
|
|
9887
10496
|
}
|
|
9888
10497
|
}
|