@posthog/agent 2.1.131 → 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 -606
- 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 +1300 -655
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +1278 -635
- 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/utils/common.ts +1 -1
|
@@ -513,7 +513,7 @@ var require_has_flag = __commonJS({
|
|
|
513
513
|
var require_supports_color = __commonJS({
|
|
514
514
|
"../../node_modules/supports-color/index.js"(exports, module) {
|
|
515
515
|
"use strict";
|
|
516
|
-
var
|
|
516
|
+
var os5 = __require("os");
|
|
517
517
|
var tty = __require("tty");
|
|
518
518
|
var hasFlag = require_has_flag();
|
|
519
519
|
var { env } = process;
|
|
@@ -561,7 +561,7 @@ var require_supports_color = __commonJS({
|
|
|
561
561
|
return min;
|
|
562
562
|
}
|
|
563
563
|
if (process.platform === "win32") {
|
|
564
|
-
const osRelease =
|
|
564
|
+
const osRelease = os5.release().split(".");
|
|
565
565
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
566
566
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
567
567
|
}
|
|
@@ -809,10 +809,10 @@ var require_src2 = __commonJS({
|
|
|
809
809
|
var fs_1 = __require("fs");
|
|
810
810
|
var debug_1 = __importDefault(require_src());
|
|
811
811
|
var log = debug_1.default("@kwsites/file-exists");
|
|
812
|
-
function check(
|
|
813
|
-
log(`checking %s`,
|
|
812
|
+
function check(path9, isFile, isDirectory) {
|
|
813
|
+
log(`checking %s`, path9);
|
|
814
814
|
try {
|
|
815
|
-
const stat = fs_1.statSync(
|
|
815
|
+
const stat = fs_1.statSync(path9);
|
|
816
816
|
if (stat.isFile() && isFile) {
|
|
817
817
|
log(`[OK] path represents a file`);
|
|
818
818
|
return true;
|
|
@@ -832,8 +832,8 @@ var require_src2 = __commonJS({
|
|
|
832
832
|
throw e;
|
|
833
833
|
}
|
|
834
834
|
}
|
|
835
|
-
function exists2(
|
|
836
|
-
return check(
|
|
835
|
+
function exists2(path9, type = exports.READABLE) {
|
|
836
|
+
return check(path9, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
|
|
837
837
|
}
|
|
838
838
|
exports.exists = exists2;
|
|
839
839
|
exports.FILE = 1;
|
|
@@ -908,7 +908,7 @@ import { Hono } from "hono";
|
|
|
908
908
|
// package.json
|
|
909
909
|
var package_default = {
|
|
910
910
|
name: "@posthog/agent",
|
|
911
|
-
version: "2.1.
|
|
911
|
+
version: "2.1.137",
|
|
912
912
|
repository: "https://github.com/PostHog/twig",
|
|
913
913
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
914
914
|
exports: {
|
|
@@ -984,7 +984,6 @@ var package_default = {
|
|
|
984
984
|
"@twig/git": "workspace:*",
|
|
985
985
|
"@types/bun": "latest",
|
|
986
986
|
"@types/tar": "^6.1.13",
|
|
987
|
-
minimatch: "^10.0.3",
|
|
988
987
|
msw: "^2.12.7",
|
|
989
988
|
tsup: "^8.5.1",
|
|
990
989
|
tsx: "^4.20.6",
|
|
@@ -1005,6 +1004,7 @@ var package_default = {
|
|
|
1005
1004
|
commander: "^14.0.2",
|
|
1006
1005
|
hono: "^4.11.7",
|
|
1007
1006
|
jsonwebtoken: "^9.0.2",
|
|
1007
|
+
minimatch: "^10.0.3",
|
|
1008
1008
|
tar: "^7.5.0",
|
|
1009
1009
|
uuid: "13.0.0",
|
|
1010
1010
|
"yoga-wasm-web": "^0.3.3",
|
|
@@ -1285,13 +1285,16 @@ function nodeWritableToWebWritable(nodeStream) {
|
|
|
1285
1285
|
}
|
|
1286
1286
|
|
|
1287
1287
|
// src/adapters/claude/claude-agent.ts
|
|
1288
|
-
import
|
|
1289
|
-
import * as
|
|
1290
|
-
import * as
|
|
1288
|
+
import { randomUUID } from "crypto";
|
|
1289
|
+
import * as fs3 from "fs";
|
|
1290
|
+
import * as os4 from "os";
|
|
1291
|
+
import * as path4 from "path";
|
|
1291
1292
|
import {
|
|
1292
1293
|
RequestError as RequestError2
|
|
1293
1294
|
} from "@agentclientprotocol/sdk";
|
|
1294
1295
|
import {
|
|
1296
|
+
getSessionMessages,
|
|
1297
|
+
listSessions,
|
|
1295
1298
|
query
|
|
1296
1299
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
1297
1300
|
import { v7 as uuidv7 } from "uuid";
|
|
@@ -1313,7 +1316,7 @@ function unreachable(value, logger) {
|
|
|
1313
1316
|
try {
|
|
1314
1317
|
valueAsString = JSON.stringify(value);
|
|
1315
1318
|
} catch {
|
|
1316
|
-
valueAsString = value;
|
|
1319
|
+
valueAsString = String(value);
|
|
1317
1320
|
}
|
|
1318
1321
|
logger.error(`Unexpected case: ${valueAsString}`);
|
|
1319
1322
|
}
|
|
@@ -1385,19 +1388,20 @@ var BaseAcpAgent = class {
|
|
|
1385
1388
|
}
|
|
1386
1389
|
async cancel(params) {
|
|
1387
1390
|
if (this.sessionId !== params.sessionId) {
|
|
1388
|
-
throw new Error("Session
|
|
1391
|
+
throw new Error("Session ID mismatch");
|
|
1389
1392
|
}
|
|
1390
1393
|
this.session.cancelled = true;
|
|
1391
1394
|
const meta = params._meta;
|
|
1392
1395
|
if (meta?.interruptReason) {
|
|
1393
1396
|
this.session.interruptReason = meta.interruptReason;
|
|
1394
1397
|
}
|
|
1395
|
-
await this.
|
|
1398
|
+
await this.interrupt();
|
|
1396
1399
|
}
|
|
1397
1400
|
async closeSession() {
|
|
1398
1401
|
try {
|
|
1399
1402
|
this.session.abortController.abort();
|
|
1400
1403
|
await this.cancel({ sessionId: this.sessionId });
|
|
1404
|
+
this.session.settingsManager.dispose();
|
|
1401
1405
|
this.logger.info("Closed session", { sessionId: this.sessionId });
|
|
1402
1406
|
} catch (err) {
|
|
1403
1407
|
this.logger.warn("Failed to close session", {
|
|
@@ -1572,8 +1576,8 @@ var ToolContentBuilder = class {
|
|
|
1572
1576
|
this.items.push({ type: "content", content: image(data, mimeType, uri) });
|
|
1573
1577
|
return this;
|
|
1574
1578
|
}
|
|
1575
|
-
diff(
|
|
1576
|
-
this.items.push({ type: "diff", path:
|
|
1579
|
+
diff(path9, oldText, newText) {
|
|
1580
|
+
this.items.push({ type: "diff", path: path9, oldText, newText });
|
|
1577
1581
|
return this;
|
|
1578
1582
|
}
|
|
1579
1583
|
build() {
|
|
@@ -1593,7 +1597,7 @@ var registerHookCallback = (toolUseID, {
|
|
|
1593
1597
|
onPostToolUseHook
|
|
1594
1598
|
};
|
|
1595
1599
|
};
|
|
1596
|
-
var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
|
|
1600
|
+
var createPostToolUseHook = ({ onModeChange, logger }) => async (input, toolUseID) => {
|
|
1597
1601
|
if (input.hook_event_name === "PostToolUse") {
|
|
1598
1602
|
const toolName = input.tool_name;
|
|
1599
1603
|
if (onModeChange && toolName === "EnterPlanMode") {
|
|
@@ -1608,11 +1612,54 @@ var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
|
|
|
1608
1612
|
input.tool_response
|
|
1609
1613
|
);
|
|
1610
1614
|
delete toolUseCallbacks[toolUseID];
|
|
1615
|
+
} else {
|
|
1616
|
+
logger?.error(
|
|
1617
|
+
`No onPostToolUseHook found for tool use ID: ${toolUseID}`
|
|
1618
|
+
);
|
|
1619
|
+
delete toolUseCallbacks[toolUseID];
|
|
1611
1620
|
}
|
|
1612
1621
|
}
|
|
1613
1622
|
}
|
|
1614
1623
|
return { continue: true };
|
|
1615
1624
|
};
|
|
1625
|
+
var createPreToolUseHook = (settingsManager, logger) => async (input, _toolUseID) => {
|
|
1626
|
+
if (input.hook_event_name !== "PreToolUse") {
|
|
1627
|
+
return { continue: true };
|
|
1628
|
+
}
|
|
1629
|
+
const toolName = input.tool_name;
|
|
1630
|
+
const toolInput = input.tool_input;
|
|
1631
|
+
const permissionCheck = settingsManager.checkPermission(
|
|
1632
|
+
toolName,
|
|
1633
|
+
toolInput
|
|
1634
|
+
);
|
|
1635
|
+
if (permissionCheck.decision !== "ask") {
|
|
1636
|
+
logger.info(
|
|
1637
|
+
`[PreToolUseHook] Tool: ${toolName}, Decision: ${permissionCheck.decision}, Rule: ${permissionCheck.rule}`
|
|
1638
|
+
);
|
|
1639
|
+
}
|
|
1640
|
+
switch (permissionCheck.decision) {
|
|
1641
|
+
case "allow":
|
|
1642
|
+
return {
|
|
1643
|
+
continue: true,
|
|
1644
|
+
hookSpecificOutput: {
|
|
1645
|
+
hookEventName: "PreToolUse",
|
|
1646
|
+
permissionDecision: "allow",
|
|
1647
|
+
permissionDecisionReason: `Allowed by settings rule: ${permissionCheck.rule}`
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
case "deny":
|
|
1651
|
+
return {
|
|
1652
|
+
continue: true,
|
|
1653
|
+
hookSpecificOutput: {
|
|
1654
|
+
hookEventName: "PreToolUse",
|
|
1655
|
+
permissionDecision: "deny",
|
|
1656
|
+
permissionDecisionReason: `Denied by settings rule: ${permissionCheck.rule}`
|
|
1657
|
+
}
|
|
1658
|
+
};
|
|
1659
|
+
default:
|
|
1660
|
+
return { continue: true };
|
|
1661
|
+
}
|
|
1662
|
+
};
|
|
1616
1663
|
|
|
1617
1664
|
// src/adapters/claude/mcp/tool-metadata.ts
|
|
1618
1665
|
var mcpToolMetadataCache = /* @__PURE__ */ new Map();
|
|
@@ -1689,79 +1736,7 @@ var SYSTEM_REMINDER = `
|
|
|
1689
1736
|
<system-reminder>
|
|
1690
1737
|
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.
|
|
1691
1738
|
</system-reminder>`;
|
|
1692
|
-
function
|
|
1693
|
-
let currentContent = fileContent;
|
|
1694
|
-
const randomHex = Array.from(crypto.getRandomValues(new Uint8Array(5))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1695
|
-
const markerPrefix = `__REPLACE_MARKER_${randomHex}_`;
|
|
1696
|
-
let markerCounter = 0;
|
|
1697
|
-
const markers = [];
|
|
1698
|
-
for (const edit of edits) {
|
|
1699
|
-
if (edit.oldText === "") {
|
|
1700
|
-
throw new Error(
|
|
1701
|
-
`The provided \`old_string\` is empty.
|
|
1702
|
-
|
|
1703
|
-
No edits were applied.`
|
|
1704
|
-
);
|
|
1705
|
-
}
|
|
1706
|
-
if (edit.replaceAll) {
|
|
1707
|
-
const parts = [];
|
|
1708
|
-
let lastIndex = 0;
|
|
1709
|
-
let searchIndex = 0;
|
|
1710
|
-
while (true) {
|
|
1711
|
-
const index = currentContent.indexOf(edit.oldText, searchIndex);
|
|
1712
|
-
if (index === -1) {
|
|
1713
|
-
if (searchIndex === 0) {
|
|
1714
|
-
throw new Error(
|
|
1715
|
-
`The provided \`old_string\` does not appear in the file: "${edit.oldText}".
|
|
1716
|
-
|
|
1717
|
-
No edits were applied.`
|
|
1718
|
-
);
|
|
1719
|
-
}
|
|
1720
|
-
break;
|
|
1721
|
-
}
|
|
1722
|
-
parts.push(currentContent.substring(lastIndex, index));
|
|
1723
|
-
const marker = `${markerPrefix}${markerCounter++}__`;
|
|
1724
|
-
markers.push(marker);
|
|
1725
|
-
parts.push(marker + edit.newText);
|
|
1726
|
-
lastIndex = index + edit.oldText.length;
|
|
1727
|
-
searchIndex = lastIndex;
|
|
1728
|
-
}
|
|
1729
|
-
parts.push(currentContent.substring(lastIndex));
|
|
1730
|
-
currentContent = parts.join("");
|
|
1731
|
-
} else {
|
|
1732
|
-
const index = currentContent.indexOf(edit.oldText);
|
|
1733
|
-
if (index === -1) {
|
|
1734
|
-
throw new Error(
|
|
1735
|
-
`The provided \`old_string\` does not appear in the file: "${edit.oldText}".
|
|
1736
|
-
|
|
1737
|
-
No edits were applied.`
|
|
1738
|
-
);
|
|
1739
|
-
} else {
|
|
1740
|
-
const marker = `${markerPrefix}${markerCounter++}__`;
|
|
1741
|
-
markers.push(marker);
|
|
1742
|
-
currentContent = currentContent.substring(0, index) + marker + edit.newText + currentContent.substring(index + edit.oldText.length);
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
}
|
|
1746
|
-
const lineNumbers = [];
|
|
1747
|
-
for (const marker of markers) {
|
|
1748
|
-
const index = currentContent.indexOf(marker);
|
|
1749
|
-
if (index !== -1) {
|
|
1750
|
-
const lineNumber = Math.max(
|
|
1751
|
-
0,
|
|
1752
|
-
currentContent.substring(0, index).split(/\r\n|\r|\n/).length - 1
|
|
1753
|
-
);
|
|
1754
|
-
lineNumbers.push(lineNumber);
|
|
1755
|
-
}
|
|
1756
|
-
}
|
|
1757
|
-
let finalContent = currentContent;
|
|
1758
|
-
for (const marker of markers) {
|
|
1759
|
-
finalContent = finalContent.replace(marker, "");
|
|
1760
|
-
}
|
|
1761
|
-
const uniqueLineNumbers = [...new Set(lineNumbers)].sort();
|
|
1762
|
-
return { newContent: finalContent, lineNumbers: uniqueLineNumbers };
|
|
1763
|
-
}
|
|
1764
|
-
function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ debug: false, prefix: "[ClaudeTools]" })) {
|
|
1739
|
+
function toolInfoFromToolUse(toolUse, options) {
|
|
1765
1740
|
const name = toolUse.name;
|
|
1766
1741
|
const input = toolUse.input;
|
|
1767
1742
|
switch (name) {
|
|
@@ -1786,6 +1761,13 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1786
1761
|
locations: input?.notebook_path ? [{ path: String(input.notebook_path) }] : []
|
|
1787
1762
|
};
|
|
1788
1763
|
case "Bash":
|
|
1764
|
+
if (options?.supportsTerminalOutput && options?.toolUseId) {
|
|
1765
|
+
return {
|
|
1766
|
+
title: input?.description ? String(input.description) : "Execute command",
|
|
1767
|
+
kind: "execute",
|
|
1768
|
+
content: [{ type: "terminal", terminalId: options.toolUseId }]
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1789
1771
|
return {
|
|
1790
1772
|
title: input?.description ? String(input.description) : "Execute command",
|
|
1791
1773
|
kind: "execute",
|
|
@@ -1806,11 +1788,11 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1806
1788
|
case "Read": {
|
|
1807
1789
|
let limit = "";
|
|
1808
1790
|
const inputLimit = input?.limit;
|
|
1809
|
-
const inputOffset = input?.offset ??
|
|
1791
|
+
const inputOffset = input?.offset ?? 1;
|
|
1810
1792
|
if (inputLimit) {
|
|
1811
|
-
limit = ` (${inputOffset
|
|
1812
|
-
} else if (inputOffset) {
|
|
1813
|
-
limit = ` (from line ${inputOffset
|
|
1793
|
+
limit = ` (${inputOffset} - ${inputOffset + inputLimit - 1})`;
|
|
1794
|
+
} else if (inputOffset > 1) {
|
|
1795
|
+
limit = ` (from line ${inputOffset})`;
|
|
1814
1796
|
}
|
|
1815
1797
|
return {
|
|
1816
1798
|
title: `Read ${input?.file_path ? String(input.file_path) : "File"}${limit}`,
|
|
@@ -1832,39 +1814,21 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1832
1814
|
locations: []
|
|
1833
1815
|
};
|
|
1834
1816
|
case "Edit": {
|
|
1835
|
-
const
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
let affectedLines = [];
|
|
1839
|
-
if (path8 && oldText) {
|
|
1840
|
-
try {
|
|
1841
|
-
const oldContent = cachedFileContent[path8] || "";
|
|
1842
|
-
const newContent = replaceAndCalculateLocation(oldContent, [
|
|
1843
|
-
{
|
|
1844
|
-
oldText,
|
|
1845
|
-
newText,
|
|
1846
|
-
replaceAll: false
|
|
1847
|
-
}
|
|
1848
|
-
]);
|
|
1849
|
-
oldText = oldContent;
|
|
1850
|
-
newText = newContent.newContent;
|
|
1851
|
-
affectedLines = newContent.lineNumbers;
|
|
1852
|
-
} catch (e) {
|
|
1853
|
-
logger.error("Failed to edit file", e);
|
|
1854
|
-
}
|
|
1855
|
-
}
|
|
1817
|
+
const path9 = input?.file_path ? String(input.file_path) : void 0;
|
|
1818
|
+
const oldText = input?.old_string ? String(input.old_string) : null;
|
|
1819
|
+
const newText = input?.new_string ? String(input.new_string) : "";
|
|
1856
1820
|
return {
|
|
1857
|
-
title:
|
|
1821
|
+
title: path9 ? `Edit \`${path9}\`` : "Edit",
|
|
1858
1822
|
kind: "edit",
|
|
1859
|
-
content: input &&
|
|
1823
|
+
content: input && path9 ? [
|
|
1860
1824
|
{
|
|
1861
1825
|
type: "diff",
|
|
1862
|
-
path:
|
|
1826
|
+
path: path9,
|
|
1863
1827
|
oldText,
|
|
1864
1828
|
newText
|
|
1865
1829
|
}
|
|
1866
1830
|
] : [],
|
|
1867
|
-
locations:
|
|
1831
|
+
locations: path9 ? [{ path: path9 }] : []
|
|
1868
1832
|
};
|
|
1869
1833
|
}
|
|
1870
1834
|
case "Write": {
|
|
@@ -1918,10 +1882,10 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1918
1882
|
}
|
|
1919
1883
|
if (input?.output_mode) {
|
|
1920
1884
|
switch (input.output_mode) {
|
|
1921
|
-
case "
|
|
1885
|
+
case "files_with_matches":
|
|
1922
1886
|
label += " -l";
|
|
1923
1887
|
break;
|
|
1924
|
-
case "
|
|
1888
|
+
case "count":
|
|
1925
1889
|
label += " -c";
|
|
1926
1890
|
break;
|
|
1927
1891
|
default:
|
|
@@ -1940,7 +1904,9 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1940
1904
|
if (input?.multiline) {
|
|
1941
1905
|
label += " -P";
|
|
1942
1906
|
}
|
|
1943
|
-
|
|
1907
|
+
if (input?.pattern) {
|
|
1908
|
+
label += ` "${String(input.pattern)}"`;
|
|
1909
|
+
}
|
|
1944
1910
|
if (input?.path) {
|
|
1945
1911
|
label += ` ${String(input.path)}`;
|
|
1946
1912
|
}
|
|
@@ -2034,7 +2000,49 @@ function mcpToolInfo(name, _input) {
|
|
|
2034
2000
|
content: []
|
|
2035
2001
|
};
|
|
2036
2002
|
}
|
|
2037
|
-
function
|
|
2003
|
+
function toolUpdateFromEditToolResponse(toolResponse) {
|
|
2004
|
+
if (!toolResponse || typeof toolResponse !== "object") return null;
|
|
2005
|
+
const response = toolResponse;
|
|
2006
|
+
const patches = response.structuredPatch;
|
|
2007
|
+
if (!Array.isArray(patches) || patches.length === 0) return null;
|
|
2008
|
+
const content = [];
|
|
2009
|
+
const locations = [];
|
|
2010
|
+
for (const patch of patches) {
|
|
2011
|
+
if (!patch.hunks || patch.hunks.length === 0) continue;
|
|
2012
|
+
const filePath = patch.newFileName || patch.oldFileName;
|
|
2013
|
+
const oldLines = [];
|
|
2014
|
+
const newLines = [];
|
|
2015
|
+
for (const hunk of patch.hunks) {
|
|
2016
|
+
for (const line of hunk.lines) {
|
|
2017
|
+
if (line.startsWith("-")) {
|
|
2018
|
+
oldLines.push(line.slice(1));
|
|
2019
|
+
} else if (line.startsWith("+")) {
|
|
2020
|
+
newLines.push(line.slice(1));
|
|
2021
|
+
} else if (line.startsWith(" ")) {
|
|
2022
|
+
oldLines.push(line.slice(1));
|
|
2023
|
+
newLines.push(line.slice(1));
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
content.push({
|
|
2028
|
+
type: "diff",
|
|
2029
|
+
path: filePath,
|
|
2030
|
+
oldText: oldLines.join("\n"),
|
|
2031
|
+
newText: newLines.join("\n")
|
|
2032
|
+
});
|
|
2033
|
+
const firstHunk = patch.hunks[0];
|
|
2034
|
+
locations.push({
|
|
2035
|
+
path: filePath,
|
|
2036
|
+
line: firstHunk.newStart
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
if (content.length === 0) return null;
|
|
2040
|
+
return { content, locations };
|
|
2041
|
+
}
|
|
2042
|
+
function toolUpdateFromToolResult(toolResult, toolUse, options) {
|
|
2043
|
+
if ("is_error" in toolResult && toolResult.is_error && toolResult.content && toolResult.content.length > 0) {
|
|
2044
|
+
return toAcpContentUpdate(toolResult.content, true);
|
|
2045
|
+
}
|
|
2038
2046
|
switch (toolUse?.name) {
|
|
2039
2047
|
case "Read":
|
|
2040
2048
|
if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
|
|
@@ -2051,6 +2059,16 @@ function toolUpdateFromToolResult(toolResult, toolUse) {
|
|
|
2051
2059
|
)
|
|
2052
2060
|
};
|
|
2053
2061
|
}
|
|
2062
|
+
if (itemObj.type === "image" && itemObj.source) {
|
|
2063
|
+
return {
|
|
2064
|
+
type: "content",
|
|
2065
|
+
content: {
|
|
2066
|
+
type: "image",
|
|
2067
|
+
data: itemObj.source.data ?? "",
|
|
2068
|
+
mimeType: itemObj.source.media_type ?? "image/png"
|
|
2069
|
+
}
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2054
2072
|
return {
|
|
2055
2073
|
type: "content",
|
|
2056
2074
|
content: item
|
|
@@ -2066,18 +2084,51 @@ function toolUpdateFromToolResult(toolResult, toolUse) {
|
|
|
2066
2084
|
}
|
|
2067
2085
|
return {};
|
|
2068
2086
|
case "Bash": {
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2087
|
+
const result = toolResult.content;
|
|
2088
|
+
const terminalId = "tool_use_id" in toolResult ? String(toolResult.tool_use_id) : "";
|
|
2089
|
+
const isError = "is_error" in toolResult && toolResult.is_error;
|
|
2090
|
+
let output = "";
|
|
2091
|
+
let exitCode = isError ? 1 : 0;
|
|
2092
|
+
if (result && typeof result === "object" && "type" in result && result.type === "bash_code_execution_result") {
|
|
2093
|
+
const bashResult = result;
|
|
2094
|
+
output = [bashResult.stdout, bashResult.stderr].filter(Boolean).join("\n");
|
|
2095
|
+
exitCode = bashResult.return_code;
|
|
2096
|
+
} else if (typeof result === "string") {
|
|
2097
|
+
output = result;
|
|
2098
|
+
} else if (Array.isArray(result) && result.length > 0 && "text" in result[0] && typeof result[0].text === "string") {
|
|
2099
|
+
output = result.map((c) => c.text ?? "").join("\n");
|
|
2100
|
+
}
|
|
2101
|
+
if (options?.supportsTerminalOutput) {
|
|
2102
|
+
return {
|
|
2103
|
+
content: [{ type: "terminal", terminalId }],
|
|
2104
|
+
_meta: {
|
|
2105
|
+
terminal_info: {
|
|
2106
|
+
terminal_id: terminalId
|
|
2107
|
+
},
|
|
2108
|
+
terminal_output: {
|
|
2109
|
+
terminal_id: terminalId,
|
|
2110
|
+
data: output
|
|
2111
|
+
},
|
|
2112
|
+
terminal_exit: {
|
|
2113
|
+
terminal_id: terminalId,
|
|
2114
|
+
exit_code: exitCode,
|
|
2115
|
+
signal: null
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
if (output.trim()) {
|
|
2121
|
+
return {
|
|
2122
|
+
content: toolContent().text(`\`\`\`console
|
|
2123
|
+
${output.trimEnd()}
|
|
2124
|
+
\`\`\``).build()
|
|
2125
|
+
};
|
|
2078
2126
|
}
|
|
2079
2127
|
return {};
|
|
2080
2128
|
}
|
|
2129
|
+
case "Edit":
|
|
2130
|
+
case "Write":
|
|
2131
|
+
return {};
|
|
2081
2132
|
case "ExitPlanMode": {
|
|
2082
2133
|
return { title: "Exited Plan Mode" };
|
|
2083
2134
|
}
|
|
@@ -2237,6 +2288,7 @@ function handleThinkingChunk(chunk, parentToolCallId) {
|
|
|
2237
2288
|
return update;
|
|
2238
2289
|
}
|
|
2239
2290
|
function handleToolUseChunk(chunk, ctx) {
|
|
2291
|
+
const alreadyCached = chunk.id in ctx.toolUseCache;
|
|
2240
2292
|
ctx.toolUseCache[chunk.id] = chunk;
|
|
2241
2293
|
if (chunk.name === "TodoWrite") {
|
|
2242
2294
|
const input = chunk.input;
|
|
@@ -2248,37 +2300,60 @@ function handleToolUseChunk(chunk, ctx) {
|
|
|
2248
2300
|
}
|
|
2249
2301
|
return null;
|
|
2250
2302
|
}
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2303
|
+
if (!alreadyCached && ctx.registerHooks !== false) {
|
|
2304
|
+
registerHookCallback(chunk.id, {
|
|
2305
|
+
onPostToolUseHook: async (toolUseId, _toolInput, toolResponse) => {
|
|
2306
|
+
const toolUse = ctx.toolUseCache[toolUseId];
|
|
2307
|
+
if (toolUse) {
|
|
2308
|
+
const editUpdate = toolUse.name === "Edit" ? toolUpdateFromEditToolResponse(toolResponse) : null;
|
|
2309
|
+
await ctx.client.sessionUpdate({
|
|
2310
|
+
sessionId: ctx.sessionId,
|
|
2311
|
+
update: {
|
|
2312
|
+
_meta: toolMeta(toolUse.name, toolResponse, ctx.parentToolCallId),
|
|
2313
|
+
toolCallId: toolUseId,
|
|
2314
|
+
sessionUpdate: "tool_call_update",
|
|
2315
|
+
...editUpdate ? editUpdate : {}
|
|
2316
|
+
}
|
|
2317
|
+
});
|
|
2318
|
+
} else {
|
|
2319
|
+
ctx.logger.error(
|
|
2320
|
+
`Got a tool response for tool use that wasn't tracked: ${toolUseId}`
|
|
2321
|
+
);
|
|
2322
|
+
}
|
|
2267
2323
|
}
|
|
2268
|
-
}
|
|
2269
|
-
}
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2270
2326
|
let rawInput;
|
|
2271
2327
|
try {
|
|
2272
2328
|
rawInput = JSON.parse(JSON.stringify(chunk.input));
|
|
2273
2329
|
} catch {
|
|
2274
2330
|
}
|
|
2331
|
+
const toolInfo = toolInfoFromToolUse(chunk, {
|
|
2332
|
+
supportsTerminalOutput: ctx.supportsTerminalOutput,
|
|
2333
|
+
toolUseId: chunk.id
|
|
2334
|
+
});
|
|
2335
|
+
const meta = {
|
|
2336
|
+
...toolMeta(chunk.name, void 0, ctx.parentToolCallId)
|
|
2337
|
+
};
|
|
2338
|
+
if (chunk.name === "Bash" && ctx.supportsTerminalOutput && !alreadyCached) {
|
|
2339
|
+
meta.terminal_info = { terminal_id: chunk.id };
|
|
2340
|
+
}
|
|
2341
|
+
if (alreadyCached) {
|
|
2342
|
+
return {
|
|
2343
|
+
_meta: meta,
|
|
2344
|
+
toolCallId: chunk.id,
|
|
2345
|
+
sessionUpdate: "tool_call_update",
|
|
2346
|
+
rawInput,
|
|
2347
|
+
...toolInfo
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2275
2350
|
return {
|
|
2276
|
-
_meta:
|
|
2351
|
+
_meta: meta,
|
|
2277
2352
|
toolCallId: chunk.id,
|
|
2278
2353
|
sessionUpdate: "tool_call",
|
|
2279
2354
|
rawInput,
|
|
2280
2355
|
status: "pending",
|
|
2281
|
-
...
|
|
2356
|
+
...toolInfo
|
|
2282
2357
|
};
|
|
2283
2358
|
}
|
|
2284
2359
|
function handleToolResultChunk(chunk, ctx) {
|
|
@@ -2287,36 +2362,71 @@ function handleToolResultChunk(chunk, ctx) {
|
|
|
2287
2362
|
ctx.logger.error(
|
|
2288
2363
|
`Got a tool result for tool use that wasn't tracked: ${chunk.tool_use_id}`
|
|
2289
2364
|
);
|
|
2290
|
-
return
|
|
2365
|
+
return [];
|
|
2291
2366
|
}
|
|
2292
2367
|
if (toolUse.name === "TodoWrite") {
|
|
2293
|
-
return
|
|
2368
|
+
return [];
|
|
2294
2369
|
}
|
|
2295
|
-
|
|
2296
|
-
|
|
2370
|
+
const { _meta: resultMeta, ...toolUpdate } = toolUpdateFromToolResult(
|
|
2371
|
+
chunk,
|
|
2372
|
+
toolUse,
|
|
2373
|
+
{
|
|
2374
|
+
supportsTerminalOutput: ctx.supportsTerminalOutput,
|
|
2375
|
+
toolUseId: chunk.tool_use_id
|
|
2376
|
+
}
|
|
2377
|
+
);
|
|
2378
|
+
const updates = [];
|
|
2379
|
+
if (resultMeta?.terminal_output) {
|
|
2380
|
+
const terminalOutputMeta = {
|
|
2381
|
+
terminal_output: resultMeta.terminal_output
|
|
2382
|
+
};
|
|
2383
|
+
if (ctx.parentToolCallId) {
|
|
2384
|
+
terminalOutputMeta.claudeCode = {
|
|
2385
|
+
parentToolCallId: ctx.parentToolCallId
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
updates.push({
|
|
2389
|
+
_meta: terminalOutputMeta,
|
|
2390
|
+
toolCallId: chunk.tool_use_id,
|
|
2391
|
+
sessionUpdate: "tool_call_update"
|
|
2392
|
+
});
|
|
2393
|
+
}
|
|
2394
|
+
const meta = {
|
|
2395
|
+
...toolMeta(toolUse.name, void 0, ctx.parentToolCallId),
|
|
2396
|
+
...resultMeta?.terminal_exit ? { terminal_exit: resultMeta.terminal_exit } : {}
|
|
2397
|
+
};
|
|
2398
|
+
updates.push({
|
|
2399
|
+
_meta: meta,
|
|
2297
2400
|
toolCallId: chunk.tool_use_id,
|
|
2298
2401
|
sessionUpdate: "tool_call_update",
|
|
2299
2402
|
status: chunk.is_error ? "failed" : "completed",
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
};
|
|
2403
|
+
rawOutput: chunk.content,
|
|
2404
|
+
...toolUpdate
|
|
2405
|
+
});
|
|
2406
|
+
return updates;
|
|
2305
2407
|
}
|
|
2306
2408
|
function processContentChunk(chunk, role, ctx) {
|
|
2307
2409
|
switch (chunk.type) {
|
|
2308
2410
|
case "text":
|
|
2309
|
-
case "text_delta":
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2411
|
+
case "text_delta": {
|
|
2412
|
+
const update = handleTextChunk(chunk, role, ctx.parentToolCallId);
|
|
2413
|
+
return update ? [update] : [];
|
|
2414
|
+
}
|
|
2415
|
+
case "image": {
|
|
2416
|
+
const update = handleImageChunk(chunk, role);
|
|
2417
|
+
return update ? [update] : [];
|
|
2418
|
+
}
|
|
2313
2419
|
case "thinking":
|
|
2314
|
-
case "thinking_delta":
|
|
2315
|
-
|
|
2420
|
+
case "thinking_delta": {
|
|
2421
|
+
const update = handleThinkingChunk(chunk, ctx.parentToolCallId);
|
|
2422
|
+
return update ? [update] : [];
|
|
2423
|
+
}
|
|
2316
2424
|
case "tool_use":
|
|
2317
2425
|
case "server_tool_use":
|
|
2318
|
-
case "mcp_tool_use":
|
|
2319
|
-
|
|
2426
|
+
case "mcp_tool_use": {
|
|
2427
|
+
const update = handleToolUseChunk(chunk, ctx);
|
|
2428
|
+
return update ? [update] : [];
|
|
2429
|
+
}
|
|
2320
2430
|
case "tool_result":
|
|
2321
2431
|
case "tool_search_tool_result":
|
|
2322
2432
|
case "web_fetch_tool_result":
|
|
@@ -2338,13 +2448,13 @@ function processContentChunk(chunk, role, ctx) {
|
|
|
2338
2448
|
case "container_upload":
|
|
2339
2449
|
case "compaction":
|
|
2340
2450
|
case "compaction_delta":
|
|
2341
|
-
return
|
|
2451
|
+
return [];
|
|
2342
2452
|
default:
|
|
2343
2453
|
unreachable(chunk, ctx.logger);
|
|
2344
|
-
return
|
|
2454
|
+
return [];
|
|
2345
2455
|
}
|
|
2346
2456
|
}
|
|
2347
|
-
function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId) {
|
|
2457
|
+
function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput) {
|
|
2348
2458
|
if (typeof content === "string") {
|
|
2349
2459
|
const update = {
|
|
2350
2460
|
sessionUpdate: messageUpdateType(role),
|
|
@@ -2365,18 +2475,19 @@ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentC
|
|
|
2365
2475
|
fileContentCache,
|
|
2366
2476
|
client,
|
|
2367
2477
|
logger,
|
|
2368
|
-
parentToolCallId
|
|
2478
|
+
parentToolCallId,
|
|
2479
|
+
registerHooks,
|
|
2480
|
+
supportsTerminalOutput
|
|
2369
2481
|
};
|
|
2370
2482
|
const output = [];
|
|
2371
2483
|
for (const chunk of content) {
|
|
2372
|
-
const update
|
|
2373
|
-
if (update) {
|
|
2484
|
+
for (const update of processContentChunk(chunk, role, ctx)) {
|
|
2374
2485
|
output.push({ sessionId, update });
|
|
2375
2486
|
}
|
|
2376
2487
|
}
|
|
2377
2488
|
return output;
|
|
2378
2489
|
}
|
|
2379
|
-
function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId) {
|
|
2490
|
+
function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput) {
|
|
2380
2491
|
const event = message.event;
|
|
2381
2492
|
switch (event.type) {
|
|
2382
2493
|
case "content_block_start":
|
|
@@ -2388,7 +2499,9 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
|
|
|
2388
2499
|
fileContentCache,
|
|
2389
2500
|
client,
|
|
2390
2501
|
logger,
|
|
2391
|
-
parentToolCallId
|
|
2502
|
+
parentToolCallId,
|
|
2503
|
+
registerHooks,
|
|
2504
|
+
supportsTerminalOutput
|
|
2392
2505
|
);
|
|
2393
2506
|
case "content_block_delta":
|
|
2394
2507
|
return toAcpNotifications(
|
|
@@ -2399,7 +2512,9 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
|
|
|
2399
2512
|
fileContentCache,
|
|
2400
2513
|
client,
|
|
2401
2514
|
logger,
|
|
2402
|
-
parentToolCallId
|
|
2515
|
+
parentToolCallId,
|
|
2516
|
+
registerHooks,
|
|
2517
|
+
supportsTerminalOutput
|
|
2403
2518
|
);
|
|
2404
2519
|
case "message_start":
|
|
2405
2520
|
case "message_delta":
|
|
@@ -2458,29 +2573,25 @@ async function handleSystemMessage(message, context) {
|
|
|
2458
2573
|
break;
|
|
2459
2574
|
}
|
|
2460
2575
|
}
|
|
2461
|
-
function handleResultMessage(message
|
|
2462
|
-
const
|
|
2463
|
-
if (session.cancelled) {
|
|
2464
|
-
return {
|
|
2465
|
-
shouldStop: true,
|
|
2466
|
-
stopReason: "cancelled"
|
|
2467
|
-
};
|
|
2468
|
-
}
|
|
2576
|
+
function handleResultMessage(message) {
|
|
2577
|
+
const usage = extractUsageFromResult(message);
|
|
2469
2578
|
switch (message.subtype) {
|
|
2470
2579
|
case "success": {
|
|
2471
2580
|
if (message.result.includes("Please run /login")) {
|
|
2472
2581
|
return {
|
|
2473
2582
|
shouldStop: true,
|
|
2474
|
-
error: RequestError.authRequired()
|
|
2583
|
+
error: RequestError.authRequired(),
|
|
2584
|
+
usage
|
|
2475
2585
|
};
|
|
2476
2586
|
}
|
|
2477
2587
|
if (message.is_error) {
|
|
2478
2588
|
return {
|
|
2479
2589
|
shouldStop: true,
|
|
2480
|
-
error: RequestError.internalError(void 0, message.result)
|
|
2590
|
+
error: RequestError.internalError(void 0, message.result),
|
|
2591
|
+
usage
|
|
2481
2592
|
};
|
|
2482
2593
|
}
|
|
2483
|
-
return { shouldStop: true, stopReason: "end_turn" };
|
|
2594
|
+
return { shouldStop: true, stopReason: "end_turn", usage };
|
|
2484
2595
|
}
|
|
2485
2596
|
case "error_during_execution":
|
|
2486
2597
|
if (message.is_error) {
|
|
@@ -2489,10 +2600,11 @@ function handleResultMessage(message, context) {
|
|
|
2489
2600
|
error: RequestError.internalError(
|
|
2490
2601
|
void 0,
|
|
2491
2602
|
message.errors.join(", ") || message.subtype
|
|
2492
|
-
)
|
|
2603
|
+
),
|
|
2604
|
+
usage
|
|
2493
2605
|
};
|
|
2494
2606
|
}
|
|
2495
|
-
return { shouldStop: true, stopReason: "end_turn" };
|
|
2607
|
+
return { shouldStop: true, stopReason: "end_turn", usage };
|
|
2496
2608
|
case "error_max_budget_usd":
|
|
2497
2609
|
case "error_max_turns":
|
|
2498
2610
|
case "error_max_structured_output_retries":
|
|
@@ -2502,13 +2614,37 @@ function handleResultMessage(message, context) {
|
|
|
2502
2614
|
error: RequestError.internalError(
|
|
2503
2615
|
void 0,
|
|
2504
2616
|
message.errors.join(", ") || message.subtype
|
|
2505
|
-
)
|
|
2617
|
+
),
|
|
2618
|
+
usage
|
|
2506
2619
|
};
|
|
2507
2620
|
}
|
|
2508
|
-
return { shouldStop: true, stopReason: "max_turn_requests" };
|
|
2621
|
+
return { shouldStop: true, stopReason: "max_turn_requests", usage };
|
|
2509
2622
|
default:
|
|
2510
|
-
return { shouldStop: false };
|
|
2623
|
+
return { shouldStop: false, usage };
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
function extractUsageFromResult(message) {
|
|
2627
|
+
const msg = message;
|
|
2628
|
+
const msgUsage = msg.usage;
|
|
2629
|
+
if (!msgUsage) return void 0;
|
|
2630
|
+
const modelUsage = msg.modelUsage;
|
|
2631
|
+
let contextWindowSize;
|
|
2632
|
+
if (modelUsage) {
|
|
2633
|
+
const contextWindows = Object.values(modelUsage).map(
|
|
2634
|
+
(m) => m.contextWindow
|
|
2635
|
+
);
|
|
2636
|
+
if (contextWindows.length > 0) {
|
|
2637
|
+
contextWindowSize = Math.min(...contextWindows);
|
|
2638
|
+
}
|
|
2511
2639
|
}
|
|
2640
|
+
return {
|
|
2641
|
+
inputTokens: msgUsage.input_tokens ?? 0,
|
|
2642
|
+
outputTokens: msgUsage.output_tokens ?? 0,
|
|
2643
|
+
cachedReadTokens: msgUsage.cache_read_input_tokens ?? 0,
|
|
2644
|
+
cachedWriteTokens: msgUsage.cache_creation_input_tokens ?? 0,
|
|
2645
|
+
costUsd: typeof msg.total_cost_usd === "number" ? msg.total_cost_usd : void 0,
|
|
2646
|
+
contextWindowSize
|
|
2647
|
+
};
|
|
2512
2648
|
}
|
|
2513
2649
|
async function handleStreamEvent(message, context) {
|
|
2514
2650
|
const { sessionId, client, toolUseCache, fileContentCache, logger } = context;
|
|
@@ -2520,7 +2656,9 @@ async function handleStreamEvent(message, context) {
|
|
|
2520
2656
|
fileContentCache,
|
|
2521
2657
|
client,
|
|
2522
2658
|
logger,
|
|
2523
|
-
parentToolCallId
|
|
2659
|
+
parentToolCallId,
|
|
2660
|
+
context.registerHooks,
|
|
2661
|
+
context.supportsTerminalOutput
|
|
2524
2662
|
)) {
|
|
2525
2663
|
await client.sessionUpdate(notification);
|
|
2526
2664
|
context.session.notificationHistory.push(notification);
|
|
@@ -2532,14 +2670,15 @@ function hasLocalCommandStdout(content) {
|
|
|
2532
2670
|
function hasLocalCommandStderr(content) {
|
|
2533
2671
|
return typeof content === "string" && content.includes("<local-command-stderr>");
|
|
2534
2672
|
}
|
|
2535
|
-
function isSimpleUserMessage(message) {
|
|
2536
|
-
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");
|
|
2537
|
-
}
|
|
2538
2673
|
function isLoginRequiredMessage(message) {
|
|
2539
2674
|
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;
|
|
2540
2675
|
}
|
|
2676
|
+
function isPlainTextUserMessage(message) {
|
|
2677
|
+
const content = message.message.content;
|
|
2678
|
+
return message.type === "user" && (typeof content === "string" || Array.isArray(content) && content.length === 1 && content[0].type === "text");
|
|
2679
|
+
}
|
|
2541
2680
|
function shouldSkipUserAssistantMessage(message) {
|
|
2542
|
-
return hasLocalCommandStdout(message.message.content) || hasLocalCommandStderr(message.message.content) ||
|
|
2681
|
+
return hasLocalCommandStdout(message.message.content) || hasLocalCommandStderr(message.message.content) || isLoginRequiredMessage(message);
|
|
2543
2682
|
}
|
|
2544
2683
|
function logSpecialMessages(message, logger) {
|
|
2545
2684
|
const content = message.message.content;
|
|
@@ -2560,18 +2699,33 @@ function filterMessageContent(content) {
|
|
|
2560
2699
|
}
|
|
2561
2700
|
async function handleUserAssistantMessage(message, context) {
|
|
2562
2701
|
const { session, sessionId, client, toolUseCache, fileContentCache, logger } = context;
|
|
2563
|
-
if (session.cancelled) {
|
|
2564
|
-
return {};
|
|
2565
|
-
}
|
|
2566
2702
|
if (shouldSkipUserAssistantMessage(message)) {
|
|
2703
|
+
const content2 = message.message.content;
|
|
2704
|
+
if (typeof content2 === "string" && hasLocalCommandStdout(content2) && content2.includes("Context Usage")) {
|
|
2705
|
+
const stripped = content2.replace("<local-command-stdout>", "").replace("</local-command-stdout>", "");
|
|
2706
|
+
for (const notification of toAcpNotifications(
|
|
2707
|
+
stripped,
|
|
2708
|
+
"assistant",
|
|
2709
|
+
sessionId,
|
|
2710
|
+
toolUseCache,
|
|
2711
|
+
fileContentCache,
|
|
2712
|
+
client,
|
|
2713
|
+
logger
|
|
2714
|
+
)) {
|
|
2715
|
+
await client.sessionUpdate(notification);
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2567
2718
|
logSpecialMessages(message, logger);
|
|
2568
2719
|
if (isLoginRequiredMessage(message)) {
|
|
2569
2720
|
return { shouldStop: true, error: RequestError.authRequired() };
|
|
2570
2721
|
}
|
|
2571
2722
|
return {};
|
|
2572
2723
|
}
|
|
2724
|
+
if (isPlainTextUserMessage(message)) {
|
|
2725
|
+
return {};
|
|
2726
|
+
}
|
|
2573
2727
|
const content = message.message.content;
|
|
2574
|
-
const contentToProcess = filterMessageContent(content);
|
|
2728
|
+
const contentToProcess = message.type === "assistant" ? filterMessageContent(content) : content;
|
|
2575
2729
|
const parentToolCallId = "parent_tool_use_id" in message ? message.parent_tool_use_id ?? void 0 : void 0;
|
|
2576
2730
|
for (const notification of toAcpNotifications(
|
|
2577
2731
|
contentToProcess,
|
|
@@ -2581,7 +2735,9 @@ async function handleUserAssistantMessage(message, context) {
|
|
|
2581
2735
|
fileContentCache,
|
|
2582
2736
|
client,
|
|
2583
2737
|
logger,
|
|
2584
|
-
parentToolCallId
|
|
2738
|
+
parentToolCallId,
|
|
2739
|
+
context.registerHooks,
|
|
2740
|
+
context.supportsTerminalOutput
|
|
2585
2741
|
)) {
|
|
2586
2742
|
await client.sessionUpdate(notification);
|
|
2587
2743
|
session.notificationHistory.push(notification);
|
|
@@ -2667,36 +2823,45 @@ function normalizeAskUserQuestionInput(input) {
|
|
|
2667
2823
|
}
|
|
2668
2824
|
|
|
2669
2825
|
// src/execution-mode.ts
|
|
2670
|
-
var
|
|
2826
|
+
var ALLOW_BYPASS = !IS_ROOT;
|
|
2827
|
+
var availableModes = [
|
|
2671
2828
|
{
|
|
2672
2829
|
id: "default",
|
|
2673
|
-
name: "
|
|
2674
|
-
description: "
|
|
2830
|
+
name: "Default",
|
|
2831
|
+
description: "Standard behavior, prompts for dangerous operations"
|
|
2675
2832
|
},
|
|
2676
2833
|
{
|
|
2677
2834
|
id: "acceptEdits",
|
|
2678
2835
|
name: "Accept Edits",
|
|
2679
|
-
description: "
|
|
2836
|
+
description: "Auto-accept file edit operations"
|
|
2680
2837
|
},
|
|
2681
2838
|
{
|
|
2682
2839
|
id: "plan",
|
|
2683
2840
|
name: "Plan Mode",
|
|
2684
|
-
description: "
|
|
2685
|
-
},
|
|
2686
|
-
{
|
|
2687
|
-
id: "bypassPermissions",
|
|
2688
|
-
name: "Bypass Permissions",
|
|
2689
|
-
description: "Skips all permission prompts"
|
|
2841
|
+
description: "Planning mode, no actual tool execution"
|
|
2690
2842
|
}
|
|
2843
|
+
// {
|
|
2844
|
+
// id: "dontAsk",
|
|
2845
|
+
// name: "Don't Ask",
|
|
2846
|
+
// description: "Don't prompt for permissions, deny if not pre-approved",
|
|
2847
|
+
// },
|
|
2691
2848
|
];
|
|
2849
|
+
if (ALLOW_BYPASS) {
|
|
2850
|
+
availableModes.push({
|
|
2851
|
+
id: "bypassPermissions",
|
|
2852
|
+
name: "Bypass Permissions",
|
|
2853
|
+
description: "Bypass all permission checks"
|
|
2854
|
+
});
|
|
2855
|
+
}
|
|
2692
2856
|
var TWIG_EXECUTION_MODES = [
|
|
2693
2857
|
"default",
|
|
2694
2858
|
"acceptEdits",
|
|
2695
2859
|
"plan",
|
|
2860
|
+
// "dontAsk",
|
|
2696
2861
|
"bypassPermissions"
|
|
2697
2862
|
];
|
|
2698
2863
|
function getAvailableModes() {
|
|
2699
|
-
return IS_ROOT ?
|
|
2864
|
+
return IS_ROOT ? availableModes.filter((m) => m.id !== "bypassPermissions") : availableModes;
|
|
2700
2865
|
}
|
|
2701
2866
|
|
|
2702
2867
|
// src/adapters/claude/tools.ts
|
|
@@ -2724,6 +2889,7 @@ var AUTO_ALLOWED_TOOLS = {
|
|
|
2724
2889
|
default: new Set(BASE_ALLOWED_TOOLS),
|
|
2725
2890
|
acceptEdits: /* @__PURE__ */ new Set([...BASE_ALLOWED_TOOLS, ...WRITE_TOOLS]),
|
|
2726
2891
|
plan: new Set(BASE_ALLOWED_TOOLS)
|
|
2892
|
+
// dontAsk: new Set(BASE_ALLOWED_TOOLS),
|
|
2727
2893
|
};
|
|
2728
2894
|
function isToolAllowedForMode(toolName, mode) {
|
|
2729
2895
|
if (mode === "bypassPermissions") {
|
|
@@ -2871,12 +3037,11 @@ async function validatePlanContent(planText, context) {
|
|
|
2871
3037
|
return { valid: true };
|
|
2872
3038
|
}
|
|
2873
3039
|
async function requestPlanApproval(context, updatedInput) {
|
|
2874
|
-
const { client, sessionId, toolUseID
|
|
2875
|
-
const toolInfo = toolInfoFromToolUse(
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
);
|
|
3040
|
+
const { client, sessionId, toolUseID } = context;
|
|
3041
|
+
const toolInfo = toolInfoFromToolUse({
|
|
3042
|
+
name: context.toolName,
|
|
3043
|
+
input: updatedInput
|
|
3044
|
+
});
|
|
2880
3045
|
return await client.requestPermission({
|
|
2881
3046
|
options: buildExitPlanModePermissionOptions(),
|
|
2882
3047
|
sessionId,
|
|
@@ -2895,7 +3060,14 @@ async function applyPlanApproval(response, context, updatedInput) {
|
|
|
2895
3060
|
if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "default" || response.outcome.optionId === "acceptEdits")) {
|
|
2896
3061
|
session.permissionMode = response.outcome.optionId;
|
|
2897
3062
|
await session.query.setPermissionMode(response.outcome.optionId);
|
|
2898
|
-
await context.
|
|
3063
|
+
await context.client.sessionUpdate({
|
|
3064
|
+
sessionId: context.sessionId,
|
|
3065
|
+
update: {
|
|
3066
|
+
sessionUpdate: "current_mode_update",
|
|
3067
|
+
currentModeId: response.outcome.optionId
|
|
3068
|
+
}
|
|
3069
|
+
});
|
|
3070
|
+
await context.updateConfigOption("mode", response.outcome.optionId);
|
|
2899
3071
|
return {
|
|
2900
3072
|
behavior: "allow",
|
|
2901
3073
|
updatedInput,
|
|
@@ -2916,7 +3088,7 @@ async function handleEnterPlanModeTool(context) {
|
|
|
2916
3088
|
const { session, toolInput } = context;
|
|
2917
3089
|
session.permissionMode = "plan";
|
|
2918
3090
|
await session.query.setPermissionMode("plan");
|
|
2919
|
-
await context.
|
|
3091
|
+
await context.updateConfigOption("mode", "plan");
|
|
2920
3092
|
return {
|
|
2921
3093
|
behavior: "allow",
|
|
2922
3094
|
updatedInput: toolInput
|
|
@@ -2934,6 +3106,9 @@ async function handleExitPlanModeTool(context) {
|
|
|
2934
3106
|
return validationResult.error;
|
|
2935
3107
|
}
|
|
2936
3108
|
const response = await requestPlanApproval(context, updatedInput);
|
|
3109
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
3110
|
+
throw new Error("Tool use aborted");
|
|
3111
|
+
}
|
|
2937
3112
|
return await applyPlanApproval(response, context, updatedInput);
|
|
2938
3113
|
}
|
|
2939
3114
|
function buildQuestionOptions(question) {
|
|
@@ -2957,14 +3132,13 @@ async function handleAskUserQuestionTool(context) {
|
|
|
2957
3132
|
interrupt: true
|
|
2958
3133
|
};
|
|
2959
3134
|
}
|
|
2960
|
-
const { client, sessionId, toolUseID, toolInput
|
|
3135
|
+
const { client, sessionId, toolUseID, toolInput } = context;
|
|
2961
3136
|
const firstQuestion = questions[0];
|
|
2962
3137
|
const options = buildQuestionOptions(firstQuestion);
|
|
2963
|
-
const toolInfo = toolInfoFromToolUse(
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
);
|
|
3138
|
+
const toolInfo = toolInfoFromToolUse({
|
|
3139
|
+
name: context.toolName,
|
|
3140
|
+
input: toolInput
|
|
3141
|
+
});
|
|
2968
3142
|
const response = await client.requestPermission({
|
|
2969
3143
|
options,
|
|
2970
3144
|
sessionId,
|
|
@@ -2979,6 +3153,9 @@ async function handleAskUserQuestionTool(context) {
|
|
|
2979
3153
|
}
|
|
2980
3154
|
}
|
|
2981
3155
|
});
|
|
3156
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
3157
|
+
throw new Error("Tool use aborted");
|
|
3158
|
+
}
|
|
2982
3159
|
if (response.outcome?.outcome !== "selected") {
|
|
2983
3160
|
const customMessage = response._meta?.message;
|
|
2984
3161
|
return {
|
|
@@ -3011,14 +3188,9 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
3011
3188
|
toolUseID,
|
|
3012
3189
|
client,
|
|
3013
3190
|
sessionId,
|
|
3014
|
-
fileContentCache,
|
|
3015
3191
|
suggestions
|
|
3016
3192
|
} = context;
|
|
3017
|
-
const toolInfo = toolInfoFromToolUse(
|
|
3018
|
-
{ name: toolName, input: toolInput },
|
|
3019
|
-
fileContentCache,
|
|
3020
|
-
context.logger
|
|
3021
|
-
);
|
|
3193
|
+
const toolInfo = toolInfoFromToolUse({ name: toolName, input: toolInput });
|
|
3022
3194
|
const options = buildPermissionOptions(
|
|
3023
3195
|
toolName,
|
|
3024
3196
|
toolInput,
|
|
@@ -3037,6 +3209,9 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
3037
3209
|
rawInput: toolInput
|
|
3038
3210
|
}
|
|
3039
3211
|
});
|
|
3212
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
3213
|
+
throw new Error("Tool use aborted");
|
|
3214
|
+
}
|
|
3040
3215
|
if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "allow" || response.outcome.optionId === "allow_always")) {
|
|
3041
3216
|
if (response.outcome.optionId === "allow_always") {
|
|
3042
3217
|
return {
|
|
@@ -3113,16 +3288,18 @@ async function canUseTool(context) {
|
|
|
3113
3288
|
var UNSUPPORTED_COMMANDS = [
|
|
3114
3289
|
"context",
|
|
3115
3290
|
"cost",
|
|
3291
|
+
"keybindings-help",
|
|
3116
3292
|
"login",
|
|
3117
3293
|
"logout",
|
|
3118
3294
|
"output-style:new",
|
|
3119
3295
|
"release-notes",
|
|
3120
3296
|
"todos"
|
|
3121
3297
|
];
|
|
3122
|
-
|
|
3123
|
-
const commands = await q.supportedCommands();
|
|
3298
|
+
function getAvailableSlashCommands(commands) {
|
|
3124
3299
|
return commands.map((command) => {
|
|
3125
|
-
const input = command.argumentHint ? {
|
|
3300
|
+
const input = command.argumentHint != null ? {
|
|
3301
|
+
hint: Array.isArray(command.argumentHint) ? command.argumentHint.join(" ") : command.argumentHint
|
|
3302
|
+
} : null;
|
|
3126
3303
|
let name = command.name;
|
|
3127
3304
|
if (command.name.endsWith(" (MCP)")) {
|
|
3128
3305
|
name = `mcp:${name.replace(" (MCP)", "")}`;
|
|
@@ -3220,13 +3397,19 @@ function buildEnvironment() {
|
|
|
3220
3397
|
ENABLE_TOOL_SEARCH: "auto:0"
|
|
3221
3398
|
};
|
|
3222
3399
|
}
|
|
3223
|
-
function buildHooks(userHooks, onModeChange) {
|
|
3400
|
+
function buildHooks(userHooks, onModeChange, settingsManager, logger) {
|
|
3224
3401
|
return {
|
|
3225
3402
|
...userHooks,
|
|
3226
3403
|
PostToolUse: [
|
|
3227
3404
|
...userHooks?.PostToolUse || [],
|
|
3228
3405
|
{
|
|
3229
|
-
hooks: [createPostToolUseHook({ onModeChange })]
|
|
3406
|
+
hooks: [createPostToolUseHook({ onModeChange, logger })]
|
|
3407
|
+
}
|
|
3408
|
+
],
|
|
3409
|
+
PreToolUse: [
|
|
3410
|
+
...userHooks?.PreToolUse || [],
|
|
3411
|
+
{
|
|
3412
|
+
hooks: [createPreToolUseHook(settingsManager, logger)]
|
|
3230
3413
|
}
|
|
3231
3414
|
]
|
|
3232
3415
|
};
|
|
@@ -3320,12 +3503,22 @@ function buildSessionOptions(params) {
|
|
|
3320
3503
|
permissionMode: params.permissionMode,
|
|
3321
3504
|
canUseTool: params.canUseTool,
|
|
3322
3505
|
executable: "node",
|
|
3506
|
+
tools: { type: "preset", preset: "claude_code" },
|
|
3507
|
+
extraArgs: {
|
|
3508
|
+
...params.userProvidedOptions?.extraArgs,
|
|
3509
|
+
"replay-user-messages": ""
|
|
3510
|
+
},
|
|
3323
3511
|
mcpServers: buildMcpServers(
|
|
3324
3512
|
params.userProvidedOptions?.mcpServers,
|
|
3325
3513
|
params.mcpServers
|
|
3326
3514
|
),
|
|
3327
3515
|
env: buildEnvironment(),
|
|
3328
|
-
hooks: buildHooks(
|
|
3516
|
+
hooks: buildHooks(
|
|
3517
|
+
params.userProvidedOptions?.hooks,
|
|
3518
|
+
params.onModeChange,
|
|
3519
|
+
params.settingsManager,
|
|
3520
|
+
params.logger
|
|
3521
|
+
),
|
|
3329
3522
|
abortController: getAbortController(
|
|
3330
3523
|
params.userProvidedOptions?.abortController
|
|
3331
3524
|
),
|
|
@@ -3342,13 +3535,36 @@ function buildSessionOptions(params) {
|
|
|
3342
3535
|
}
|
|
3343
3536
|
if (params.isResume) {
|
|
3344
3537
|
options.resume = params.sessionId;
|
|
3345
|
-
options.forkSession = false;
|
|
3538
|
+
options.forkSession = params.forkSession ?? false;
|
|
3346
3539
|
} else {
|
|
3347
3540
|
options.sessionId = params.sessionId;
|
|
3541
|
+
options.model = DEFAULT_MODEL;
|
|
3348
3542
|
}
|
|
3349
3543
|
if (params.additionalDirectories) {
|
|
3350
3544
|
options.additionalDirectories = params.additionalDirectories;
|
|
3351
3545
|
}
|
|
3546
|
+
if (params.disableBuiltInTools) {
|
|
3547
|
+
const builtInTools = [
|
|
3548
|
+
"Read",
|
|
3549
|
+
"Write",
|
|
3550
|
+
"Edit",
|
|
3551
|
+
"Bash",
|
|
3552
|
+
"Glob",
|
|
3553
|
+
"Grep",
|
|
3554
|
+
"Task",
|
|
3555
|
+
"TodoWrite",
|
|
3556
|
+
"ExitPlanMode",
|
|
3557
|
+
"WebSearch",
|
|
3558
|
+
"WebFetch",
|
|
3559
|
+
"SlashCommand",
|
|
3560
|
+
"Skill",
|
|
3561
|
+
"NotebookEdit"
|
|
3562
|
+
];
|
|
3563
|
+
options.disallowedTools = [
|
|
3564
|
+
...options.disallowedTools ?? [],
|
|
3565
|
+
...builtInTools
|
|
3566
|
+
];
|
|
3567
|
+
}
|
|
3352
3568
|
clearStatsigCache();
|
|
3353
3569
|
return options;
|
|
3354
3570
|
}
|
|
@@ -3361,15 +3577,268 @@ function clearStatsigCache() {
|
|
|
3361
3577
|
});
|
|
3362
3578
|
}
|
|
3363
3579
|
|
|
3580
|
+
// src/adapters/claude/session/settings.ts
|
|
3581
|
+
import * as fs2 from "fs";
|
|
3582
|
+
import * as os3 from "os";
|
|
3583
|
+
import * as path3 from "path";
|
|
3584
|
+
import { minimatch } from "minimatch";
|
|
3585
|
+
var ACP_TOOL_NAME_PREFIX = "mcp__acp__";
|
|
3586
|
+
var acpToolNames = {
|
|
3587
|
+
read: `${ACP_TOOL_NAME_PREFIX}Read`,
|
|
3588
|
+
edit: `${ACP_TOOL_NAME_PREFIX}Edit`,
|
|
3589
|
+
write: `${ACP_TOOL_NAME_PREFIX}Write`,
|
|
3590
|
+
bash: `${ACP_TOOL_NAME_PREFIX}Bash`
|
|
3591
|
+
};
|
|
3592
|
+
var SHELL_OPERATORS = ["&&", "||", ";", "|", "$(", "`", "\n"];
|
|
3593
|
+
function containsShellOperator(str) {
|
|
3594
|
+
return SHELL_OPERATORS.some((op) => str.includes(op));
|
|
3595
|
+
}
|
|
3596
|
+
var FILE_EDITING_TOOLS = [acpToolNames.edit, acpToolNames.write];
|
|
3597
|
+
var FILE_READING_TOOLS = [acpToolNames.read];
|
|
3598
|
+
var TOOL_ARG_ACCESSORS = {
|
|
3599
|
+
[acpToolNames.read]: (input) => input?.file_path,
|
|
3600
|
+
[acpToolNames.edit]: (input) => input?.file_path,
|
|
3601
|
+
[acpToolNames.write]: (input) => input?.file_path,
|
|
3602
|
+
[acpToolNames.bash]: (input) => input?.command
|
|
3603
|
+
};
|
|
3604
|
+
function parseRule(rule) {
|
|
3605
|
+
const match = rule.match(/^(\w+)(?:\((.+)\))?$/);
|
|
3606
|
+
if (!match) {
|
|
3607
|
+
return { toolName: rule };
|
|
3608
|
+
}
|
|
3609
|
+
const toolName = match[1] ?? rule;
|
|
3610
|
+
const argument = match[2];
|
|
3611
|
+
if (argument?.endsWith(":*")) {
|
|
3612
|
+
return {
|
|
3613
|
+
toolName,
|
|
3614
|
+
argument: argument.slice(0, -2),
|
|
3615
|
+
isWildcard: true
|
|
3616
|
+
};
|
|
3617
|
+
}
|
|
3618
|
+
return { toolName, argument };
|
|
3619
|
+
}
|
|
3620
|
+
function normalizePath(filePath, cwd) {
|
|
3621
|
+
let resolved = filePath;
|
|
3622
|
+
if (resolved.startsWith("~/")) {
|
|
3623
|
+
resolved = path3.join(os3.homedir(), resolved.slice(2));
|
|
3624
|
+
} else if (resolved.startsWith("./")) {
|
|
3625
|
+
resolved = path3.join(cwd, resolved.slice(2));
|
|
3626
|
+
} else if (!path3.isAbsolute(resolved)) {
|
|
3627
|
+
resolved = path3.join(cwd, resolved);
|
|
3628
|
+
}
|
|
3629
|
+
return path3.normalize(resolved).replace(/\\/g, "/");
|
|
3630
|
+
}
|
|
3631
|
+
function matchesGlob(pattern, filePath, cwd) {
|
|
3632
|
+
const normalizedPattern = normalizePath(pattern, cwd);
|
|
3633
|
+
const normalizedPath = normalizePath(filePath, cwd);
|
|
3634
|
+
return minimatch(normalizedPath, normalizedPattern, {
|
|
3635
|
+
dot: true,
|
|
3636
|
+
matchBase: false,
|
|
3637
|
+
nocase: process.platform === "win32"
|
|
3638
|
+
});
|
|
3639
|
+
}
|
|
3640
|
+
function matchesRule(rule, toolName, toolInput, cwd) {
|
|
3641
|
+
const ruleAppliesToTool = rule.toolName === "Bash" && toolName === acpToolNames.bash || rule.toolName === "Edit" && FILE_EDITING_TOOLS.includes(toolName) || rule.toolName === "Read" && FILE_READING_TOOLS.includes(toolName);
|
|
3642
|
+
if (!ruleAppliesToTool) {
|
|
3643
|
+
return false;
|
|
3644
|
+
}
|
|
3645
|
+
if (!rule.argument) {
|
|
3646
|
+
return true;
|
|
3647
|
+
}
|
|
3648
|
+
const argAccessor = TOOL_ARG_ACCESSORS[toolName];
|
|
3649
|
+
if (!argAccessor) {
|
|
3650
|
+
return true;
|
|
3651
|
+
}
|
|
3652
|
+
const actualArg = argAccessor(toolInput);
|
|
3653
|
+
if (!actualArg) {
|
|
3654
|
+
return false;
|
|
3655
|
+
}
|
|
3656
|
+
if (toolName === acpToolNames.bash) {
|
|
3657
|
+
if (rule.isWildcard) {
|
|
3658
|
+
if (!actualArg.startsWith(rule.argument)) {
|
|
3659
|
+
return false;
|
|
3660
|
+
}
|
|
3661
|
+
const remainder = actualArg.slice(rule.argument.length);
|
|
3662
|
+
if (containsShellOperator(remainder)) {
|
|
3663
|
+
return false;
|
|
3664
|
+
}
|
|
3665
|
+
return true;
|
|
3666
|
+
}
|
|
3667
|
+
return actualArg === rule.argument;
|
|
3668
|
+
}
|
|
3669
|
+
return matchesGlob(rule.argument, actualArg, cwd);
|
|
3670
|
+
}
|
|
3671
|
+
async function loadSettingsFile(filePath) {
|
|
3672
|
+
if (!filePath) {
|
|
3673
|
+
return {};
|
|
3674
|
+
}
|
|
3675
|
+
try {
|
|
3676
|
+
const content = await fs2.promises.readFile(filePath, "utf-8");
|
|
3677
|
+
return JSON.parse(content);
|
|
3678
|
+
} catch {
|
|
3679
|
+
return {};
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3682
|
+
function getManagedSettingsPath() {
|
|
3683
|
+
switch (process.platform) {
|
|
3684
|
+
case "darwin":
|
|
3685
|
+
return "/Library/Application Support/ClaudeCode/managed-settings.json";
|
|
3686
|
+
case "linux":
|
|
3687
|
+
return "/etc/claude-code/managed-settings.json";
|
|
3688
|
+
case "win32":
|
|
3689
|
+
return "C:\\Program Files\\ClaudeCode\\managed-settings.json";
|
|
3690
|
+
default:
|
|
3691
|
+
return "/etc/claude-code/managed-settings.json";
|
|
3692
|
+
}
|
|
3693
|
+
}
|
|
3694
|
+
var SettingsManager = class {
|
|
3695
|
+
cwd;
|
|
3696
|
+
userSettings = {};
|
|
3697
|
+
projectSettings = {};
|
|
3698
|
+
localSettings = {};
|
|
3699
|
+
enterpriseSettings = {};
|
|
3700
|
+
mergedSettings = {};
|
|
3701
|
+
initialized = false;
|
|
3702
|
+
constructor(cwd) {
|
|
3703
|
+
this.cwd = cwd;
|
|
3704
|
+
}
|
|
3705
|
+
async initialize() {
|
|
3706
|
+
if (this.initialized) {
|
|
3707
|
+
return;
|
|
3708
|
+
}
|
|
3709
|
+
await this.loadAllSettings();
|
|
3710
|
+
this.initialized = true;
|
|
3711
|
+
}
|
|
3712
|
+
getUserSettingsPath() {
|
|
3713
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || path3.join(os3.homedir(), ".claude");
|
|
3714
|
+
return path3.join(configDir, "settings.json");
|
|
3715
|
+
}
|
|
3716
|
+
getProjectSettingsPath() {
|
|
3717
|
+
return path3.join(this.cwd, ".claude", "settings.json");
|
|
3718
|
+
}
|
|
3719
|
+
getLocalSettingsPath() {
|
|
3720
|
+
return path3.join(this.cwd, ".claude", "settings.local.json");
|
|
3721
|
+
}
|
|
3722
|
+
async loadAllSettings() {
|
|
3723
|
+
const [userSettings, projectSettings, localSettings, enterpriseSettings] = await Promise.all([
|
|
3724
|
+
loadSettingsFile(this.getUserSettingsPath()),
|
|
3725
|
+
loadSettingsFile(this.getProjectSettingsPath()),
|
|
3726
|
+
loadSettingsFile(this.getLocalSettingsPath()),
|
|
3727
|
+
loadSettingsFile(getManagedSettingsPath())
|
|
3728
|
+
]);
|
|
3729
|
+
this.userSettings = userSettings;
|
|
3730
|
+
this.projectSettings = projectSettings;
|
|
3731
|
+
this.localSettings = localSettings;
|
|
3732
|
+
this.enterpriseSettings = enterpriseSettings;
|
|
3733
|
+
this.mergeAllSettings();
|
|
3734
|
+
}
|
|
3735
|
+
mergeAllSettings() {
|
|
3736
|
+
const allSettings = [
|
|
3737
|
+
this.userSettings,
|
|
3738
|
+
this.projectSettings,
|
|
3739
|
+
this.localSettings,
|
|
3740
|
+
this.enterpriseSettings
|
|
3741
|
+
];
|
|
3742
|
+
const permissions = {
|
|
3743
|
+
allow: [],
|
|
3744
|
+
deny: [],
|
|
3745
|
+
ask: []
|
|
3746
|
+
};
|
|
3747
|
+
const merged = { permissions };
|
|
3748
|
+
for (const settings of allSettings) {
|
|
3749
|
+
if (settings.permissions) {
|
|
3750
|
+
if (settings.permissions.allow) {
|
|
3751
|
+
permissions.allow?.push(...settings.permissions.allow);
|
|
3752
|
+
}
|
|
3753
|
+
if (settings.permissions.deny) {
|
|
3754
|
+
permissions.deny?.push(...settings.permissions.deny);
|
|
3755
|
+
}
|
|
3756
|
+
if (settings.permissions.ask) {
|
|
3757
|
+
permissions.ask?.push(...settings.permissions.ask);
|
|
3758
|
+
}
|
|
3759
|
+
if (settings.permissions.additionalDirectories) {
|
|
3760
|
+
permissions.additionalDirectories = [
|
|
3761
|
+
...permissions.additionalDirectories || [],
|
|
3762
|
+
...settings.permissions.additionalDirectories
|
|
3763
|
+
];
|
|
3764
|
+
}
|
|
3765
|
+
if (settings.permissions.defaultMode) {
|
|
3766
|
+
permissions.defaultMode = settings.permissions.defaultMode;
|
|
3767
|
+
}
|
|
3768
|
+
}
|
|
3769
|
+
if (settings.env) {
|
|
3770
|
+
merged.env = { ...merged.env, ...settings.env };
|
|
3771
|
+
}
|
|
3772
|
+
if (settings.model) {
|
|
3773
|
+
merged.model = settings.model;
|
|
3774
|
+
}
|
|
3775
|
+
}
|
|
3776
|
+
this.mergedSettings = merged;
|
|
3777
|
+
}
|
|
3778
|
+
checkPermission(toolName, toolInput) {
|
|
3779
|
+
if (!toolName.startsWith(ACP_TOOL_NAME_PREFIX)) {
|
|
3780
|
+
return { decision: "ask" };
|
|
3781
|
+
}
|
|
3782
|
+
const permissions = this.mergedSettings.permissions;
|
|
3783
|
+
if (!permissions) {
|
|
3784
|
+
return { decision: "ask" };
|
|
3785
|
+
}
|
|
3786
|
+
for (const rule of permissions.deny || []) {
|
|
3787
|
+
const parsed = parseRule(rule);
|
|
3788
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
3789
|
+
return { decision: "deny", rule, source: "deny" };
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
for (const rule of permissions.allow || []) {
|
|
3793
|
+
const parsed = parseRule(rule);
|
|
3794
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
3795
|
+
return { decision: "allow", rule, source: "allow" };
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
for (const rule of permissions.ask || []) {
|
|
3799
|
+
const parsed = parseRule(rule);
|
|
3800
|
+
if (matchesRule(parsed, toolName, toolInput, this.cwd)) {
|
|
3801
|
+
return { decision: "ask", rule, source: "ask" };
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
return { decision: "ask" };
|
|
3805
|
+
}
|
|
3806
|
+
getSettings() {
|
|
3807
|
+
return this.mergedSettings;
|
|
3808
|
+
}
|
|
3809
|
+
getCwd() {
|
|
3810
|
+
return this.cwd;
|
|
3811
|
+
}
|
|
3812
|
+
async setCwd(cwd) {
|
|
3813
|
+
if (this.cwd === cwd) {
|
|
3814
|
+
return;
|
|
3815
|
+
}
|
|
3816
|
+
this.dispose();
|
|
3817
|
+
this.cwd = cwd;
|
|
3818
|
+
this.initialized = false;
|
|
3819
|
+
await this.initialize();
|
|
3820
|
+
}
|
|
3821
|
+
dispose() {
|
|
3822
|
+
this.initialized = false;
|
|
3823
|
+
}
|
|
3824
|
+
};
|
|
3825
|
+
|
|
3364
3826
|
// src/adapters/claude/claude-agent.ts
|
|
3365
3827
|
var SESSION_VALIDATION_TIMEOUT_MS = 1e4;
|
|
3828
|
+
var MAX_TITLE_LENGTH = 256;
|
|
3829
|
+
function sanitizeTitle(text2) {
|
|
3830
|
+
const sanitized = text2.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
3831
|
+
if (sanitized.length <= MAX_TITLE_LENGTH) {
|
|
3832
|
+
return sanitized;
|
|
3833
|
+
}
|
|
3834
|
+
return `${sanitized.slice(0, MAX_TITLE_LENGTH - 1)}\u2026`;
|
|
3835
|
+
}
|
|
3366
3836
|
var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
3367
3837
|
adapterName = "claude";
|
|
3368
3838
|
toolUseCache;
|
|
3369
3839
|
backgroundTerminals = {};
|
|
3370
3840
|
clientCapabilities;
|
|
3371
3841
|
options;
|
|
3372
|
-
lastSentConfigOptions;
|
|
3373
3842
|
constructor(client, options) {
|
|
3374
3843
|
super(client);
|
|
3375
3844
|
this.options = options;
|
|
@@ -3390,125 +3859,416 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3390
3859
|
sse: true
|
|
3391
3860
|
},
|
|
3392
3861
|
loadSession: true,
|
|
3862
|
+
sessionCapabilities: {
|
|
3863
|
+
list: {},
|
|
3864
|
+
fork: {},
|
|
3865
|
+
resume: {}
|
|
3866
|
+
},
|
|
3393
3867
|
_meta: {
|
|
3394
3868
|
posthog: {
|
|
3395
3869
|
resumeSession: true
|
|
3870
|
+
},
|
|
3871
|
+
claudeCode: {
|
|
3872
|
+
promptQueueing: true
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3875
|
+
},
|
|
3876
|
+
agentInfo: {
|
|
3877
|
+
name: package_default.name,
|
|
3878
|
+
title: "Claude Agent",
|
|
3879
|
+
version: package_default.version
|
|
3880
|
+
},
|
|
3881
|
+
authMethods: []
|
|
3882
|
+
};
|
|
3883
|
+
}
|
|
3884
|
+
async newSession(params) {
|
|
3885
|
+
if (fs3.existsSync(path4.resolve(os4.homedir(), ".claude.json.backup")) && !fs3.existsSync(path4.resolve(os4.homedir(), ".claude.json"))) {
|
|
3886
|
+
throw RequestError2.authRequired();
|
|
3887
|
+
}
|
|
3888
|
+
const response = await this.createSession(params, {
|
|
3889
|
+
// Revisit these meta values once we support resume
|
|
3890
|
+
resume: params._meta?.claudeCode?.options?.resume
|
|
3891
|
+
});
|
|
3892
|
+
return response;
|
|
3893
|
+
}
|
|
3894
|
+
async unstable_forkSession(params) {
|
|
3895
|
+
return this.createSession(
|
|
3896
|
+
{
|
|
3897
|
+
cwd: params.cwd,
|
|
3898
|
+
mcpServers: params.mcpServers ?? [],
|
|
3899
|
+
_meta: params._meta
|
|
3900
|
+
},
|
|
3901
|
+
{ resume: params.sessionId, forkSession: true }
|
|
3902
|
+
);
|
|
3903
|
+
}
|
|
3904
|
+
async unstable_resumeSession(params) {
|
|
3905
|
+
const response = await this.createSession(
|
|
3906
|
+
{
|
|
3907
|
+
cwd: params.cwd,
|
|
3908
|
+
mcpServers: params.mcpServers ?? [],
|
|
3909
|
+
_meta: params._meta
|
|
3910
|
+
},
|
|
3911
|
+
{
|
|
3912
|
+
resume: params.sessionId
|
|
3913
|
+
}
|
|
3914
|
+
);
|
|
3915
|
+
return response;
|
|
3916
|
+
}
|
|
3917
|
+
async loadSession(params) {
|
|
3918
|
+
const response = await this.createSession(
|
|
3919
|
+
{
|
|
3920
|
+
cwd: params.cwd,
|
|
3921
|
+
mcpServers: params.mcpServers ?? [],
|
|
3922
|
+
_meta: params._meta
|
|
3923
|
+
},
|
|
3924
|
+
{ resume: params.sessionId, skipBackgroundFetches: true }
|
|
3925
|
+
);
|
|
3926
|
+
await this.replaySessionHistory(params.sessionId);
|
|
3927
|
+
this.deferBackgroundFetches(this.session.query);
|
|
3928
|
+
return {
|
|
3929
|
+
modes: response.modes,
|
|
3930
|
+
models: response.models,
|
|
3931
|
+
configOptions: response.configOptions
|
|
3932
|
+
};
|
|
3933
|
+
}
|
|
3934
|
+
async unstable_listSessions(params) {
|
|
3935
|
+
const sdkSessions = await listSessions({ dir: params.cwd ?? void 0 });
|
|
3936
|
+
const sessions = [];
|
|
3937
|
+
for (const session of sdkSessions) {
|
|
3938
|
+
if (!session.cwd) continue;
|
|
3939
|
+
sessions.push({
|
|
3940
|
+
sessionId: session.sessionId,
|
|
3941
|
+
cwd: session.cwd,
|
|
3942
|
+
title: sanitizeTitle(session.customTitle || session.summary || ""),
|
|
3943
|
+
updatedAt: new Date(session.lastModified).toISOString()
|
|
3944
|
+
});
|
|
3945
|
+
}
|
|
3946
|
+
return {
|
|
3947
|
+
sessions
|
|
3948
|
+
};
|
|
3949
|
+
}
|
|
3950
|
+
async prompt(params) {
|
|
3951
|
+
this.session.cancelled = false;
|
|
3952
|
+
this.session.interruptReason = void 0;
|
|
3953
|
+
this.session.accumulatedUsage = {
|
|
3954
|
+
inputTokens: 0,
|
|
3955
|
+
outputTokens: 0,
|
|
3956
|
+
cachedReadTokens: 0,
|
|
3957
|
+
cachedWriteTokens: 0
|
|
3958
|
+
};
|
|
3959
|
+
const userMessage = promptToClaude(params);
|
|
3960
|
+
if (this.session.promptRunning) {
|
|
3961
|
+
const uuid = randomUUID();
|
|
3962
|
+
userMessage.uuid = uuid;
|
|
3963
|
+
this.session.input.push(userMessage);
|
|
3964
|
+
const order = this.session.nextPendingOrder++;
|
|
3965
|
+
const cancelled = await new Promise((resolve4) => {
|
|
3966
|
+
this.session.pendingMessages.set(uuid, { resolve: resolve4, order });
|
|
3967
|
+
});
|
|
3968
|
+
if (cancelled) {
|
|
3969
|
+
return { stopReason: "cancelled" };
|
|
3970
|
+
}
|
|
3971
|
+
} else {
|
|
3972
|
+
this.session.input.push(userMessage);
|
|
3973
|
+
}
|
|
3974
|
+
await this.broadcastUserMessage(params);
|
|
3975
|
+
this.session.promptRunning = true;
|
|
3976
|
+
let handedOff = false;
|
|
3977
|
+
let lastAssistantTotalUsage = null;
|
|
3978
|
+
const supportsTerminalOutput = this.clientCapabilities?._meta?.terminal_output === true;
|
|
3979
|
+
const context = {
|
|
3980
|
+
session: this.session,
|
|
3981
|
+
sessionId: params.sessionId,
|
|
3982
|
+
client: this.client,
|
|
3983
|
+
toolUseCache: this.toolUseCache,
|
|
3984
|
+
fileContentCache: this.fileContentCache,
|
|
3985
|
+
logger: this.logger,
|
|
3986
|
+
supportsTerminalOutput
|
|
3987
|
+
};
|
|
3988
|
+
try {
|
|
3989
|
+
while (true) {
|
|
3990
|
+
const { value: message, done } = await this.session.query.next();
|
|
3991
|
+
if (done || !message) {
|
|
3992
|
+
if (this.session.cancelled) {
|
|
3993
|
+
return {
|
|
3994
|
+
stopReason: "cancelled",
|
|
3995
|
+
_meta: this.session.interruptReason ? { interruptReason: this.session.interruptReason } : void 0
|
|
3996
|
+
};
|
|
3997
|
+
}
|
|
3998
|
+
break;
|
|
3999
|
+
}
|
|
4000
|
+
switch (message.type) {
|
|
4001
|
+
case "system":
|
|
4002
|
+
if (message.subtype === "compact_boundary") {
|
|
4003
|
+
lastAssistantTotalUsage = 0;
|
|
4004
|
+
}
|
|
4005
|
+
await handleSystemMessage(message, context);
|
|
4006
|
+
break;
|
|
4007
|
+
case "result": {
|
|
4008
|
+
if (this.session.cancelled) {
|
|
4009
|
+
return { stopReason: "cancelled" };
|
|
4010
|
+
}
|
|
4011
|
+
this.session.accumulatedUsage.inputTokens += message.usage.input_tokens;
|
|
4012
|
+
this.session.accumulatedUsage.outputTokens += message.usage.output_tokens;
|
|
4013
|
+
this.session.accumulatedUsage.cachedReadTokens += message.usage.cache_read_input_tokens;
|
|
4014
|
+
this.session.accumulatedUsage.cachedWriteTokens += message.usage.cache_creation_input_tokens;
|
|
4015
|
+
const contextWindows = Object.values(message.modelUsage).map(
|
|
4016
|
+
(m) => m.contextWindow
|
|
4017
|
+
);
|
|
4018
|
+
const contextWindowSize = contextWindows.length > 0 ? Math.min(...contextWindows) : 2e5;
|
|
4019
|
+
if (lastAssistantTotalUsage !== null) {
|
|
4020
|
+
await this.client.sessionUpdate({
|
|
4021
|
+
sessionId: params.sessionId,
|
|
4022
|
+
update: {
|
|
4023
|
+
sessionUpdate: "usage_update",
|
|
4024
|
+
used: lastAssistantTotalUsage,
|
|
4025
|
+
size: contextWindowSize,
|
|
4026
|
+
cost: {
|
|
4027
|
+
amount: message.total_cost_usd,
|
|
4028
|
+
currency: "USD"
|
|
4029
|
+
}
|
|
4030
|
+
}
|
|
4031
|
+
});
|
|
4032
|
+
}
|
|
4033
|
+
await this.client.extNotification("_posthog/usage_update", {
|
|
4034
|
+
sessionId: params.sessionId,
|
|
4035
|
+
used: {
|
|
4036
|
+
inputTokens: message.usage.input_tokens,
|
|
4037
|
+
outputTokens: message.usage.output_tokens,
|
|
4038
|
+
cachedReadTokens: message.usage.cache_read_input_tokens,
|
|
4039
|
+
cachedWriteTokens: message.usage.cache_creation_input_tokens
|
|
4040
|
+
},
|
|
4041
|
+
cost: message.total_cost_usd
|
|
4042
|
+
});
|
|
4043
|
+
const usage = {
|
|
4044
|
+
inputTokens: this.session.accumulatedUsage.inputTokens,
|
|
4045
|
+
outputTokens: this.session.accumulatedUsage.outputTokens,
|
|
4046
|
+
cachedReadTokens: this.session.accumulatedUsage.cachedReadTokens,
|
|
4047
|
+
cachedWriteTokens: this.session.accumulatedUsage.cachedWriteTokens,
|
|
4048
|
+
totalTokens: this.session.accumulatedUsage.inputTokens + this.session.accumulatedUsage.outputTokens + this.session.accumulatedUsage.cachedReadTokens + this.session.accumulatedUsage.cachedWriteTokens
|
|
4049
|
+
};
|
|
4050
|
+
const result = handleResultMessage(message);
|
|
4051
|
+
if (result.error) throw result.error;
|
|
4052
|
+
switch (message.subtype) {
|
|
4053
|
+
case "error_max_budget_usd":
|
|
4054
|
+
case "error_max_turns":
|
|
4055
|
+
case "error_max_structured_output_retries":
|
|
4056
|
+
return { stopReason: "max_turn_requests", usage };
|
|
4057
|
+
default:
|
|
4058
|
+
return { stopReason: "end_turn", usage };
|
|
4059
|
+
}
|
|
4060
|
+
}
|
|
4061
|
+
case "stream_event":
|
|
4062
|
+
await handleStreamEvent(message, context);
|
|
4063
|
+
break;
|
|
4064
|
+
case "user":
|
|
4065
|
+
case "assistant": {
|
|
4066
|
+
if (this.session.cancelled) {
|
|
4067
|
+
break;
|
|
4068
|
+
}
|
|
4069
|
+
if (message.type === "user" && "uuid" in message && message.uuid) {
|
|
4070
|
+
const pending = this.session.pendingMessages.get(
|
|
4071
|
+
message.uuid
|
|
4072
|
+
);
|
|
4073
|
+
if (pending) {
|
|
4074
|
+
pending.resolve(false);
|
|
4075
|
+
this.session.pendingMessages.delete(message.uuid);
|
|
4076
|
+
handedOff = true;
|
|
4077
|
+
return { stopReason: "end_turn" };
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4080
|
+
if ("usage" in message.message && message.parent_tool_use_id === null) {
|
|
4081
|
+
const usage = message.message.usage;
|
|
4082
|
+
lastAssistantTotalUsage = usage.input_tokens + usage.output_tokens + usage.cache_read_input_tokens + usage.cache_creation_input_tokens;
|
|
4083
|
+
}
|
|
4084
|
+
const result = await handleUserAssistantMessage(message, context);
|
|
4085
|
+
if (result.error) throw result.error;
|
|
4086
|
+
if (result.shouldStop) {
|
|
4087
|
+
return { stopReason: "end_turn" };
|
|
4088
|
+
}
|
|
4089
|
+
break;
|
|
3396
4090
|
}
|
|
4091
|
+
case "tool_progress":
|
|
4092
|
+
case "auth_status":
|
|
4093
|
+
case "tool_use_summary":
|
|
4094
|
+
break;
|
|
4095
|
+
default:
|
|
4096
|
+
unreachable(message, this.logger);
|
|
4097
|
+
break;
|
|
3397
4098
|
}
|
|
3398
|
-
}
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
id: "claude-login",
|
|
3407
|
-
name: "Log in with Claude Code",
|
|
3408
|
-
description: "Run `claude /login` in the terminal"
|
|
4099
|
+
}
|
|
4100
|
+
throw new Error("Session did not end in result");
|
|
4101
|
+
} finally {
|
|
4102
|
+
if (!handedOff) {
|
|
4103
|
+
this.session.promptRunning = false;
|
|
4104
|
+
for (const [key, pending] of this.session.pendingMessages) {
|
|
4105
|
+
pending.resolve(true);
|
|
4106
|
+
this.session.pendingMessages.delete(key);
|
|
3409
4107
|
}
|
|
3410
|
-
|
|
3411
|
-
}
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
3412
4110
|
}
|
|
3413
|
-
|
|
3414
|
-
|
|
4111
|
+
// Called by BaseAcpAgent#cancel() to interrupt the session
|
|
4112
|
+
async interrupt() {
|
|
4113
|
+
this.session.cancelled = true;
|
|
4114
|
+
for (const [, pending] of this.session.pendingMessages) {
|
|
4115
|
+
pending.resolve(true);
|
|
4116
|
+
}
|
|
4117
|
+
this.session.pendingMessages.clear();
|
|
4118
|
+
await this.session.query.interrupt();
|
|
3415
4119
|
}
|
|
3416
|
-
async
|
|
3417
|
-
|
|
4120
|
+
async unstable_setSessionModel(params) {
|
|
4121
|
+
const sdkModelId = toSdkModelId(params.modelId);
|
|
4122
|
+
await this.session.query.setModel(sdkModelId);
|
|
4123
|
+
this.session.modelId = params.modelId;
|
|
4124
|
+
await this.updateConfigOption("model", params.modelId);
|
|
4125
|
+
return {};
|
|
4126
|
+
}
|
|
4127
|
+
async setSessionMode(params) {
|
|
4128
|
+
await this.applySessionMode(params.modeId);
|
|
4129
|
+
await this.updateConfigOption("mode", params.modeId);
|
|
4130
|
+
return {};
|
|
4131
|
+
}
|
|
4132
|
+
async setSessionConfigOption(params) {
|
|
4133
|
+
const option = this.session.configOptions.find(
|
|
4134
|
+
(o) => o.id === params.configId
|
|
4135
|
+
);
|
|
4136
|
+
if (!option) {
|
|
4137
|
+
throw new Error(`Unknown config option: ${params.configId}`);
|
|
4138
|
+
}
|
|
4139
|
+
const allValues = "options" in option && Array.isArray(option.options) ? option.options.flatMap(
|
|
4140
|
+
(o) => "options" in o && Array.isArray(o.options) ? o.options : [o]
|
|
4141
|
+
) : [];
|
|
4142
|
+
const validValue = allValues.find((o) => o.value === params.value);
|
|
4143
|
+
if (!validValue) {
|
|
4144
|
+
throw new Error(
|
|
4145
|
+
`Invalid value for config option ${params.configId}: ${params.value}`
|
|
4146
|
+
);
|
|
4147
|
+
}
|
|
4148
|
+
if (params.configId === "mode") {
|
|
4149
|
+
await this.applySessionMode(params.value);
|
|
4150
|
+
await this.client.sessionUpdate({
|
|
4151
|
+
sessionId: this.sessionId,
|
|
4152
|
+
update: {
|
|
4153
|
+
sessionUpdate: "current_mode_update",
|
|
4154
|
+
currentModeId: params.value
|
|
4155
|
+
}
|
|
4156
|
+
});
|
|
4157
|
+
} else if (params.configId === "model") {
|
|
4158
|
+
const sdkModelId = toSdkModelId(params.value);
|
|
4159
|
+
await this.session.query.setModel(sdkModelId);
|
|
4160
|
+
this.session.modelId = params.value;
|
|
4161
|
+
}
|
|
4162
|
+
this.session.configOptions = this.session.configOptions.map(
|
|
4163
|
+
(o) => o.id === params.configId ? { ...o, currentValue: params.value } : o
|
|
4164
|
+
);
|
|
4165
|
+
return { configOptions: this.session.configOptions };
|
|
4166
|
+
}
|
|
4167
|
+
async updateConfigOption(configId, value) {
|
|
4168
|
+
this.session.configOptions = this.session.configOptions.map(
|
|
4169
|
+
(o) => o.id === configId ? { ...o, currentValue: value } : o
|
|
4170
|
+
);
|
|
4171
|
+
await this.client.sessionUpdate({
|
|
4172
|
+
sessionId: this.sessionId,
|
|
4173
|
+
update: {
|
|
4174
|
+
sessionUpdate: "config_option_update",
|
|
4175
|
+
configOptions: this.session.configOptions
|
|
4176
|
+
}
|
|
4177
|
+
});
|
|
4178
|
+
}
|
|
4179
|
+
async applySessionMode(modeId) {
|
|
4180
|
+
if (!TWIG_EXECUTION_MODES.includes(modeId)) {
|
|
4181
|
+
throw new Error("Invalid Mode");
|
|
4182
|
+
}
|
|
4183
|
+
const previousMode = this.session.permissionMode;
|
|
4184
|
+
this.session.permissionMode = modeId;
|
|
4185
|
+
try {
|
|
4186
|
+
await this.session.query.setPermissionMode(modeId);
|
|
4187
|
+
} catch (error) {
|
|
4188
|
+
this.session.permissionMode = previousMode;
|
|
4189
|
+
if (error instanceof Error) {
|
|
4190
|
+
if (!error.message) {
|
|
4191
|
+
error.message = "Invalid Mode";
|
|
4192
|
+
}
|
|
4193
|
+
throw error;
|
|
4194
|
+
}
|
|
4195
|
+
throw new Error("Invalid Mode");
|
|
4196
|
+
}
|
|
4197
|
+
}
|
|
4198
|
+
async createSession(params, creationOpts = {}) {
|
|
4199
|
+
const { cwd } = params;
|
|
4200
|
+
const { resume, forkSession } = creationOpts;
|
|
4201
|
+
const isResume = !!resume;
|
|
3418
4202
|
const meta = params._meta;
|
|
3419
4203
|
const taskId = meta?.persistence?.taskId;
|
|
3420
|
-
|
|
3421
|
-
|
|
4204
|
+
let sessionId;
|
|
4205
|
+
if (forkSession) {
|
|
4206
|
+
sessionId = uuidv7();
|
|
4207
|
+
} else if (isResume) {
|
|
4208
|
+
sessionId = resume;
|
|
4209
|
+
} else {
|
|
4210
|
+
sessionId = uuidv7();
|
|
4211
|
+
}
|
|
4212
|
+
const input = new Pushable();
|
|
4213
|
+
const settingsManager = new SettingsManager(cwd);
|
|
4214
|
+
await settingsManager.initialize();
|
|
4215
|
+
const mcpServers = parseMcpServers(params);
|
|
4216
|
+
const systemPrompt = buildSystemPrompt(meta?.systemPrompt);
|
|
4217
|
+
this.logger.info(isResume ? "Resuming session" : "Creating new session", {
|
|
3422
4218
|
sessionId,
|
|
3423
4219
|
taskId,
|
|
3424
4220
|
taskRunId: meta?.taskRunId,
|
|
3425
|
-
cwd
|
|
4221
|
+
cwd
|
|
3426
4222
|
});
|
|
3427
4223
|
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
3428
|
-
const mcpServers = parseMcpServers(params);
|
|
3429
4224
|
const options = buildSessionOptions({
|
|
3430
|
-
cwd
|
|
4225
|
+
cwd,
|
|
3431
4226
|
mcpServers,
|
|
3432
4227
|
permissionMode,
|
|
3433
4228
|
canUseTool: this.createCanUseTool(sessionId),
|
|
3434
4229
|
logger: this.logger,
|
|
3435
|
-
systemPrompt
|
|
4230
|
+
systemPrompt,
|
|
3436
4231
|
userProvidedOptions: meta?.claudeCode?.options,
|
|
3437
4232
|
sessionId,
|
|
3438
|
-
isResume
|
|
3439
|
-
|
|
4233
|
+
isResume,
|
|
4234
|
+
forkSession,
|
|
4235
|
+
additionalDirectories: meta?.claudeCode?.options?.additionalDirectories,
|
|
4236
|
+
disableBuiltInTools: meta?.disableBuiltInTools,
|
|
4237
|
+
settingsManager,
|
|
4238
|
+
onModeChange: this.createOnModeChange(),
|
|
3440
4239
|
onProcessSpawned: this.options?.onProcessSpawned,
|
|
3441
4240
|
onProcessExited: this.options?.onProcessExited
|
|
3442
4241
|
});
|
|
3443
|
-
const
|
|
3444
|
-
options.model = DEFAULT_MODEL;
|
|
4242
|
+
const abortController = options.abortController;
|
|
3445
4243
|
const q = query({ prompt: input, options });
|
|
3446
|
-
const session =
|
|
3447
|
-
|
|
3448
|
-
q,
|
|
4244
|
+
const session = {
|
|
4245
|
+
query: q,
|
|
3449
4246
|
input,
|
|
4247
|
+
cancelled: false,
|
|
4248
|
+
settingsManager,
|
|
3450
4249
|
permissionMode,
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
const resolvedSdkModel = toSdkModelId(modelOptions.currentModelId);
|
|
3466
|
-
if (resolvedSdkModel !== DEFAULT_MODEL) {
|
|
3467
|
-
await this.trySetModel(q, modelOptions.currentModelId);
|
|
3468
|
-
}
|
|
3469
|
-
const configOptions = await this.buildConfigOptions(modelOptions);
|
|
3470
|
-
return {
|
|
3471
|
-
sessionId,
|
|
3472
|
-
configOptions
|
|
3473
|
-
};
|
|
3474
|
-
}
|
|
3475
|
-
async loadSession(params) {
|
|
3476
|
-
return this.resumeSession(params);
|
|
3477
|
-
}
|
|
3478
|
-
async resumeSession(params) {
|
|
3479
|
-
const meta = params._meta;
|
|
3480
|
-
const taskId = meta?.persistence?.taskId;
|
|
3481
|
-
const sessionId = meta?.sessionId;
|
|
3482
|
-
if (!sessionId) {
|
|
3483
|
-
throw new Error("Cannot resume session without sessionId");
|
|
3484
|
-
}
|
|
3485
|
-
if (this.sessionId === sessionId) {
|
|
3486
|
-
return {};
|
|
3487
|
-
}
|
|
3488
|
-
this.logger.info("Resuming session", {
|
|
3489
|
-
sessionId,
|
|
3490
|
-
taskId,
|
|
3491
|
-
taskRunId: meta?.taskRunId,
|
|
3492
|
-
cwd: params.cwd
|
|
3493
|
-
});
|
|
3494
|
-
const mcpServers = parseMcpServers(params);
|
|
3495
|
-
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
3496
|
-
const { query: q, session } = await this.initializeQuery({
|
|
3497
|
-
cwd: params.cwd,
|
|
3498
|
-
permissionMode,
|
|
3499
|
-
mcpServers,
|
|
3500
|
-
systemPrompt: buildSystemPrompt(meta?.systemPrompt),
|
|
3501
|
-
userProvidedOptions: meta?.claudeCode?.options,
|
|
3502
|
-
sessionId,
|
|
3503
|
-
isResume: true,
|
|
3504
|
-
additionalDirectories: meta?.claudeCode?.options?.additionalDirectories
|
|
3505
|
-
});
|
|
3506
|
-
this.logger.info("Session query initialized, awaiting resumption", {
|
|
3507
|
-
sessionId,
|
|
3508
|
-
taskId,
|
|
4250
|
+
abortController,
|
|
4251
|
+
accumulatedUsage: {
|
|
4252
|
+
inputTokens: 0,
|
|
4253
|
+
outputTokens: 0,
|
|
4254
|
+
cachedReadTokens: 0,
|
|
4255
|
+
cachedWriteTokens: 0
|
|
4256
|
+
},
|
|
4257
|
+
configOptions: [],
|
|
4258
|
+
promptRunning: false,
|
|
4259
|
+
pendingMessages: /* @__PURE__ */ new Map(),
|
|
4260
|
+
nextPendingOrder: 0,
|
|
4261
|
+
// Custom properties
|
|
4262
|
+
cwd,
|
|
4263
|
+
notificationHistory: [],
|
|
3509
4264
|
taskRunId: meta?.taskRunId
|
|
3510
|
-
}
|
|
3511
|
-
session
|
|
4265
|
+
};
|
|
4266
|
+
this.session = session;
|
|
4267
|
+
this.sessionId = sessionId;
|
|
4268
|
+
this.logger.info(
|
|
4269
|
+
isResume ? "Session query initialized, awaiting resumption" : "Session query initialized, awaiting initialization",
|
|
4270
|
+
{ sessionId, taskId, taskRunId: meta?.taskRunId }
|
|
4271
|
+
);
|
|
3512
4272
|
try {
|
|
3513
4273
|
const result = await withTimeout(
|
|
3514
4274
|
q.initializationResult(),
|
|
@@ -3516,231 +4276,181 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3516
4276
|
);
|
|
3517
4277
|
if (result.result === "timeout") {
|
|
3518
4278
|
throw new Error(
|
|
3519
|
-
`Session resumption timed out for sessionId=${sessionId}`
|
|
4279
|
+
`Session ${isResume ? forkSession ? "fork" : "resumption" : "initialization"} timed out for sessionId=${sessionId}`
|
|
3520
4280
|
);
|
|
3521
4281
|
}
|
|
3522
4282
|
} catch (err) {
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
4283
|
+
settingsManager.dispose();
|
|
4284
|
+
this.logger.error(
|
|
4285
|
+
isResume ? forkSession ? "Session fork failed" : "Session resumption failed" : "Session initialization failed",
|
|
4286
|
+
{
|
|
4287
|
+
sessionId,
|
|
4288
|
+
taskId,
|
|
4289
|
+
taskRunId: meta?.taskRunId,
|
|
4290
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4291
|
+
}
|
|
4292
|
+
);
|
|
3529
4293
|
throw err;
|
|
3530
4294
|
}
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
const configOptions = await this.buildConfigOptions();
|
|
3538
|
-
return { configOptions };
|
|
3539
|
-
}
|
|
3540
|
-
async prompt(params) {
|
|
3541
|
-
this.session.cancelled = false;
|
|
3542
|
-
this.session.interruptReason = void 0;
|
|
3543
|
-
await this.broadcastUserMessage(params);
|
|
3544
|
-
this.session.input.push(promptToClaude(params));
|
|
3545
|
-
return this.processMessages(params.sessionId);
|
|
3546
|
-
}
|
|
3547
|
-
async setSessionConfigOption(params) {
|
|
3548
|
-
const configId = params.configId;
|
|
3549
|
-
const value = params.value;
|
|
3550
|
-
if (configId === "mode") {
|
|
3551
|
-
const modeId = value;
|
|
3552
|
-
if (!TWIG_EXECUTION_MODES.includes(modeId)) {
|
|
3553
|
-
throw new Error("Invalid Mode");
|
|
3554
|
-
}
|
|
3555
|
-
this.session.permissionMode = modeId;
|
|
3556
|
-
await this.session.query.setPermissionMode(modeId);
|
|
3557
|
-
} else if (configId === "model") {
|
|
3558
|
-
await this.setModelWithFallback(this.session.query, value);
|
|
3559
|
-
this.session.modelId = value;
|
|
3560
|
-
} else {
|
|
3561
|
-
throw new Error("Unsupported config option");
|
|
3562
|
-
}
|
|
3563
|
-
await this.emitConfigOptionsUpdate();
|
|
3564
|
-
return { configOptions: await this.buildConfigOptions() };
|
|
3565
|
-
}
|
|
3566
|
-
async interruptSession() {
|
|
3567
|
-
await this.session.query.interrupt();
|
|
3568
|
-
}
|
|
3569
|
-
async extMethod(method, params) {
|
|
3570
|
-
if (method === "_posthog/session/resume") {
|
|
3571
|
-
const result = await this.resumeSession(
|
|
3572
|
-
params
|
|
3573
|
-
);
|
|
3574
|
-
return {
|
|
3575
|
-
_meta: {
|
|
3576
|
-
configOptions: result.configOptions
|
|
3577
|
-
}
|
|
3578
|
-
};
|
|
4295
|
+
if (meta?.taskRunId) {
|
|
4296
|
+
await this.client.extNotification("_posthog/sdk_session", {
|
|
4297
|
+
taskRunId: meta.taskRunId,
|
|
4298
|
+
sessionId,
|
|
4299
|
+
adapter: "claude"
|
|
4300
|
+
});
|
|
3579
4301
|
}
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
4302
|
+
const settingsModel = settingsManager.getSettings().model;
|
|
4303
|
+
const modelOptions = await this.getModelConfigOptions();
|
|
4304
|
+
const resolvedModelId = settingsModel || modelOptions.currentModelId;
|
|
4305
|
+
session.modelId = resolvedModelId;
|
|
4306
|
+
if (!isResume) {
|
|
4307
|
+
const resolvedSdkModel = toSdkModelId(resolvedModelId);
|
|
4308
|
+
if (resolvedSdkModel !== DEFAULT_MODEL) {
|
|
4309
|
+
await this.session.query.setModel(resolvedSdkModel);
|
|
4310
|
+
}
|
|
4311
|
+
}
|
|
4312
|
+
const availableModes2 = getAvailableModes();
|
|
4313
|
+
const modes = {
|
|
4314
|
+
currentModeId: permissionMode,
|
|
4315
|
+
availableModes: availableModes2.map((mode) => ({
|
|
4316
|
+
id: mode.id,
|
|
4317
|
+
name: mode.name,
|
|
4318
|
+
description: mode.description ?? void 0
|
|
4319
|
+
}))
|
|
4320
|
+
};
|
|
4321
|
+
const models = {
|
|
4322
|
+
currentModelId: resolvedModelId,
|
|
4323
|
+
availableModels: modelOptions.options.map(
|
|
4324
|
+
(opt) => ({
|
|
4325
|
+
modelId: opt.value,
|
|
4326
|
+
name: opt.name,
|
|
4327
|
+
description: opt.description
|
|
4328
|
+
})
|
|
4329
|
+
)
|
|
3591
4330
|
};
|
|
3592
|
-
this.
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
systemPrompt: config.systemPrompt,
|
|
3605
|
-
userProvidedOptions: config.userProvidedOptions,
|
|
3606
|
-
sessionId: config.sessionId,
|
|
3607
|
-
isResume: config.isResume,
|
|
3608
|
-
additionalDirectories: config.additionalDirectories,
|
|
3609
|
-
onModeChange: this.createOnModeChange(config.sessionId),
|
|
3610
|
-
onProcessSpawned: this.options?.onProcessSpawned,
|
|
3611
|
-
onProcessExited: this.options?.onProcessExited
|
|
3612
|
-
});
|
|
3613
|
-
const q = query({ prompt: input, options });
|
|
3614
|
-
const abortController = options.abortController;
|
|
3615
|
-
const session = this.createSession(
|
|
3616
|
-
config.sessionId,
|
|
3617
|
-
q,
|
|
3618
|
-
input,
|
|
3619
|
-
config.permissionMode,
|
|
3620
|
-
config.cwd,
|
|
3621
|
-
abortController
|
|
4331
|
+
const configOptions = this.buildConfigOptions(permissionMode, modelOptions);
|
|
4332
|
+
session.configOptions = configOptions;
|
|
4333
|
+
if (!creationOpts.skipBackgroundFetches) {
|
|
4334
|
+
this.deferBackgroundFetches(q);
|
|
4335
|
+
}
|
|
4336
|
+
this.logger.info(
|
|
4337
|
+
isResume ? "Session resumed successfully" : "Session created successfully",
|
|
4338
|
+
{
|
|
4339
|
+
sessionId,
|
|
4340
|
+
taskId,
|
|
4341
|
+
taskRunId: meta?.taskRunId
|
|
4342
|
+
}
|
|
3622
4343
|
);
|
|
3623
|
-
return {
|
|
4344
|
+
return { sessionId, modes, models, configOptions };
|
|
3624
4345
|
}
|
|
3625
4346
|
createCanUseTool(sessionId) {
|
|
3626
|
-
return async (toolName, toolInput, { suggestions, toolUseID }) => canUseTool({
|
|
4347
|
+
return async (toolName, toolInput, { suggestions, toolUseID, signal }) => canUseTool({
|
|
3627
4348
|
session: this.session,
|
|
3628
4349
|
toolName,
|
|
3629
4350
|
toolInput,
|
|
3630
4351
|
toolUseID,
|
|
3631
4352
|
suggestions,
|
|
4353
|
+
signal,
|
|
3632
4354
|
client: this.client,
|
|
3633
4355
|
sessionId,
|
|
3634
4356
|
fileContentCache: this.fileContentCache,
|
|
3635
4357
|
logger: this.logger,
|
|
3636
|
-
|
|
4358
|
+
updateConfigOption: (configId, value) => this.updateConfigOption(configId, value)
|
|
3637
4359
|
});
|
|
3638
4360
|
}
|
|
3639
|
-
createOnModeChange(
|
|
4361
|
+
createOnModeChange() {
|
|
3640
4362
|
return async (newMode) => {
|
|
3641
4363
|
if (this.session) {
|
|
3642
4364
|
this.session.permissionMode = newMode;
|
|
3643
4365
|
}
|
|
3644
|
-
await this.
|
|
4366
|
+
await this.updateConfigOption("mode", newMode);
|
|
3645
4367
|
};
|
|
3646
4368
|
}
|
|
3647
|
-
|
|
3648
|
-
const options = [];
|
|
4369
|
+
buildConfigOptions(currentModeId, modelOptions) {
|
|
3649
4370
|
const modeOptions = getAvailableModes().map((mode) => ({
|
|
3650
4371
|
value: mode.id,
|
|
3651
4372
|
name: mode.name,
|
|
3652
4373
|
description: mode.description ?? void 0
|
|
3653
4374
|
}));
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
return options;
|
|
4375
|
+
return [
|
|
4376
|
+
{
|
|
4377
|
+
id: "mode",
|
|
4378
|
+
name: "Approval Preset",
|
|
4379
|
+
type: "select",
|
|
4380
|
+
currentValue: currentModeId,
|
|
4381
|
+
options: modeOptions,
|
|
4382
|
+
category: "mode",
|
|
4383
|
+
description: "Choose an approval and sandboxing preset for your session"
|
|
4384
|
+
},
|
|
4385
|
+
{
|
|
4386
|
+
id: "model",
|
|
4387
|
+
name: "Model",
|
|
4388
|
+
type: "select",
|
|
4389
|
+
currentValue: modelOptions.currentModelId,
|
|
4390
|
+
options: modelOptions.options,
|
|
4391
|
+
category: "model",
|
|
4392
|
+
description: "Choose which model Claude should use"
|
|
4393
|
+
}
|
|
4394
|
+
];
|
|
3675
4395
|
}
|
|
3676
|
-
async
|
|
3677
|
-
const
|
|
3678
|
-
const serialized = JSON.stringify(configOptions);
|
|
3679
|
-
if (this.lastSentConfigOptions && JSON.stringify(this.lastSentConfigOptions) === serialized) {
|
|
3680
|
-
return;
|
|
3681
|
-
}
|
|
3682
|
-
this.lastSentConfigOptions = configOptions;
|
|
4396
|
+
async sendAvailableCommandsUpdate() {
|
|
4397
|
+
const commands = await this.session.query.supportedCommands();
|
|
3683
4398
|
await this.client.sessionUpdate({
|
|
3684
|
-
sessionId:
|
|
4399
|
+
sessionId: this.sessionId,
|
|
3685
4400
|
update: {
|
|
3686
|
-
sessionUpdate: "
|
|
3687
|
-
|
|
4401
|
+
sessionUpdate: "available_commands_update",
|
|
4402
|
+
availableCommands: getAvailableSlashCommands(commands)
|
|
3688
4403
|
}
|
|
3689
4404
|
});
|
|
3690
4405
|
}
|
|
3691
|
-
|
|
3692
|
-
const backupExists = fs2.existsSync(
|
|
3693
|
-
path3.resolve(os3.homedir(), ".claude.json.backup")
|
|
3694
|
-
);
|
|
3695
|
-
const configExists = fs2.existsSync(
|
|
3696
|
-
path3.resolve(os3.homedir(), ".claude.json")
|
|
3697
|
-
);
|
|
3698
|
-
if (backupExists && !configExists) {
|
|
3699
|
-
throw RequestError2.authRequired();
|
|
3700
|
-
}
|
|
3701
|
-
}
|
|
3702
|
-
async trySetModel(q, modelId) {
|
|
3703
|
-
try {
|
|
3704
|
-
await this.setModelWithFallback(q, modelId);
|
|
3705
|
-
} catch (err) {
|
|
3706
|
-
this.logger.warn("Failed to set model", { modelId, error: err });
|
|
3707
|
-
}
|
|
3708
|
-
}
|
|
3709
|
-
async setModelWithFallback(q, modelId) {
|
|
3710
|
-
const sdkModelId = toSdkModelId(modelId);
|
|
4406
|
+
async replaySessionHistory(sessionId) {
|
|
3711
4407
|
try {
|
|
3712
|
-
await
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
4408
|
+
const messages = await getSessionMessages(sessionId, {
|
|
4409
|
+
dir: this.session.cwd
|
|
4410
|
+
});
|
|
4411
|
+
const replayContext = {
|
|
4412
|
+
session: this.session,
|
|
4413
|
+
sessionId,
|
|
4414
|
+
client: this.client,
|
|
4415
|
+
toolUseCache: this.toolUseCache,
|
|
4416
|
+
fileContentCache: this.fileContentCache,
|
|
4417
|
+
logger: this.logger,
|
|
4418
|
+
registerHooks: false
|
|
4419
|
+
};
|
|
4420
|
+
for (const msg of messages) {
|
|
4421
|
+
const sdkMessage = {
|
|
4422
|
+
type: msg.type,
|
|
4423
|
+
message: msg.message,
|
|
4424
|
+
parent_tool_use_id: msg.parent_tool_use_id
|
|
4425
|
+
};
|
|
4426
|
+
await handleUserAssistantMessage(
|
|
4427
|
+
sdkMessage,
|
|
4428
|
+
replayContext
|
|
4429
|
+
);
|
|
3716
4430
|
}
|
|
3717
|
-
|
|
4431
|
+
} catch (err) {
|
|
4432
|
+
this.logger.warn("Failed to replay session history", {
|
|
4433
|
+
sessionId,
|
|
4434
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4435
|
+
});
|
|
3718
4436
|
}
|
|
3719
4437
|
}
|
|
4438
|
+
// ================================
|
|
4439
|
+
// EXTENSION METHODS
|
|
4440
|
+
// ================================
|
|
3720
4441
|
/**
|
|
3721
4442
|
* Fire-and-forget: fetch slash commands and MCP tool metadata in parallel.
|
|
3722
4443
|
* Both populate caches used later — neither is needed to return configOptions.
|
|
3723
4444
|
*/
|
|
3724
|
-
deferBackgroundFetches(q
|
|
4445
|
+
deferBackgroundFetches(q) {
|
|
3725
4446
|
Promise.all([
|
|
3726
|
-
|
|
4447
|
+
new Promise((resolve4) => setTimeout(resolve4, 10)).then(
|
|
4448
|
+
() => this.sendAvailableCommandsUpdate()
|
|
4449
|
+
),
|
|
3727
4450
|
fetchMcpToolMetadata(q, this.logger)
|
|
3728
|
-
]).
|
|
3729
|
-
this.
|
|
3730
|
-
|
|
3731
|
-
this.logger.warn("Failed to fetch deferred session data", { err });
|
|
3732
|
-
});
|
|
3733
|
-
}
|
|
3734
|
-
sendAvailableCommandsUpdate(sessionId, availableCommands) {
|
|
3735
|
-
setTimeout(() => {
|
|
3736
|
-
this.client.sessionUpdate({
|
|
3737
|
-
sessionId,
|
|
3738
|
-
update: {
|
|
3739
|
-
sessionUpdate: "available_commands_update",
|
|
3740
|
-
availableCommands
|
|
3741
|
-
}
|
|
3742
|
-
});
|
|
3743
|
-
}, 0);
|
|
4451
|
+
]).catch(
|
|
4452
|
+
(err) => this.logger.error("Background fetch failed", { error: err })
|
|
4453
|
+
);
|
|
3744
4454
|
}
|
|
3745
4455
|
async broadcastUserMessage(params) {
|
|
3746
4456
|
for (const chunk of params.prompt) {
|
|
@@ -3755,71 +4465,6 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3755
4465
|
this.appendNotification(params.sessionId, notification);
|
|
3756
4466
|
}
|
|
3757
4467
|
}
|
|
3758
|
-
async processMessages(sessionId) {
|
|
3759
|
-
const context = {
|
|
3760
|
-
session: this.session,
|
|
3761
|
-
sessionId,
|
|
3762
|
-
client: this.client,
|
|
3763
|
-
toolUseCache: this.toolUseCache,
|
|
3764
|
-
fileContentCache: this.fileContentCache,
|
|
3765
|
-
logger: this.logger
|
|
3766
|
-
};
|
|
3767
|
-
while (true) {
|
|
3768
|
-
const { value: message, done } = await this.session.query.next();
|
|
3769
|
-
if (done || !message) {
|
|
3770
|
-
return this.handleSessionEnd();
|
|
3771
|
-
}
|
|
3772
|
-
const response = await this.handleMessage(message, context);
|
|
3773
|
-
if (response) {
|
|
3774
|
-
return response;
|
|
3775
|
-
}
|
|
3776
|
-
}
|
|
3777
|
-
}
|
|
3778
|
-
handleSessionEnd() {
|
|
3779
|
-
if (this.session.cancelled) {
|
|
3780
|
-
return {
|
|
3781
|
-
stopReason: "cancelled",
|
|
3782
|
-
_meta: this.session.interruptReason ? { interruptReason: this.session.interruptReason } : void 0
|
|
3783
|
-
};
|
|
3784
|
-
}
|
|
3785
|
-
throw new Error("Session did not end in result");
|
|
3786
|
-
}
|
|
3787
|
-
async handleMessage(message, context) {
|
|
3788
|
-
switch (message.type) {
|
|
3789
|
-
case "system":
|
|
3790
|
-
await handleSystemMessage(message, context);
|
|
3791
|
-
return null;
|
|
3792
|
-
case "result": {
|
|
3793
|
-
const result = handleResultMessage(message, context);
|
|
3794
|
-
if (result.error) throw result.error;
|
|
3795
|
-
if (result.shouldStop) {
|
|
3796
|
-
return {
|
|
3797
|
-
stopReason: result.stopReason
|
|
3798
|
-
};
|
|
3799
|
-
}
|
|
3800
|
-
return null;
|
|
3801
|
-
}
|
|
3802
|
-
case "stream_event":
|
|
3803
|
-
await handleStreamEvent(message, context);
|
|
3804
|
-
return null;
|
|
3805
|
-
case "user":
|
|
3806
|
-
case "assistant": {
|
|
3807
|
-
const result = await handleUserAssistantMessage(message, context);
|
|
3808
|
-
if (result.error) throw result.error;
|
|
3809
|
-
if (result.shouldStop) {
|
|
3810
|
-
return { stopReason: "end_turn" };
|
|
3811
|
-
}
|
|
3812
|
-
return null;
|
|
3813
|
-
}
|
|
3814
|
-
case "tool_progress":
|
|
3815
|
-
case "auth_status":
|
|
3816
|
-
case "tool_use_summary":
|
|
3817
|
-
return null;
|
|
3818
|
-
default:
|
|
3819
|
-
unreachable(message, this.logger);
|
|
3820
|
-
return null;
|
|
3821
|
-
}
|
|
3822
|
-
}
|
|
3823
4468
|
};
|
|
3824
4469
|
|
|
3825
4470
|
// src/adapters/codex/spawn.ts
|
|
@@ -4446,8 +5091,8 @@ var PostHogAPIClient = class {
|
|
|
4446
5091
|
};
|
|
4447
5092
|
|
|
4448
5093
|
// src/session-log-writer.ts
|
|
4449
|
-
import
|
|
4450
|
-
import
|
|
5094
|
+
import fs4 from "fs";
|
|
5095
|
+
import path5 from "path";
|
|
4451
5096
|
var SessionLogWriter = class _SessionLogWriter {
|
|
4452
5097
|
static FLUSH_DEBOUNCE_MS = 500;
|
|
4453
5098
|
static FLUSH_MAX_INTERVAL_MS = 5e3;
|
|
@@ -4485,13 +5130,13 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4485
5130
|
this.sessions.set(sessionId, { context });
|
|
4486
5131
|
this.lastFlushAttemptTime.set(sessionId, Date.now());
|
|
4487
5132
|
if (this.localCachePath) {
|
|
4488
|
-
const sessionDir =
|
|
5133
|
+
const sessionDir = path5.join(
|
|
4489
5134
|
this.localCachePath,
|
|
4490
5135
|
"sessions",
|
|
4491
5136
|
context.runId
|
|
4492
5137
|
);
|
|
4493
5138
|
try {
|
|
4494
|
-
|
|
5139
|
+
fs4.mkdirSync(sessionDir, { recursive: true });
|
|
4495
5140
|
} catch (error) {
|
|
4496
5141
|
this.logger.warn("Failed to create local cache directory", {
|
|
4497
5142
|
sessionDir,
|
|
@@ -4688,14 +5333,14 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4688
5333
|
if (!this.localCachePath) return;
|
|
4689
5334
|
const session = this.sessions.get(sessionId);
|
|
4690
5335
|
if (!session) return;
|
|
4691
|
-
const logPath =
|
|
5336
|
+
const logPath = path5.join(
|
|
4692
5337
|
this.localCachePath,
|
|
4693
5338
|
"sessions",
|
|
4694
5339
|
session.context.runId,
|
|
4695
5340
|
"logs.ndjson"
|
|
4696
5341
|
);
|
|
4697
5342
|
try {
|
|
4698
|
-
|
|
5343
|
+
fs4.appendFileSync(logPath, `${JSON.stringify(entry)}
|
|
4699
5344
|
`);
|
|
4700
5345
|
} catch (error) {
|
|
4701
5346
|
this.logger.warn("Failed to write to local cache", {
|
|
@@ -4709,8 +5354,8 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4709
5354
|
};
|
|
4710
5355
|
|
|
4711
5356
|
// ../git/dist/queries.js
|
|
4712
|
-
import * as
|
|
4713
|
-
import * as
|
|
5357
|
+
import * as fs6 from "fs/promises";
|
|
5358
|
+
import * as path7 from "path";
|
|
4714
5359
|
|
|
4715
5360
|
// ../../node_modules/simple-git/dist/esm/index.js
|
|
4716
5361
|
var import_file_exists = __toESM(require_dist(), 1);
|
|
@@ -4719,7 +5364,7 @@ var import_promise_deferred = __toESM(require_dist2(), 1);
|
|
|
4719
5364
|
var import_promise_deferred2 = __toESM(require_dist2(), 1);
|
|
4720
5365
|
import { Buffer as Buffer2 } from "buffer";
|
|
4721
5366
|
import { spawn as spawn3 } from "child_process";
|
|
4722
|
-
import { normalize } from "path";
|
|
5367
|
+
import { normalize as normalize2 } from "path";
|
|
4723
5368
|
import { EventEmitter } from "events";
|
|
4724
5369
|
var __defProp2 = Object.defineProperty;
|
|
4725
5370
|
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
@@ -4749,8 +5394,8 @@ function pathspec(...paths) {
|
|
|
4749
5394
|
cache.set(key, paths);
|
|
4750
5395
|
return key;
|
|
4751
5396
|
}
|
|
4752
|
-
function isPathSpec(
|
|
4753
|
-
return
|
|
5397
|
+
function isPathSpec(path9) {
|
|
5398
|
+
return path9 instanceof String && cache.has(path9);
|
|
4754
5399
|
}
|
|
4755
5400
|
function toPaths(pathSpec) {
|
|
4756
5401
|
return cache.get(pathSpec) || [];
|
|
@@ -4839,8 +5484,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
4839
5484
|
function forEachLineWithContent(input, callback) {
|
|
4840
5485
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
4841
5486
|
}
|
|
4842
|
-
function folderExists(
|
|
4843
|
-
return (0, import_file_exists.exists)(
|
|
5487
|
+
function folderExists(path9) {
|
|
5488
|
+
return (0, import_file_exists.exists)(path9, import_file_exists.FOLDER);
|
|
4844
5489
|
}
|
|
4845
5490
|
function append(target, item) {
|
|
4846
5491
|
if (Array.isArray(target)) {
|
|
@@ -5244,8 +5889,8 @@ function checkIsRepoRootTask() {
|
|
|
5244
5889
|
commands,
|
|
5245
5890
|
format: "utf-8",
|
|
5246
5891
|
onError,
|
|
5247
|
-
parser(
|
|
5248
|
-
return /^\.(git)?$/.test(
|
|
5892
|
+
parser(path9) {
|
|
5893
|
+
return /^\.(git)?$/.test(path9.trim());
|
|
5249
5894
|
}
|
|
5250
5895
|
};
|
|
5251
5896
|
}
|
|
@@ -5679,11 +6324,11 @@ function parseGrep(grep) {
|
|
|
5679
6324
|
const paths = /* @__PURE__ */ new Set();
|
|
5680
6325
|
const results = {};
|
|
5681
6326
|
forEachLineWithContent(grep, (input) => {
|
|
5682
|
-
const [
|
|
5683
|
-
paths.add(
|
|
5684
|
-
(results[
|
|
6327
|
+
const [path9, line, preview] = input.split(NULL);
|
|
6328
|
+
paths.add(path9);
|
|
6329
|
+
(results[path9] = results[path9] || []).push({
|
|
5685
6330
|
line: asNumber(line),
|
|
5686
|
-
path:
|
|
6331
|
+
path: path9,
|
|
5687
6332
|
preview
|
|
5688
6333
|
});
|
|
5689
6334
|
});
|
|
@@ -6448,14 +7093,14 @@ var init_hash_object = __esm({
|
|
|
6448
7093
|
init_task();
|
|
6449
7094
|
}
|
|
6450
7095
|
});
|
|
6451
|
-
function parseInit(bare,
|
|
7096
|
+
function parseInit(bare, path9, text2) {
|
|
6452
7097
|
const response = String(text2).trim();
|
|
6453
7098
|
let result;
|
|
6454
7099
|
if (result = initResponseRegex.exec(response)) {
|
|
6455
|
-
return new InitSummary(bare,
|
|
7100
|
+
return new InitSummary(bare, path9, false, result[1]);
|
|
6456
7101
|
}
|
|
6457
7102
|
if (result = reInitResponseRegex.exec(response)) {
|
|
6458
|
-
return new InitSummary(bare,
|
|
7103
|
+
return new InitSummary(bare, path9, true, result[1]);
|
|
6459
7104
|
}
|
|
6460
7105
|
let gitDir = "";
|
|
6461
7106
|
const tokens = response.split(" ");
|
|
@@ -6466,7 +7111,7 @@ function parseInit(bare, path8, text2) {
|
|
|
6466
7111
|
break;
|
|
6467
7112
|
}
|
|
6468
7113
|
}
|
|
6469
|
-
return new InitSummary(bare,
|
|
7114
|
+
return new InitSummary(bare, path9, /^re/i.test(response), gitDir);
|
|
6470
7115
|
}
|
|
6471
7116
|
var InitSummary;
|
|
6472
7117
|
var initResponseRegex;
|
|
@@ -6475,9 +7120,9 @@ var init_InitSummary = __esm({
|
|
|
6475
7120
|
"src/lib/responses/InitSummary.ts"() {
|
|
6476
7121
|
"use strict";
|
|
6477
7122
|
InitSummary = class {
|
|
6478
|
-
constructor(bare,
|
|
7123
|
+
constructor(bare, path9, existing, gitDir) {
|
|
6479
7124
|
this.bare = bare;
|
|
6480
|
-
this.path =
|
|
7125
|
+
this.path = path9;
|
|
6481
7126
|
this.existing = existing;
|
|
6482
7127
|
this.gitDir = gitDir;
|
|
6483
7128
|
}
|
|
@@ -6489,7 +7134,7 @@ var init_InitSummary = __esm({
|
|
|
6489
7134
|
function hasBareCommand(command) {
|
|
6490
7135
|
return command.includes(bareCommand);
|
|
6491
7136
|
}
|
|
6492
|
-
function initTask(bare = false,
|
|
7137
|
+
function initTask(bare = false, path9, customArgs) {
|
|
6493
7138
|
const commands = ["init", ...customArgs];
|
|
6494
7139
|
if (bare && !hasBareCommand(commands)) {
|
|
6495
7140
|
commands.splice(1, 0, bareCommand);
|
|
@@ -6498,7 +7143,7 @@ function initTask(bare = false, path8, customArgs) {
|
|
|
6498
7143
|
commands,
|
|
6499
7144
|
format: "utf-8",
|
|
6500
7145
|
parser(text2) {
|
|
6501
|
-
return parseInit(commands.includes("--bare"),
|
|
7146
|
+
return parseInit(commands.includes("--bare"), path9, text2);
|
|
6502
7147
|
}
|
|
6503
7148
|
};
|
|
6504
7149
|
}
|
|
@@ -7314,12 +7959,12 @@ var init_FileStatusSummary = __esm({
|
|
|
7314
7959
|
"use strict";
|
|
7315
7960
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
7316
7961
|
FileStatusSummary = class {
|
|
7317
|
-
constructor(
|
|
7318
|
-
this.path =
|
|
7962
|
+
constructor(path9, index, working_dir) {
|
|
7963
|
+
this.path = path9;
|
|
7319
7964
|
this.index = index;
|
|
7320
7965
|
this.working_dir = working_dir;
|
|
7321
7966
|
if (index === "R" || working_dir === "R") {
|
|
7322
|
-
const detail = fromPathRegex.exec(
|
|
7967
|
+
const detail = fromPathRegex.exec(path9) || [null, path9, path9];
|
|
7323
7968
|
this.from = detail[2] || "";
|
|
7324
7969
|
this.path = detail[1] || "";
|
|
7325
7970
|
}
|
|
@@ -7350,14 +7995,14 @@ function splitLine(result, lineStr) {
|
|
|
7350
7995
|
default:
|
|
7351
7996
|
return;
|
|
7352
7997
|
}
|
|
7353
|
-
function data(index, workingDir,
|
|
7998
|
+
function data(index, workingDir, path9) {
|
|
7354
7999
|
const raw = `${index}${workingDir}`;
|
|
7355
8000
|
const handler = parsers6.get(raw);
|
|
7356
8001
|
if (handler) {
|
|
7357
|
-
handler(result,
|
|
8002
|
+
handler(result, path9);
|
|
7358
8003
|
}
|
|
7359
8004
|
if (raw !== "##" && raw !== "!!") {
|
|
7360
|
-
result.files.push(new FileStatusSummary(
|
|
8005
|
+
result.files.push(new FileStatusSummary(path9, index, workingDir));
|
|
7361
8006
|
}
|
|
7362
8007
|
}
|
|
7363
8008
|
}
|
|
@@ -7670,9 +8315,9 @@ var init_simple_git_api = __esm({
|
|
|
7670
8315
|
next
|
|
7671
8316
|
);
|
|
7672
8317
|
}
|
|
7673
|
-
hashObject(
|
|
8318
|
+
hashObject(path9, write) {
|
|
7674
8319
|
return this._runTask(
|
|
7675
|
-
hashObjectTask(
|
|
8320
|
+
hashObjectTask(path9, write === true),
|
|
7676
8321
|
trailingFunctionArgument(arguments)
|
|
7677
8322
|
);
|
|
7678
8323
|
}
|
|
@@ -8025,8 +8670,8 @@ var init_branch = __esm({
|
|
|
8025
8670
|
}
|
|
8026
8671
|
});
|
|
8027
8672
|
function toPath(input) {
|
|
8028
|
-
const
|
|
8029
|
-
return
|
|
8673
|
+
const path9 = input.trim().replace(/^["']|["']$/g, "");
|
|
8674
|
+
return path9 && normalize2(path9);
|
|
8030
8675
|
}
|
|
8031
8676
|
var parseCheckIgnore;
|
|
8032
8677
|
var init_CheckIgnore = __esm({
|
|
@@ -8340,8 +8985,8 @@ __export(sub_module_exports, {
|
|
|
8340
8985
|
subModuleTask: () => subModuleTask,
|
|
8341
8986
|
updateSubModuleTask: () => updateSubModuleTask
|
|
8342
8987
|
});
|
|
8343
|
-
function addSubModuleTask(repo,
|
|
8344
|
-
return subModuleTask(["add", repo,
|
|
8988
|
+
function addSubModuleTask(repo, path9) {
|
|
8989
|
+
return subModuleTask(["add", repo, path9]);
|
|
8345
8990
|
}
|
|
8346
8991
|
function initSubModuleTask(customArgs) {
|
|
8347
8992
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -8671,8 +9316,8 @@ var require_git = __commonJS2({
|
|
|
8671
9316
|
}
|
|
8672
9317
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
8673
9318
|
};
|
|
8674
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
8675
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
9319
|
+
Git2.prototype.submoduleAdd = function(repo, path9, then) {
|
|
9320
|
+
return this._runTask(addSubModuleTask2(repo, path9), trailingFunctionArgument2(arguments));
|
|
8676
9321
|
};
|
|
8677
9322
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
8678
9323
|
return this._runTask(
|
|
@@ -9273,22 +9918,22 @@ function createGitClient(baseDir, options) {
|
|
|
9273
9918
|
|
|
9274
9919
|
// ../git/dist/lock-detector.js
|
|
9275
9920
|
import { execFile } from "child_process";
|
|
9276
|
-
import
|
|
9277
|
-
import
|
|
9921
|
+
import fs5 from "fs/promises";
|
|
9922
|
+
import path6 from "path";
|
|
9278
9923
|
import { promisify } from "util";
|
|
9279
9924
|
var execFileAsync = promisify(execFile);
|
|
9280
9925
|
async function getIndexLockPath(repoPath) {
|
|
9281
9926
|
try {
|
|
9282
9927
|
const { stdout } = await execFileAsync("git", ["rev-parse", "--git-path", "index.lock"], { cwd: repoPath });
|
|
9283
|
-
return
|
|
9928
|
+
return path6.resolve(repoPath, stdout.trim());
|
|
9284
9929
|
} catch {
|
|
9285
|
-
return
|
|
9930
|
+
return path6.join(repoPath, ".git", "index.lock");
|
|
9286
9931
|
}
|
|
9287
9932
|
}
|
|
9288
9933
|
async function getLockInfo(repoPath) {
|
|
9289
9934
|
const lockPath = await getIndexLockPath(repoPath);
|
|
9290
9935
|
try {
|
|
9291
|
-
const stat = await
|
|
9936
|
+
const stat = await fs5.stat(lockPath);
|
|
9292
9937
|
return {
|
|
9293
9938
|
path: lockPath,
|
|
9294
9939
|
ageMs: Date.now() - stat.mtimeMs
|
|
@@ -9299,7 +9944,7 @@ async function getLockInfo(repoPath) {
|
|
|
9299
9944
|
}
|
|
9300
9945
|
async function removeLock(repoPath) {
|
|
9301
9946
|
const lockPath = await getIndexLockPath(repoPath);
|
|
9302
|
-
await
|
|
9947
|
+
await fs5.rm(lockPath, { force: true });
|
|
9303
9948
|
}
|
|
9304
9949
|
async function isLocked(repoPath) {
|
|
9305
9950
|
return await getLockInfo(repoPath) !== null;
|
|
@@ -9466,7 +10111,7 @@ async function getHeadSha(baseDir, options) {
|
|
|
9466
10111
|
|
|
9467
10112
|
// src/sagas/apply-snapshot-saga.ts
|
|
9468
10113
|
import { mkdir as mkdir3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
9469
|
-
import { join as
|
|
10114
|
+
import { join as join6 } from "path";
|
|
9470
10115
|
|
|
9471
10116
|
// ../shared/dist/index.js
|
|
9472
10117
|
var consoleLogger = {
|
|
@@ -9597,8 +10242,8 @@ var Saga = class {
|
|
|
9597
10242
|
|
|
9598
10243
|
// ../git/dist/sagas/tree.js
|
|
9599
10244
|
import { existsSync as existsSync4 } from "fs";
|
|
9600
|
-
import * as
|
|
9601
|
-
import * as
|
|
10245
|
+
import * as fs7 from "fs/promises";
|
|
10246
|
+
import * as path8 from "path";
|
|
9602
10247
|
import * as tar from "tar";
|
|
9603
10248
|
|
|
9604
10249
|
// ../git/dist/git-saga.js
|
|
@@ -9624,14 +10269,14 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9624
10269
|
tempIndexPath = null;
|
|
9625
10270
|
async executeGitOperations(input) {
|
|
9626
10271
|
const { baseDir, lastTreeHash, archivePath, signal } = input;
|
|
9627
|
-
const tmpDir =
|
|
10272
|
+
const tmpDir = path8.join(baseDir, ".git", "twig-tmp");
|
|
9628
10273
|
await this.step({
|
|
9629
10274
|
name: "create_tmp_dir",
|
|
9630
|
-
execute: () =>
|
|
10275
|
+
execute: () => fs7.mkdir(tmpDir, { recursive: true }),
|
|
9631
10276
|
rollback: async () => {
|
|
9632
10277
|
}
|
|
9633
10278
|
});
|
|
9634
|
-
this.tempIndexPath =
|
|
10279
|
+
this.tempIndexPath = path8.join(tmpDir, `index-${Date.now()}`);
|
|
9635
10280
|
const tempIndexGit = this.git.env({
|
|
9636
10281
|
...process.env,
|
|
9637
10282
|
GIT_INDEX_FILE: this.tempIndexPath
|
|
@@ -9641,7 +10286,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9641
10286
|
execute: () => tempIndexGit.raw(["read-tree", "HEAD"]),
|
|
9642
10287
|
rollback: async () => {
|
|
9643
10288
|
if (this.tempIndexPath) {
|
|
9644
|
-
await
|
|
10289
|
+
await fs7.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9645
10290
|
});
|
|
9646
10291
|
}
|
|
9647
10292
|
}
|
|
@@ -9650,7 +10295,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9650
10295
|
const treeHash = await this.readOnlyStep("write_tree", () => tempIndexGit.raw(["write-tree"]));
|
|
9651
10296
|
if (lastTreeHash && treeHash === lastTreeHash) {
|
|
9652
10297
|
this.log.debug("No changes since last capture", { treeHash });
|
|
9653
|
-
await
|
|
10298
|
+
await fs7.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9654
10299
|
});
|
|
9655
10300
|
return { snapshot: null, changed: false };
|
|
9656
10301
|
}
|
|
@@ -9662,7 +10307,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9662
10307
|
}
|
|
9663
10308
|
});
|
|
9664
10309
|
const changes = await this.readOnlyStep("get_changes", () => this.getChanges(this.git, baseCommit, treeHash));
|
|
9665
|
-
await
|
|
10310
|
+
await fs7.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9666
10311
|
});
|
|
9667
10312
|
const snapshot = {
|
|
9668
10313
|
treeHash,
|
|
@@ -9686,15 +10331,15 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9686
10331
|
if (filesToArchive.length === 0) {
|
|
9687
10332
|
return void 0;
|
|
9688
10333
|
}
|
|
9689
|
-
const existingFiles = filesToArchive.filter((f) => existsSync4(
|
|
10334
|
+
const existingFiles = filesToArchive.filter((f) => existsSync4(path8.join(baseDir, f)));
|
|
9690
10335
|
if (existingFiles.length === 0) {
|
|
9691
10336
|
return void 0;
|
|
9692
10337
|
}
|
|
9693
10338
|
await this.step({
|
|
9694
10339
|
name: "create_archive",
|
|
9695
10340
|
execute: async () => {
|
|
9696
|
-
const archiveDir =
|
|
9697
|
-
await
|
|
10341
|
+
const archiveDir = path8.dirname(archivePath);
|
|
10342
|
+
await fs7.mkdir(archiveDir, { recursive: true });
|
|
9698
10343
|
await tar.create({
|
|
9699
10344
|
gzip: true,
|
|
9700
10345
|
file: archivePath,
|
|
@@ -9702,7 +10347,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9702
10347
|
}, existingFiles);
|
|
9703
10348
|
},
|
|
9704
10349
|
rollback: async () => {
|
|
9705
|
-
await
|
|
10350
|
+
await fs7.rm(archivePath, { force: true }).catch(() => {
|
|
9706
10351
|
});
|
|
9707
10352
|
}
|
|
9708
10353
|
});
|
|
@@ -9801,9 +10446,9 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9801
10446
|
const filesToExtract = changes.filter((c) => c.status !== "D").map((c) => c.path);
|
|
9802
10447
|
await this.readOnlyStep("backup_existing_files", async () => {
|
|
9803
10448
|
for (const filePath of filesToExtract) {
|
|
9804
|
-
const fullPath =
|
|
10449
|
+
const fullPath = path8.join(baseDir, filePath);
|
|
9805
10450
|
try {
|
|
9806
|
-
const content = await
|
|
10451
|
+
const content = await fs7.readFile(fullPath);
|
|
9807
10452
|
this.fileBackups.set(filePath, content);
|
|
9808
10453
|
} catch {
|
|
9809
10454
|
}
|
|
@@ -9820,16 +10465,16 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9820
10465
|
},
|
|
9821
10466
|
rollback: async () => {
|
|
9822
10467
|
for (const filePath of this.extractedFiles) {
|
|
9823
|
-
const fullPath =
|
|
10468
|
+
const fullPath = path8.join(baseDir, filePath);
|
|
9824
10469
|
const backup = this.fileBackups.get(filePath);
|
|
9825
10470
|
if (backup) {
|
|
9826
|
-
const dir =
|
|
9827
|
-
await
|
|
10471
|
+
const dir = path8.dirname(fullPath);
|
|
10472
|
+
await fs7.mkdir(dir, { recursive: true }).catch(() => {
|
|
9828
10473
|
});
|
|
9829
|
-
await
|
|
10474
|
+
await fs7.writeFile(fullPath, backup).catch(() => {
|
|
9830
10475
|
});
|
|
9831
10476
|
} else {
|
|
9832
|
-
await
|
|
10477
|
+
await fs7.rm(fullPath, { force: true }).catch(() => {
|
|
9833
10478
|
});
|
|
9834
10479
|
}
|
|
9835
10480
|
}
|
|
@@ -9837,10 +10482,10 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9837
10482
|
});
|
|
9838
10483
|
}
|
|
9839
10484
|
for (const change of changes.filter((c) => c.status === "D")) {
|
|
9840
|
-
const fullPath =
|
|
10485
|
+
const fullPath = path8.join(baseDir, change.path);
|
|
9841
10486
|
const backupContent = await this.readOnlyStep(`backup_${change.path}`, async () => {
|
|
9842
10487
|
try {
|
|
9843
|
-
return await
|
|
10488
|
+
return await fs7.readFile(fullPath);
|
|
9844
10489
|
} catch {
|
|
9845
10490
|
return null;
|
|
9846
10491
|
}
|
|
@@ -9848,15 +10493,15 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9848
10493
|
await this.step({
|
|
9849
10494
|
name: `delete_${change.path}`,
|
|
9850
10495
|
execute: async () => {
|
|
9851
|
-
await
|
|
10496
|
+
await fs7.rm(fullPath, { force: true });
|
|
9852
10497
|
this.log.debug(`Deleted file: ${change.path}`);
|
|
9853
10498
|
},
|
|
9854
10499
|
rollback: async () => {
|
|
9855
10500
|
if (backupContent) {
|
|
9856
|
-
const dir =
|
|
9857
|
-
await
|
|
10501
|
+
const dir = path8.dirname(fullPath);
|
|
10502
|
+
await fs7.mkdir(dir, { recursive: true }).catch(() => {
|
|
9858
10503
|
});
|
|
9859
|
-
await
|
|
10504
|
+
await fs7.writeFile(fullPath, backupContent).catch(() => {
|
|
9860
10505
|
});
|
|
9861
10506
|
}
|
|
9862
10507
|
}
|
|
@@ -9878,7 +10523,7 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
9878
10523
|
archivePath = null;
|
|
9879
10524
|
async execute(input) {
|
|
9880
10525
|
const { snapshot, repositoryPath, apiClient, taskId, runId } = input;
|
|
9881
|
-
const tmpDir =
|
|
10526
|
+
const tmpDir = join6(repositoryPath, ".posthog", "tmp");
|
|
9882
10527
|
if (!snapshot.archiveUrl) {
|
|
9883
10528
|
throw new Error("Cannot apply snapshot: no archive URL");
|
|
9884
10529
|
}
|
|
@@ -9889,7 +10534,7 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
9889
10534
|
rollback: async () => {
|
|
9890
10535
|
}
|
|
9891
10536
|
});
|
|
9892
|
-
const archivePath =
|
|
10537
|
+
const archivePath = join6(tmpDir, `${snapshot.treeHash}.tar.gz`);
|
|
9893
10538
|
this.archivePath = archivePath;
|
|
9894
10539
|
await this.step({
|
|
9895
10540
|
name: "download_archive",
|
|
@@ -9938,7 +10583,7 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
9938
10583
|
// src/sagas/capture-tree-saga.ts
|
|
9939
10584
|
import { existsSync as existsSync5 } from "fs";
|
|
9940
10585
|
import { readFile as readFile3, rm as rm4 } from "fs/promises";
|
|
9941
|
-
import { join as
|
|
10586
|
+
import { join as join7 } from "path";
|
|
9942
10587
|
var CaptureTreeSaga2 = class extends Saga {
|
|
9943
10588
|
async execute(input) {
|
|
9944
10589
|
const {
|
|
@@ -9949,14 +10594,14 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
9949
10594
|
taskId,
|
|
9950
10595
|
runId
|
|
9951
10596
|
} = input;
|
|
9952
|
-
const tmpDir =
|
|
9953
|
-
if (existsSync5(
|
|
10597
|
+
const tmpDir = join7(repositoryPath, ".posthog", "tmp");
|
|
10598
|
+
if (existsSync5(join7(repositoryPath, ".gitmodules"))) {
|
|
9954
10599
|
this.log.warn(
|
|
9955
10600
|
"Repository has submodules - snapshot may not capture submodule state"
|
|
9956
10601
|
);
|
|
9957
10602
|
}
|
|
9958
10603
|
const shouldArchive = !!apiClient;
|
|
9959
|
-
const archivePath = shouldArchive ?
|
|
10604
|
+
const archivePath = shouldArchive ? join7(tmpDir, `tree-${Date.now()}.tar.gz`) : void 0;
|
|
9960
10605
|
const gitCaptureSaga = new CaptureTreeSaga(this.log);
|
|
9961
10606
|
const captureResult = await gitCaptureSaga.run({
|
|
9962
10607
|
baseDir: repositoryPath,
|