@posthog/agent 2.1.125 → 2.1.137
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/claude/conversion/tool-use-to-acp.d.ts +14 -28
- package/dist/adapters/claude/conversion/tool-use-to-acp.js +116 -164
- package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -1
- package/dist/adapters/claude/permissions/permission-options.js +33 -0
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
- package/dist/adapters/claude/tools.js +21 -11
- package/dist/adapters/claude/tools.js.map +1 -1
- package/dist/agent.js +1251 -640
- package/dist/agent.js.map +1 -1
- package/dist/posthog-api.js +2 -2
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.js +1295 -684
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +1278 -669
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +2 -2
- package/src/adapters/base-acp-agent.ts +6 -3
- package/src/adapters/claude/UPSTREAM.md +63 -0
- package/src/adapters/claude/claude-agent.ts +682 -421
- package/src/adapters/claude/conversion/sdk-to-acp.ts +249 -85
- package/src/adapters/claude/conversion/tool-use-to-acp.ts +174 -149
- package/src/adapters/claude/hooks.ts +53 -1
- package/src/adapters/claude/permissions/permission-handlers.ts +39 -21
- package/src/adapters/claude/session/commands.ts +13 -9
- package/src/adapters/claude/session/mcp-config.ts +2 -5
- package/src/adapters/claude/session/options.ts +58 -6
- package/src/adapters/claude/session/settings.ts +326 -0
- package/src/adapters/claude/tools.ts +1 -0
- package/src/adapters/claude/types.ts +38 -0
- package/src/execution-mode.ts +26 -10
- package/src/server/agent-server.test.ts +41 -1
- package/src/session-log-writer.ts +1 -36
- package/src/utils/common.ts +1 -1
|
@@ -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
|
|
3396
3873
|
}
|
|
3397
3874
|
}
|
|
3398
3875
|
},
|
|
3399
3876
|
agentInfo: {
|
|
3400
3877
|
name: package_default.name,
|
|
3401
|
-
title: "Claude
|
|
3878
|
+
title: "Claude Agent",
|
|
3402
3879
|
version: package_default.version
|
|
3403
3880
|
},
|
|
3404
|
-
authMethods: [
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
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;
|
|
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;
|
|
3409
4098
|
}
|
|
3410
|
-
|
|
3411
|
-
|
|
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);
|
|
4107
|
+
}
|
|
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;
|
|
@@ -4459,7 +5104,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4459
5104
|
lastFlushAttemptTime = /* @__PURE__ */ new Map();
|
|
4460
5105
|
retryCounts = /* @__PURE__ */ new Map();
|
|
4461
5106
|
sessions = /* @__PURE__ */ new Map();
|
|
4462
|
-
messageCounts = /* @__PURE__ */ new Map();
|
|
4463
5107
|
logger;
|
|
4464
5108
|
localCachePath;
|
|
4465
5109
|
constructor(options = {}) {
|
|
@@ -4469,19 +5113,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4469
5113
|
}
|
|
4470
5114
|
async flushAll() {
|
|
4471
5115
|
const sessionIds = [...this.sessions.keys()];
|
|
4472
|
-
const pendingCounts = sessionIds.map((id) => {
|
|
4473
|
-
const session = this.sessions.get(id);
|
|
4474
|
-
return {
|
|
4475
|
-
taskId: session?.context.taskId,
|
|
4476
|
-
runId: session?.context.runId,
|
|
4477
|
-
pending: this.pendingEntries.get(id)?.length ?? 0,
|
|
4478
|
-
messages: this.messageCounts.get(id) ?? 0
|
|
4479
|
-
};
|
|
4480
|
-
});
|
|
4481
|
-
this.logger.info("flushAll called", {
|
|
4482
|
-
sessions: sessionIds.length,
|
|
4483
|
-
pending: pendingCounts
|
|
4484
|
-
});
|
|
4485
5116
|
const flushPromises = [];
|
|
4486
5117
|
for (const sessionId of sessionIds) {
|
|
4487
5118
|
flushPromises.push(this.flush(sessionId));
|
|
@@ -4499,13 +5130,13 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4499
5130
|
this.sessions.set(sessionId, { context });
|
|
4500
5131
|
this.lastFlushAttemptTime.set(sessionId, Date.now());
|
|
4501
5132
|
if (this.localCachePath) {
|
|
4502
|
-
const sessionDir =
|
|
5133
|
+
const sessionDir = path5.join(
|
|
4503
5134
|
this.localCachePath,
|
|
4504
5135
|
"sessions",
|
|
4505
5136
|
context.runId
|
|
4506
5137
|
);
|
|
4507
5138
|
try {
|
|
4508
|
-
|
|
5139
|
+
fs4.mkdirSync(sessionDir, { recursive: true });
|
|
4509
5140
|
} catch (error) {
|
|
4510
5141
|
this.logger.warn("Failed to create local cache directory", {
|
|
4511
5142
|
sessionDir,
|
|
@@ -4525,15 +5156,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4525
5156
|
});
|
|
4526
5157
|
return;
|
|
4527
5158
|
}
|
|
4528
|
-
const count = (this.messageCounts.get(sessionId) ?? 0) + 1;
|
|
4529
|
-
this.messageCounts.set(sessionId, count);
|
|
4530
|
-
if (count % 10 === 1) {
|
|
4531
|
-
this.logger.info("Messages received", {
|
|
4532
|
-
count,
|
|
4533
|
-
taskId: session.context.taskId,
|
|
4534
|
-
runId: session.context.runId
|
|
4535
|
-
});
|
|
4536
|
-
}
|
|
4537
5159
|
try {
|
|
4538
5160
|
const message = JSON.parse(line);
|
|
4539
5161
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4582,12 +5204,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4582
5204
|
this.emitCoalescedMessage(sessionId, session);
|
|
4583
5205
|
const pending = this.pendingEntries.get(sessionId);
|
|
4584
5206
|
if (!this.posthogAPI || !pending?.length) {
|
|
4585
|
-
this.logger.info("flush: nothing to persist", {
|
|
4586
|
-
taskId: session.context.taskId,
|
|
4587
|
-
runId: session.context.runId,
|
|
4588
|
-
hasPosthogAPI: !!this.posthogAPI,
|
|
4589
|
-
pendingCount: pending?.length ?? 0
|
|
4590
|
-
});
|
|
4591
5207
|
return;
|
|
4592
5208
|
}
|
|
4593
5209
|
this.pendingEntries.delete(sessionId);
|
|
@@ -4604,11 +5220,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4604
5220
|
pending
|
|
4605
5221
|
);
|
|
4606
5222
|
this.retryCounts.set(sessionId, 0);
|
|
4607
|
-
this.logger.info("Flushed session logs", {
|
|
4608
|
-
taskId: session.context.taskId,
|
|
4609
|
-
runId: session.context.runId,
|
|
4610
|
-
entryCount: pending.length
|
|
4611
|
-
});
|
|
4612
5223
|
} catch (error) {
|
|
4613
5224
|
const retryCount = (this.retryCounts.get(sessionId) ?? 0) + 1;
|
|
4614
5225
|
this.retryCounts.set(sessionId, retryCount);
|
|
@@ -4722,14 +5333,14 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4722
5333
|
if (!this.localCachePath) return;
|
|
4723
5334
|
const session = this.sessions.get(sessionId);
|
|
4724
5335
|
if (!session) return;
|
|
4725
|
-
const logPath =
|
|
5336
|
+
const logPath = path5.join(
|
|
4726
5337
|
this.localCachePath,
|
|
4727
5338
|
"sessions",
|
|
4728
5339
|
session.context.runId,
|
|
4729
5340
|
"logs.ndjson"
|
|
4730
5341
|
);
|
|
4731
5342
|
try {
|
|
4732
|
-
|
|
5343
|
+
fs4.appendFileSync(logPath, `${JSON.stringify(entry)}
|
|
4733
5344
|
`);
|
|
4734
5345
|
} catch (error) {
|
|
4735
5346
|
this.logger.warn("Failed to write to local cache", {
|
|
@@ -4743,8 +5354,8 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4743
5354
|
};
|
|
4744
5355
|
|
|
4745
5356
|
// ../git/dist/queries.js
|
|
4746
|
-
import * as
|
|
4747
|
-
import * as
|
|
5357
|
+
import * as fs6 from "fs/promises";
|
|
5358
|
+
import * as path7 from "path";
|
|
4748
5359
|
|
|
4749
5360
|
// ../../node_modules/simple-git/dist/esm/index.js
|
|
4750
5361
|
var import_file_exists = __toESM(require_dist(), 1);
|
|
@@ -4753,7 +5364,7 @@ var import_promise_deferred = __toESM(require_dist2(), 1);
|
|
|
4753
5364
|
var import_promise_deferred2 = __toESM(require_dist2(), 1);
|
|
4754
5365
|
import { Buffer as Buffer2 } from "buffer";
|
|
4755
5366
|
import { spawn as spawn3 } from "child_process";
|
|
4756
|
-
import { normalize } from "path";
|
|
5367
|
+
import { normalize as normalize2 } from "path";
|
|
4757
5368
|
import { EventEmitter } from "events";
|
|
4758
5369
|
var __defProp2 = Object.defineProperty;
|
|
4759
5370
|
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
@@ -4783,8 +5394,8 @@ function pathspec(...paths) {
|
|
|
4783
5394
|
cache.set(key, paths);
|
|
4784
5395
|
return key;
|
|
4785
5396
|
}
|
|
4786
|
-
function isPathSpec(
|
|
4787
|
-
return
|
|
5397
|
+
function isPathSpec(path9) {
|
|
5398
|
+
return path9 instanceof String && cache.has(path9);
|
|
4788
5399
|
}
|
|
4789
5400
|
function toPaths(pathSpec) {
|
|
4790
5401
|
return cache.get(pathSpec) || [];
|
|
@@ -4873,8 +5484,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
4873
5484
|
function forEachLineWithContent(input, callback) {
|
|
4874
5485
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
4875
5486
|
}
|
|
4876
|
-
function folderExists(
|
|
4877
|
-
return (0, import_file_exists.exists)(
|
|
5487
|
+
function folderExists(path9) {
|
|
5488
|
+
return (0, import_file_exists.exists)(path9, import_file_exists.FOLDER);
|
|
4878
5489
|
}
|
|
4879
5490
|
function append(target, item) {
|
|
4880
5491
|
if (Array.isArray(target)) {
|
|
@@ -5278,8 +5889,8 @@ function checkIsRepoRootTask() {
|
|
|
5278
5889
|
commands,
|
|
5279
5890
|
format: "utf-8",
|
|
5280
5891
|
onError,
|
|
5281
|
-
parser(
|
|
5282
|
-
return /^\.(git)?$/.test(
|
|
5892
|
+
parser(path9) {
|
|
5893
|
+
return /^\.(git)?$/.test(path9.trim());
|
|
5283
5894
|
}
|
|
5284
5895
|
};
|
|
5285
5896
|
}
|
|
@@ -5713,11 +6324,11 @@ function parseGrep(grep) {
|
|
|
5713
6324
|
const paths = /* @__PURE__ */ new Set();
|
|
5714
6325
|
const results = {};
|
|
5715
6326
|
forEachLineWithContent(grep, (input) => {
|
|
5716
|
-
const [
|
|
5717
|
-
paths.add(
|
|
5718
|
-
(results[
|
|
6327
|
+
const [path9, line, preview] = input.split(NULL);
|
|
6328
|
+
paths.add(path9);
|
|
6329
|
+
(results[path9] = results[path9] || []).push({
|
|
5719
6330
|
line: asNumber(line),
|
|
5720
|
-
path:
|
|
6331
|
+
path: path9,
|
|
5721
6332
|
preview
|
|
5722
6333
|
});
|
|
5723
6334
|
});
|
|
@@ -6482,14 +7093,14 @@ var init_hash_object = __esm({
|
|
|
6482
7093
|
init_task();
|
|
6483
7094
|
}
|
|
6484
7095
|
});
|
|
6485
|
-
function parseInit(bare,
|
|
7096
|
+
function parseInit(bare, path9, text2) {
|
|
6486
7097
|
const response = String(text2).trim();
|
|
6487
7098
|
let result;
|
|
6488
7099
|
if (result = initResponseRegex.exec(response)) {
|
|
6489
|
-
return new InitSummary(bare,
|
|
7100
|
+
return new InitSummary(bare, path9, false, result[1]);
|
|
6490
7101
|
}
|
|
6491
7102
|
if (result = reInitResponseRegex.exec(response)) {
|
|
6492
|
-
return new InitSummary(bare,
|
|
7103
|
+
return new InitSummary(bare, path9, true, result[1]);
|
|
6493
7104
|
}
|
|
6494
7105
|
let gitDir = "";
|
|
6495
7106
|
const tokens = response.split(" ");
|
|
@@ -6500,7 +7111,7 @@ function parseInit(bare, path8, text2) {
|
|
|
6500
7111
|
break;
|
|
6501
7112
|
}
|
|
6502
7113
|
}
|
|
6503
|
-
return new InitSummary(bare,
|
|
7114
|
+
return new InitSummary(bare, path9, /^re/i.test(response), gitDir);
|
|
6504
7115
|
}
|
|
6505
7116
|
var InitSummary;
|
|
6506
7117
|
var initResponseRegex;
|
|
@@ -6509,9 +7120,9 @@ var init_InitSummary = __esm({
|
|
|
6509
7120
|
"src/lib/responses/InitSummary.ts"() {
|
|
6510
7121
|
"use strict";
|
|
6511
7122
|
InitSummary = class {
|
|
6512
|
-
constructor(bare,
|
|
7123
|
+
constructor(bare, path9, existing, gitDir) {
|
|
6513
7124
|
this.bare = bare;
|
|
6514
|
-
this.path =
|
|
7125
|
+
this.path = path9;
|
|
6515
7126
|
this.existing = existing;
|
|
6516
7127
|
this.gitDir = gitDir;
|
|
6517
7128
|
}
|
|
@@ -6523,7 +7134,7 @@ var init_InitSummary = __esm({
|
|
|
6523
7134
|
function hasBareCommand(command) {
|
|
6524
7135
|
return command.includes(bareCommand);
|
|
6525
7136
|
}
|
|
6526
|
-
function initTask(bare = false,
|
|
7137
|
+
function initTask(bare = false, path9, customArgs) {
|
|
6527
7138
|
const commands = ["init", ...customArgs];
|
|
6528
7139
|
if (bare && !hasBareCommand(commands)) {
|
|
6529
7140
|
commands.splice(1, 0, bareCommand);
|
|
@@ -6532,7 +7143,7 @@ function initTask(bare = false, path8, customArgs) {
|
|
|
6532
7143
|
commands,
|
|
6533
7144
|
format: "utf-8",
|
|
6534
7145
|
parser(text2) {
|
|
6535
|
-
return parseInit(commands.includes("--bare"),
|
|
7146
|
+
return parseInit(commands.includes("--bare"), path9, text2);
|
|
6536
7147
|
}
|
|
6537
7148
|
};
|
|
6538
7149
|
}
|
|
@@ -7348,12 +7959,12 @@ var init_FileStatusSummary = __esm({
|
|
|
7348
7959
|
"use strict";
|
|
7349
7960
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
7350
7961
|
FileStatusSummary = class {
|
|
7351
|
-
constructor(
|
|
7352
|
-
this.path =
|
|
7962
|
+
constructor(path9, index, working_dir) {
|
|
7963
|
+
this.path = path9;
|
|
7353
7964
|
this.index = index;
|
|
7354
7965
|
this.working_dir = working_dir;
|
|
7355
7966
|
if (index === "R" || working_dir === "R") {
|
|
7356
|
-
const detail = fromPathRegex.exec(
|
|
7967
|
+
const detail = fromPathRegex.exec(path9) || [null, path9, path9];
|
|
7357
7968
|
this.from = detail[2] || "";
|
|
7358
7969
|
this.path = detail[1] || "";
|
|
7359
7970
|
}
|
|
@@ -7384,14 +7995,14 @@ function splitLine(result, lineStr) {
|
|
|
7384
7995
|
default:
|
|
7385
7996
|
return;
|
|
7386
7997
|
}
|
|
7387
|
-
function data(index, workingDir,
|
|
7998
|
+
function data(index, workingDir, path9) {
|
|
7388
7999
|
const raw = `${index}${workingDir}`;
|
|
7389
8000
|
const handler = parsers6.get(raw);
|
|
7390
8001
|
if (handler) {
|
|
7391
|
-
handler(result,
|
|
8002
|
+
handler(result, path9);
|
|
7392
8003
|
}
|
|
7393
8004
|
if (raw !== "##" && raw !== "!!") {
|
|
7394
|
-
result.files.push(new FileStatusSummary(
|
|
8005
|
+
result.files.push(new FileStatusSummary(path9, index, workingDir));
|
|
7395
8006
|
}
|
|
7396
8007
|
}
|
|
7397
8008
|
}
|
|
@@ -7704,9 +8315,9 @@ var init_simple_git_api = __esm({
|
|
|
7704
8315
|
next
|
|
7705
8316
|
);
|
|
7706
8317
|
}
|
|
7707
|
-
hashObject(
|
|
8318
|
+
hashObject(path9, write) {
|
|
7708
8319
|
return this._runTask(
|
|
7709
|
-
hashObjectTask(
|
|
8320
|
+
hashObjectTask(path9, write === true),
|
|
7710
8321
|
trailingFunctionArgument(arguments)
|
|
7711
8322
|
);
|
|
7712
8323
|
}
|
|
@@ -8059,8 +8670,8 @@ var init_branch = __esm({
|
|
|
8059
8670
|
}
|
|
8060
8671
|
});
|
|
8061
8672
|
function toPath(input) {
|
|
8062
|
-
const
|
|
8063
|
-
return
|
|
8673
|
+
const path9 = input.trim().replace(/^["']|["']$/g, "");
|
|
8674
|
+
return path9 && normalize2(path9);
|
|
8064
8675
|
}
|
|
8065
8676
|
var parseCheckIgnore;
|
|
8066
8677
|
var init_CheckIgnore = __esm({
|
|
@@ -8374,8 +8985,8 @@ __export(sub_module_exports, {
|
|
|
8374
8985
|
subModuleTask: () => subModuleTask,
|
|
8375
8986
|
updateSubModuleTask: () => updateSubModuleTask
|
|
8376
8987
|
});
|
|
8377
|
-
function addSubModuleTask(repo,
|
|
8378
|
-
return subModuleTask(["add", repo,
|
|
8988
|
+
function addSubModuleTask(repo, path9) {
|
|
8989
|
+
return subModuleTask(["add", repo, path9]);
|
|
8379
8990
|
}
|
|
8380
8991
|
function initSubModuleTask(customArgs) {
|
|
8381
8992
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -8705,8 +9316,8 @@ var require_git = __commonJS2({
|
|
|
8705
9316
|
}
|
|
8706
9317
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
8707
9318
|
};
|
|
8708
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
8709
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
9319
|
+
Git2.prototype.submoduleAdd = function(repo, path9, then) {
|
|
9320
|
+
return this._runTask(addSubModuleTask2(repo, path9), trailingFunctionArgument2(arguments));
|
|
8710
9321
|
};
|
|
8711
9322
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
8712
9323
|
return this._runTask(
|
|
@@ -9307,22 +9918,22 @@ function createGitClient(baseDir, options) {
|
|
|
9307
9918
|
|
|
9308
9919
|
// ../git/dist/lock-detector.js
|
|
9309
9920
|
import { execFile } from "child_process";
|
|
9310
|
-
import
|
|
9311
|
-
import
|
|
9921
|
+
import fs5 from "fs/promises";
|
|
9922
|
+
import path6 from "path";
|
|
9312
9923
|
import { promisify } from "util";
|
|
9313
9924
|
var execFileAsync = promisify(execFile);
|
|
9314
9925
|
async function getIndexLockPath(repoPath) {
|
|
9315
9926
|
try {
|
|
9316
9927
|
const { stdout } = await execFileAsync("git", ["rev-parse", "--git-path", "index.lock"], { cwd: repoPath });
|
|
9317
|
-
return
|
|
9928
|
+
return path6.resolve(repoPath, stdout.trim());
|
|
9318
9929
|
} catch {
|
|
9319
|
-
return
|
|
9930
|
+
return path6.join(repoPath, ".git", "index.lock");
|
|
9320
9931
|
}
|
|
9321
9932
|
}
|
|
9322
9933
|
async function getLockInfo(repoPath) {
|
|
9323
9934
|
const lockPath = await getIndexLockPath(repoPath);
|
|
9324
9935
|
try {
|
|
9325
|
-
const stat = await
|
|
9936
|
+
const stat = await fs5.stat(lockPath);
|
|
9326
9937
|
return {
|
|
9327
9938
|
path: lockPath,
|
|
9328
9939
|
ageMs: Date.now() - stat.mtimeMs
|
|
@@ -9333,7 +9944,7 @@ async function getLockInfo(repoPath) {
|
|
|
9333
9944
|
}
|
|
9334
9945
|
async function removeLock(repoPath) {
|
|
9335
9946
|
const lockPath = await getIndexLockPath(repoPath);
|
|
9336
|
-
await
|
|
9947
|
+
await fs5.rm(lockPath, { force: true });
|
|
9337
9948
|
}
|
|
9338
9949
|
async function isLocked(repoPath) {
|
|
9339
9950
|
return await getLockInfo(repoPath) !== null;
|
|
@@ -9500,7 +10111,7 @@ async function getHeadSha(baseDir, options) {
|
|
|
9500
10111
|
|
|
9501
10112
|
// src/sagas/apply-snapshot-saga.ts
|
|
9502
10113
|
import { mkdir as mkdir3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
9503
|
-
import { join as
|
|
10114
|
+
import { join as join6 } from "path";
|
|
9504
10115
|
|
|
9505
10116
|
// ../shared/dist/index.js
|
|
9506
10117
|
var consoleLogger = {
|
|
@@ -9631,8 +10242,8 @@ var Saga = class {
|
|
|
9631
10242
|
|
|
9632
10243
|
// ../git/dist/sagas/tree.js
|
|
9633
10244
|
import { existsSync as existsSync4 } from "fs";
|
|
9634
|
-
import * as
|
|
9635
|
-
import * as
|
|
10245
|
+
import * as fs7 from "fs/promises";
|
|
10246
|
+
import * as path8 from "path";
|
|
9636
10247
|
import * as tar from "tar";
|
|
9637
10248
|
|
|
9638
10249
|
// ../git/dist/git-saga.js
|
|
@@ -9658,14 +10269,14 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9658
10269
|
tempIndexPath = null;
|
|
9659
10270
|
async executeGitOperations(input) {
|
|
9660
10271
|
const { baseDir, lastTreeHash, archivePath, signal } = input;
|
|
9661
|
-
const tmpDir =
|
|
10272
|
+
const tmpDir = path8.join(baseDir, ".git", "twig-tmp");
|
|
9662
10273
|
await this.step({
|
|
9663
10274
|
name: "create_tmp_dir",
|
|
9664
|
-
execute: () =>
|
|
10275
|
+
execute: () => fs7.mkdir(tmpDir, { recursive: true }),
|
|
9665
10276
|
rollback: async () => {
|
|
9666
10277
|
}
|
|
9667
10278
|
});
|
|
9668
|
-
this.tempIndexPath =
|
|
10279
|
+
this.tempIndexPath = path8.join(tmpDir, `index-${Date.now()}`);
|
|
9669
10280
|
const tempIndexGit = this.git.env({
|
|
9670
10281
|
...process.env,
|
|
9671
10282
|
GIT_INDEX_FILE: this.tempIndexPath
|
|
@@ -9675,7 +10286,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9675
10286
|
execute: () => tempIndexGit.raw(["read-tree", "HEAD"]),
|
|
9676
10287
|
rollback: async () => {
|
|
9677
10288
|
if (this.tempIndexPath) {
|
|
9678
|
-
await
|
|
10289
|
+
await fs7.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9679
10290
|
});
|
|
9680
10291
|
}
|
|
9681
10292
|
}
|
|
@@ -9684,7 +10295,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9684
10295
|
const treeHash = await this.readOnlyStep("write_tree", () => tempIndexGit.raw(["write-tree"]));
|
|
9685
10296
|
if (lastTreeHash && treeHash === lastTreeHash) {
|
|
9686
10297
|
this.log.debug("No changes since last capture", { treeHash });
|
|
9687
|
-
await
|
|
10298
|
+
await fs7.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9688
10299
|
});
|
|
9689
10300
|
return { snapshot: null, changed: false };
|
|
9690
10301
|
}
|
|
@@ -9696,7 +10307,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9696
10307
|
}
|
|
9697
10308
|
});
|
|
9698
10309
|
const changes = await this.readOnlyStep("get_changes", () => this.getChanges(this.git, baseCommit, treeHash));
|
|
9699
|
-
await
|
|
10310
|
+
await fs7.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9700
10311
|
});
|
|
9701
10312
|
const snapshot = {
|
|
9702
10313
|
treeHash,
|
|
@@ -9720,15 +10331,15 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9720
10331
|
if (filesToArchive.length === 0) {
|
|
9721
10332
|
return void 0;
|
|
9722
10333
|
}
|
|
9723
|
-
const existingFiles = filesToArchive.filter((f) => existsSync4(
|
|
10334
|
+
const existingFiles = filesToArchive.filter((f) => existsSync4(path8.join(baseDir, f)));
|
|
9724
10335
|
if (existingFiles.length === 0) {
|
|
9725
10336
|
return void 0;
|
|
9726
10337
|
}
|
|
9727
10338
|
await this.step({
|
|
9728
10339
|
name: "create_archive",
|
|
9729
10340
|
execute: async () => {
|
|
9730
|
-
const archiveDir =
|
|
9731
|
-
await
|
|
10341
|
+
const archiveDir = path8.dirname(archivePath);
|
|
10342
|
+
await fs7.mkdir(archiveDir, { recursive: true });
|
|
9732
10343
|
await tar.create({
|
|
9733
10344
|
gzip: true,
|
|
9734
10345
|
file: archivePath,
|
|
@@ -9736,7 +10347,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9736
10347
|
}, existingFiles);
|
|
9737
10348
|
},
|
|
9738
10349
|
rollback: async () => {
|
|
9739
|
-
await
|
|
10350
|
+
await fs7.rm(archivePath, { force: true }).catch(() => {
|
|
9740
10351
|
});
|
|
9741
10352
|
}
|
|
9742
10353
|
});
|
|
@@ -9835,9 +10446,9 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9835
10446
|
const filesToExtract = changes.filter((c) => c.status !== "D").map((c) => c.path);
|
|
9836
10447
|
await this.readOnlyStep("backup_existing_files", async () => {
|
|
9837
10448
|
for (const filePath of filesToExtract) {
|
|
9838
|
-
const fullPath =
|
|
10449
|
+
const fullPath = path8.join(baseDir, filePath);
|
|
9839
10450
|
try {
|
|
9840
|
-
const content = await
|
|
10451
|
+
const content = await fs7.readFile(fullPath);
|
|
9841
10452
|
this.fileBackups.set(filePath, content);
|
|
9842
10453
|
} catch {
|
|
9843
10454
|
}
|
|
@@ -9854,16 +10465,16 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9854
10465
|
},
|
|
9855
10466
|
rollback: async () => {
|
|
9856
10467
|
for (const filePath of this.extractedFiles) {
|
|
9857
|
-
const fullPath =
|
|
10468
|
+
const fullPath = path8.join(baseDir, filePath);
|
|
9858
10469
|
const backup = this.fileBackups.get(filePath);
|
|
9859
10470
|
if (backup) {
|
|
9860
|
-
const dir =
|
|
9861
|
-
await
|
|
10471
|
+
const dir = path8.dirname(fullPath);
|
|
10472
|
+
await fs7.mkdir(dir, { recursive: true }).catch(() => {
|
|
9862
10473
|
});
|
|
9863
|
-
await
|
|
10474
|
+
await fs7.writeFile(fullPath, backup).catch(() => {
|
|
9864
10475
|
});
|
|
9865
10476
|
} else {
|
|
9866
|
-
await
|
|
10477
|
+
await fs7.rm(fullPath, { force: true }).catch(() => {
|
|
9867
10478
|
});
|
|
9868
10479
|
}
|
|
9869
10480
|
}
|
|
@@ -9871,10 +10482,10 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9871
10482
|
});
|
|
9872
10483
|
}
|
|
9873
10484
|
for (const change of changes.filter((c) => c.status === "D")) {
|
|
9874
|
-
const fullPath =
|
|
10485
|
+
const fullPath = path8.join(baseDir, change.path);
|
|
9875
10486
|
const backupContent = await this.readOnlyStep(`backup_${change.path}`, async () => {
|
|
9876
10487
|
try {
|
|
9877
|
-
return await
|
|
10488
|
+
return await fs7.readFile(fullPath);
|
|
9878
10489
|
} catch {
|
|
9879
10490
|
return null;
|
|
9880
10491
|
}
|
|
@@ -9882,15 +10493,15 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9882
10493
|
await this.step({
|
|
9883
10494
|
name: `delete_${change.path}`,
|
|
9884
10495
|
execute: async () => {
|
|
9885
|
-
await
|
|
10496
|
+
await fs7.rm(fullPath, { force: true });
|
|
9886
10497
|
this.log.debug(`Deleted file: ${change.path}`);
|
|
9887
10498
|
},
|
|
9888
10499
|
rollback: async () => {
|
|
9889
10500
|
if (backupContent) {
|
|
9890
|
-
const dir =
|
|
9891
|
-
await
|
|
10501
|
+
const dir = path8.dirname(fullPath);
|
|
10502
|
+
await fs7.mkdir(dir, { recursive: true }).catch(() => {
|
|
9892
10503
|
});
|
|
9893
|
-
await
|
|
10504
|
+
await fs7.writeFile(fullPath, backupContent).catch(() => {
|
|
9894
10505
|
});
|
|
9895
10506
|
}
|
|
9896
10507
|
}
|
|
@@ -9912,7 +10523,7 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
9912
10523
|
archivePath = null;
|
|
9913
10524
|
async execute(input) {
|
|
9914
10525
|
const { snapshot, repositoryPath, apiClient, taskId, runId } = input;
|
|
9915
|
-
const tmpDir =
|
|
10526
|
+
const tmpDir = join6(repositoryPath, ".posthog", "tmp");
|
|
9916
10527
|
if (!snapshot.archiveUrl) {
|
|
9917
10528
|
throw new Error("Cannot apply snapshot: no archive URL");
|
|
9918
10529
|
}
|
|
@@ -9923,7 +10534,7 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
9923
10534
|
rollback: async () => {
|
|
9924
10535
|
}
|
|
9925
10536
|
});
|
|
9926
|
-
const archivePath =
|
|
10537
|
+
const archivePath = join6(tmpDir, `${snapshot.treeHash}.tar.gz`);
|
|
9927
10538
|
this.archivePath = archivePath;
|
|
9928
10539
|
await this.step({
|
|
9929
10540
|
name: "download_archive",
|
|
@@ -9972,7 +10583,7 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
9972
10583
|
// src/sagas/capture-tree-saga.ts
|
|
9973
10584
|
import { existsSync as existsSync5 } from "fs";
|
|
9974
10585
|
import { readFile as readFile3, rm as rm4 } from "fs/promises";
|
|
9975
|
-
import { join as
|
|
10586
|
+
import { join as join7 } from "path";
|
|
9976
10587
|
var CaptureTreeSaga2 = class extends Saga {
|
|
9977
10588
|
async execute(input) {
|
|
9978
10589
|
const {
|
|
@@ -9983,14 +10594,14 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
9983
10594
|
taskId,
|
|
9984
10595
|
runId
|
|
9985
10596
|
} = input;
|
|
9986
|
-
const tmpDir =
|
|
9987
|
-
if (existsSync5(
|
|
10597
|
+
const tmpDir = join7(repositoryPath, ".posthog", "tmp");
|
|
10598
|
+
if (existsSync5(join7(repositoryPath, ".gitmodules"))) {
|
|
9988
10599
|
this.log.warn(
|
|
9989
10600
|
"Repository has submodules - snapshot may not capture submodule state"
|
|
9990
10601
|
);
|
|
9991
10602
|
}
|
|
9992
10603
|
const shouldArchive = !!apiClient;
|
|
9993
|
-
const archivePath = shouldArchive ?
|
|
10604
|
+
const archivePath = shouldArchive ? join7(tmpDir, `tree-${Date.now()}.tar.gz`) : void 0;
|
|
9994
10605
|
const gitCaptureSaga = new CaptureTreeSaga(this.log);
|
|
9995
10606
|
const captureResult = await gitCaptureSaga.run({
|
|
9996
10607
|
baseDir: repositoryPath,
|