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