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