@mnemonik/cli 7.127.1 → 7.127.3

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.
@@ -187,8 +187,8 @@ function privateEntries(entries, user, directory) {
187
187
  function* prepareAclDirectory(state) {
188
188
  const directory = join(state, "audit-tmp");
189
189
  if (auditDirectories.has(directory)) {
190
- const stat2 = lstatSync(directory);
191
- if (!stat2.isDirectory() || stat2.isSymbolicLink())
190
+ const stat3 = lstatSync(directory);
191
+ if (!stat3.isDirectory() || stat3.isSymbolicLink())
192
192
  throw new Error("acl_permissions");
193
193
  return directory;
194
194
  }
@@ -200,8 +200,8 @@ function* prepareAclDirectory(state) {
200
200
  if (error.code !== "EEXIST")
201
201
  throw error;
202
202
  }
203
- const stat = lstatSync(directory);
204
- if (!stat.isDirectory() || stat.isSymbolicLink())
203
+ const stat2 = lstatSync(directory);
204
+ if (!stat2.isDirectory() || stat2.isSymbolicLink())
205
205
  throw new Error("acl_permissions");
206
206
  if (!auditDirectories.has(directory)) {
207
207
  currentIdentity ??= yield* readWindowsIdentity();
@@ -291,8 +291,8 @@ function ownerSids(output) {
291
291
  return owners;
292
292
  }
293
293
  function* hookPermission(path, created, session, directory = true) {
294
- const stat = lstatSync(path);
295
- if (stat.isSymbolicLink() || (directory ? !stat.isDirectory() : !stat.isFile()))
294
+ const stat2 = lstatSync(path);
295
+ if (stat2.isSymbolicLink() || (directory ? !stat2.isDirectory() : !stat2.isFile()))
296
296
  throw new Error("cache unavailable");
297
297
  const marker = join(session, ".windows-acl.json");
298
298
  let receipt = receipts.get(session);
@@ -469,8 +469,8 @@ var init_runtimeSigners = __esm({
469
469
  const result = promisify(execFile)(file, args);
470
470
  if (input === void 0)
471
471
  return result;
472
- return new Promise((resolve4, reject2) => {
473
- void result.then(resolve4, reject2);
472
+ return new Promise((resolve5, reject2) => {
473
+ void result.then(resolve5, reject2);
474
474
  result.child.stdin?.on("error", reject2);
475
475
  result.child.stdin?.end(input);
476
476
  });
@@ -503,8 +503,8 @@ var init_runtimeSigners = __esm({
503
503
  const options = hookOptions(file, args);
504
504
  if (treeListing)
505
505
  options.timeout = 3e4;
506
- return new Promise((resolve4, reject2) => {
507
- const child = execFile(file, args, options, (error, stdout2) => error ? reject2(error) : resolve4({ stdout: stdout2 }));
506
+ return new Promise((resolve5, reject2) => {
507
+ const child = execFile(file, args, options, (error, stdout2) => error ? reject2(error) : resolve5({ stdout: stdout2 }));
508
508
  if (input !== void 0) {
509
509
  child.stdin?.on("error", reject2);
510
510
  child.stdin?.end(input);
@@ -596,10 +596,10 @@ function parseIdentityFile(text) {
596
596
  async function readIdentityFile(dir, options = {}) {
597
597
  const path = join2(dir, ".mnemonik.json");
598
598
  try {
599
- const stat = await lstat(path);
600
- if (stat.isSymbolicLink())
599
+ const stat2 = await lstat(path);
600
+ if (stat2.isSymbolicLink())
601
601
  return { kind: "malformed", detail: "symlink" };
602
- if (!stat.isFile())
602
+ if (!stat2.isFile())
603
603
  return { kind: "malformed", detail: "identity path is not a regular file" };
604
604
  const text = await readFile2(path, "utf8");
605
605
  if (options.selectedRoot) {
@@ -659,10 +659,10 @@ function runGit(cwd, args) {
659
659
  }
660
660
  async function boundaryAt(path) {
661
661
  try {
662
- const stat = await lstat2(join3(path, ".git"));
663
- if (stat.isDirectory())
662
+ const stat2 = await lstat2(join3(path, ".git"));
663
+ if (stat2.isDirectory())
664
664
  return { path, kind: "directory" };
665
- if (stat.isFile())
665
+ if (stat2.isFile())
666
666
  return { path, kind: "file" };
667
667
  } catch (error) {
668
668
  const code = error.code;
@@ -879,7 +879,7 @@ var init_runtimeReader = __esm({
879
879
  throw new RuntimeError("permission");
880
880
  return v;
881
881
  };
882
- statIdentity = (stat) => [stat.dev, stat.ino, stat.mode, stat.uid, stat.size, stat.mtimeMs, stat.ctimeMs].join(":");
882
+ statIdentity = (stat2) => [stat2.dev, stat2.ino, stat2.mode, stat2.uid, stat2.size, stat2.mtimeMs, stat2.ctimeMs].join(":");
883
883
  RuntimeReader = class _RuntimeReader {
884
884
  run;
885
885
  options;
@@ -1003,14 +1003,14 @@ var init_runtimeReader = __esm({
1003
1003
  let part = parse(absolute).root;
1004
1004
  for (const name of absolute.slice(part.length).split(sep2)) {
1005
1005
  part = join4(part, name);
1006
- const stat = await lstat3(part).catch((e) => {
1006
+ const stat2 = await lstat3(part).catch((e) => {
1007
1007
  if (allowMissing && missing(e))
1008
1008
  return null;
1009
1009
  throw e;
1010
1010
  });
1011
- if (!stat)
1011
+ if (!stat2)
1012
1012
  break;
1013
- if (stat.isSymbolicLink() || (part === absolute ? directory ? !stat.isDirectory() : !stat.isFile() : !stat.isDirectory()))
1013
+ if (stat2.isSymbolicLink() || (part === absolute ? directory ? !stat2.isDirectory() : !stat2.isFile() : !stat2.isDirectory()))
1014
1014
  throw new RuntimeError("permission");
1015
1015
  if (part !== this.state && !part.startsWith(this.state + sep2))
1016
1016
  continue;
@@ -1021,11 +1021,11 @@ var init_runtimeReader = __esm({
1021
1021
  await prepareWindowsAclDirectory(this.state, this.run).catch((error) => {
1022
1022
  throw new RuntimeError(error instanceof Error && error.message === "acl_permissions" ? "permission" : "acl_unavailable");
1023
1023
  });
1024
- Object.assign(stat, await lstat3(part));
1024
+ Object.assign(stat2, await lstat3(part));
1025
1025
  }
1026
- if (stat.isDirectory())
1026
+ if (stat2.isDirectory())
1027
1027
  this.windowsDirectories.add(part);
1028
- const identity = statIdentity(stat);
1028
+ const identity = statIdentity(stat2);
1029
1029
  const previous = this.windowsStats.get(part);
1030
1030
  if (previous !== void 0 && previous !== identity) {
1031
1031
  this.traceAudit({
@@ -1041,7 +1041,7 @@ var init_runtimeReader = __esm({
1041
1041
  }
1042
1042
  this.windowsStats.set(part, identity);
1043
1043
  windowsPaths.push(part);
1044
- } else if (stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
1044
+ } else if (stat2.uid !== process.getuid?.() || (stat2.mode & 63) !== 0)
1045
1045
  throw new RuntimeError("permission");
1046
1046
  }
1047
1047
  await this.audit(windowsPaths);
@@ -1057,10 +1057,10 @@ var init_runtimeReader = __esm({
1057
1057
  return;
1058
1058
  const absolute = this.guardedPath(path);
1059
1059
  const parent = dirname3(absolute);
1060
- const stat = await lstat3(parent);
1061
- if (!stat.isDirectory() || stat.isSymbolicLink() || !this.windowsStats.get(parent)?.startsWith(`${stat.dev}:${stat.ino}:`))
1060
+ const stat2 = await lstat3(parent);
1061
+ if (!stat2.isDirectory() || stat2.isSymbolicLink() || !this.windowsStats.get(parent)?.startsWith(`${stat2.dev}:${stat2.ino}:`))
1062
1062
  throw new RuntimeError("permission");
1063
- this.windowsStats.set(parent, statIdentity(stat));
1063
+ this.windowsStats.set(parent, statIdentity(stat2));
1064
1064
  this.windowsAudit.delete(absolute);
1065
1065
  this.windowsStats.delete(absolute);
1066
1066
  }
@@ -1307,7 +1307,7 @@ async function waitForResult(path, waitMs) {
1307
1307
  return result;
1308
1308
  if (Date.now() >= deadline)
1309
1309
  return RUNNING;
1310
- await new Promise((resolve4) => setTimeout(resolve4, Math.min(50, deadline - Date.now())));
1310
+ await new Promise((resolve5) => setTimeout(resolve5, Math.min(50, deadline - Date.now())));
1311
1311
  }
1312
1312
  }
1313
1313
  async function runEnsure(options) {
@@ -1469,8 +1469,8 @@ async function pendingProjectSetup(root) {
1469
1469
  for (const host of ["credit", "cursor", "grok"]) {
1470
1470
  const directory = join5(tmpdir(), `mnemonik-${host}-${uid}`);
1471
1471
  try {
1472
- const stat = await lstat4(directory);
1473
- if (stat.isSymbolicLink() || !stat.isDirectory())
1472
+ const stat2 = await lstat4(directory);
1473
+ if (stat2.isSymbolicLink() || !stat2.isDirectory())
1474
1474
  continue;
1475
1475
  for (const name of await readdir2(directory)) {
1476
1476
  if (!/^[a-f0-9]{64}$/.test(name))
@@ -1529,6 +1529,319 @@ process.stdin.on('end', () => {
1529
1529
  }
1530
1530
  });
1531
1531
 
1532
+ // packages/shared/dist/settingsIo.js
1533
+ import { createHash as createHash3, randomBytes } from "node:crypto";
1534
+ import { link, lstat as lstat5, mkdir as mkdir2, open as open3, readFile as readFile4, readdir as readdir3, realpath as realpath2, rename as rename2, rm as rm2, rmdir, stat, unlink } from "node:fs/promises";
1535
+ import { basename as basename3, dirname as dirname5, join as join6, resolve as resolve4 } from "node:path";
1536
+ function errorCode(error) {
1537
+ return error && typeof error === "object" && "code" in error ? String(error.code) : void 0;
1538
+ }
1539
+ async function syncDirectory(path) {
1540
+ try {
1541
+ const handle = await open3(path, "r");
1542
+ try {
1543
+ await handle.sync();
1544
+ } finally {
1545
+ await handle.close();
1546
+ }
1547
+ } catch {
1548
+ }
1549
+ }
1550
+ function lockOwnerIsAlive(raw) {
1551
+ const pid = Number.parseInt(raw.split(/\s+/, 1)[0] ?? "", 10);
1552
+ if (!Number.isSafeInteger(pid) || pid <= 0)
1553
+ return false;
1554
+ try {
1555
+ process.kill(pid, 0);
1556
+ return true;
1557
+ } catch (error) {
1558
+ return errorCode(error) === "EPERM";
1559
+ }
1560
+ }
1561
+ async function removeStaleLock(lockPath, staleMs = LOCK_STALE_MS) {
1562
+ const info = await lstat5(lockPath);
1563
+ if (!info.isDirectory()) {
1564
+ throw new Error(`Refusing unsafe non-directory installer lock ${lockPath}`);
1565
+ }
1566
+ if (Date.now() - info.mtimeMs <= staleMs)
1567
+ return false;
1568
+ const entries = await readdir3(lockPath);
1569
+ if (entries.length === 0) {
1570
+ try {
1571
+ await rmdir(lockPath);
1572
+ return true;
1573
+ } catch (error) {
1574
+ if (["ENOENT", "ENOTEMPTY"].includes(errorCode(error) ?? ""))
1575
+ return false;
1576
+ throw error;
1577
+ }
1578
+ }
1579
+ const [entry] = entries;
1580
+ if (entries.length !== 1 || entry === void 0 || !entry.startsWith("owner-"))
1581
+ return false;
1582
+ const tokenPath = join6(lockPath, entry);
1583
+ const owner = await readFile4(tokenPath, "utf8");
1584
+ if (lockOwnerIsAlive(owner))
1585
+ return false;
1586
+ try {
1587
+ await unlink(tokenPath);
1588
+ } catch (error) {
1589
+ if (errorCode(error) === "ENOENT")
1590
+ return false;
1591
+ throw error;
1592
+ }
1593
+ try {
1594
+ await rmdir(lockPath);
1595
+ return true;
1596
+ } catch (error) {
1597
+ if (["ENOENT", "ENOTEMPTY"].includes(errorCode(error) ?? ""))
1598
+ return false;
1599
+ throw error;
1600
+ }
1601
+ }
1602
+ async function acquireLock(lockPath, options = {}) {
1603
+ await mkdir2(dirname5(lockPath), { recursive: true });
1604
+ const owner = `${process.pid} ${Date.now()} ${randomBytes(12).toString("hex")}
1605
+ `;
1606
+ for (let attempt = 0; attempt < (options.retries ?? LOCK_RETRIES); attempt += 1) {
1607
+ try {
1608
+ await mkdir2(lockPath, { mode: 448 });
1609
+ const tokenPath = join6(lockPath, `owner-${process.pid}-${randomBytes(12).toString("hex")}`);
1610
+ const handle = await open3(tokenPath, "wx", 384);
1611
+ try {
1612
+ await handle.writeFile(owner, "utf8");
1613
+ await handle.sync();
1614
+ } finally {
1615
+ await handle.close();
1616
+ }
1617
+ await syncDirectory(lockPath);
1618
+ await syncDirectory(dirname5(lockPath));
1619
+ return tokenPath;
1620
+ } catch (error) {
1621
+ if (errorCode(error) !== "EEXIST") {
1622
+ await rmdir(lockPath).catch(() => void 0);
1623
+ if (errorCode(error) === "ENOENT")
1624
+ continue;
1625
+ throw error;
1626
+ }
1627
+ try {
1628
+ if (await removeStaleLock(lockPath, options.staleMs))
1629
+ continue;
1630
+ } catch (inspectError) {
1631
+ if (errorCode(inspectError) === "ENOENT")
1632
+ continue;
1633
+ throw inspectError;
1634
+ }
1635
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, options.retryMs ?? LOCK_RETRY_MS));
1636
+ }
1637
+ }
1638
+ throw new Error(`Timed out waiting for installer lock ${lockPath}`);
1639
+ }
1640
+ async function withFileLock(lockPath, action, options) {
1641
+ const tokenPath = await acquireLock(lockPath, options);
1642
+ try {
1643
+ return await action();
1644
+ } finally {
1645
+ try {
1646
+ await unlink(tokenPath);
1647
+ await rmdir(lockPath);
1648
+ await syncDirectory(dirname5(lockPath));
1649
+ } catch {
1650
+ }
1651
+ }
1652
+ }
1653
+ var LOCK_STALE_MS, LOCK_RETRIES, LOCK_RETRY_MS;
1654
+ var init_settingsIo = __esm({
1655
+ "packages/shared/dist/settingsIo.js"() {
1656
+ "use strict";
1657
+ LOCK_STALE_MS = 12e4;
1658
+ LOCK_RETRIES = 100;
1659
+ LOCK_RETRY_MS = 20;
1660
+ }
1661
+ });
1662
+
1663
+ // packages/shared/dist/hookConversation.js
1664
+ import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
1665
+ import { constants as constants3 } from "node:fs";
1666
+ import { lstat as lstat6, mkdir as mkdir3, open as open4, rename as rename3, rm as rm3, utimes } from "node:fs/promises";
1667
+ import { dirname as dirname6 } from "node:path";
1668
+ function boundedHandle(value) {
1669
+ return typeof value === "string" && /^cv1\.[0-9a-f-]{36}\.[A-Za-z0-9_-]{43}$/.test(value);
1670
+ }
1671
+ async function configureHookConversation(input) {
1672
+ active = new HookConversation(input);
1673
+ await active.load();
1674
+ }
1675
+ function currentConversationId() {
1676
+ return active?.id;
1677
+ }
1678
+ function conversationMarker() {
1679
+ return active?.id ? `<system-reminder>mnemonik-conversation: ${active.id}</system-reminder>` : void 0;
1680
+ }
1681
+ async function adoptHookConversation(result) {
1682
+ await active?.adopt(result);
1683
+ }
1684
+ function withHookConversation(url, init, post) {
1685
+ return active ? active.request(url, init, post) : post(init);
1686
+ }
1687
+ var RETENTION_MS, CONVERSATION_STATE_FILE, HookConversation, active;
1688
+ var init_hookConversation = __esm({
1689
+ "packages/shared/dist/hookConversation.js"() {
1690
+ "use strict";
1691
+ init_settingsIo();
1692
+ init_runtimeSigners();
1693
+ RETENTION_MS = 24 * 60 * 60 * 1e3;
1694
+ CONVERSATION_STATE_FILE = "conversation.json";
1695
+ HookConversation = class {
1696
+ input;
1697
+ conversationId;
1698
+ scope;
1699
+ constructor(input) {
1700
+ this.input = input;
1701
+ this.scope = createHash4("sha256").update(JSON.stringify([
1702
+ input.server.replace(/\/$/, ""),
1703
+ input.account,
1704
+ input.host,
1705
+ input.nativeSessionId,
1706
+ input.agentId ?? ""
1707
+ ])).digest("hex");
1708
+ }
1709
+ get id() {
1710
+ return this.conversationId;
1711
+ }
1712
+ async load() {
1713
+ this.conversationId = void 0;
1714
+ try {
1715
+ const directory = await lstat6(dirname6(this.input.stateFile));
1716
+ if (!directory.isDirectory() || directory.isSymbolicLink() || process.platform !== "win32" && (process.getuid && directory.uid !== process.getuid() || directory.mode & 63))
1717
+ return;
1718
+ const file = await open4(this.input.stateFile, constants3.O_RDONLY | constants3.O_NOFOLLOW);
1719
+ try {
1720
+ const stat2 = await file.stat();
1721
+ if (!stat2.isFile() || stat2.size > 4096 || process.platform !== "win32" && (process.getuid && stat2.uid !== process.getuid() || stat2.mode & 63))
1722
+ return;
1723
+ const cache = JSON.parse(await file.readFile("utf8"));
1724
+ if (cache.scope !== this.scope || !boundedHandle(cache.conversationId) || typeof cache.usedAt !== "number" || Date.now() - stat2.mtimeMs >= RETENTION_MS)
1725
+ return;
1726
+ this.conversationId = cache.conversationId;
1727
+ } finally {
1728
+ await file.close();
1729
+ }
1730
+ await utimes(this.input.stateFile, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date());
1731
+ return this.conversationId;
1732
+ } catch {
1733
+ return void 0;
1734
+ }
1735
+ }
1736
+ async locked(run) {
1737
+ const directory = dirname6(this.input.stateFile);
1738
+ await mkdir3(directory, { recursive: true, mode: 448 });
1739
+ const stat2 = await lstat6(directory);
1740
+ if (!stat2.isDirectory() || stat2.isSymbolicLink() || process.platform !== "win32" && (process.getuid && stat2.uid !== process.getuid() || stat2.mode & 63))
1741
+ throw new Error("conversation cache unavailable");
1742
+ ensureWindowsPrivateDirectorySync(directory);
1743
+ return withFileLock(`${this.input.stateFile}.lock`, run, {
1744
+ staleMs: 1e4,
1745
+ retries: 150,
1746
+ retryMs: 20
1747
+ });
1748
+ }
1749
+ async save(id) {
1750
+ const temporary = `${this.input.stateFile}.${randomBytes2(8).toString("hex")}`;
1751
+ const file = await open4(temporary, constants3.O_CREAT | constants3.O_EXCL | constants3.O_WRONLY | constants3.O_NOFOLLOW, 384);
1752
+ try {
1753
+ await file.writeFile(JSON.stringify({ v: 1, scope: this.scope, conversationId: id, usedAt: Date.now() }));
1754
+ } finally {
1755
+ await file.close();
1756
+ }
1757
+ try {
1758
+ await rename3(temporary, this.input.stateFile);
1759
+ this.conversationId = id;
1760
+ } finally {
1761
+ await rm3(temporary, { force: true });
1762
+ }
1763
+ }
1764
+ /** Existing authenticated response is the acquisition; no additional endpoint. */
1765
+ async request(url, init, post) {
1766
+ if (typeof init.body !== "string" || !url.startsWith(`${this.input.server.replace(/\/$/, "")}/api/v1/`))
1767
+ return post(init);
1768
+ let body;
1769
+ try {
1770
+ body = JSON.parse(init.body);
1771
+ } catch {
1772
+ return post(init);
1773
+ }
1774
+ const acquisition = this.input.acquire !== false && (url.endsWith("/hooks/mcp-precheck") || url.endsWith("/hooks/injections") && ["SessionStart", "SubagentStart", "UserPromptSubmit"].includes(String(body.event)));
1775
+ const send = async () => {
1776
+ await this.load();
1777
+ const response = await post({
1778
+ ...init,
1779
+ body: JSON.stringify({
1780
+ ...body,
1781
+ ...this.conversationId ? { conversationId: this.conversationId } : {}
1782
+ })
1783
+ });
1784
+ if (acquisition && response.ok) {
1785
+ const result = await response.clone().json().catch(() => null);
1786
+ if (boundedHandle(result?.conversationId)) {
1787
+ if (this.conversationId && this.conversationId !== result.conversationId)
1788
+ throw new Error("conversation response mismatch");
1789
+ await this.save(result.conversationId);
1790
+ }
1791
+ } else if (this.conversationId)
1792
+ await this.save(this.conversationId);
1793
+ return response;
1794
+ };
1795
+ return acquisition ? this.locked(send) : send();
1796
+ }
1797
+ /** Grok adopts only an authenticated explicit-bootstrap tool result. */
1798
+ async adopt(result) {
1799
+ const walk = (value, depth) => {
1800
+ if (depth > 5 || !value)
1801
+ return;
1802
+ if (typeof value === "string" && value.length < 65536) {
1803
+ const marker = value.match(/(?:^|[\s>])mnemonik-conversation:\s*(cv1\.[0-9a-f-]{36}\.[A-Za-z0-9_-]{43})(?=$|[\s<])/);
1804
+ if (marker && boundedHandle(marker[1]))
1805
+ return marker[1];
1806
+ try {
1807
+ return walk(JSON.parse(value), depth + 1);
1808
+ } catch {
1809
+ return;
1810
+ }
1811
+ }
1812
+ if (typeof value !== "object")
1813
+ return;
1814
+ const object = value;
1815
+ if (boundedHandle(object.conversationId))
1816
+ return object.conversationId;
1817
+ if (typeof object.text === "string" && object.text.length < 65536) {
1818
+ const id2 = walk(object.text, depth + 1);
1819
+ if (id2)
1820
+ return id2;
1821
+ }
1822
+ for (const key of ["structuredContent", "result", "content"]) {
1823
+ const nested = object[key];
1824
+ for (const child of Array.isArray(nested) ? nested : [nested]) {
1825
+ const id2 = walk(child, depth + 1);
1826
+ if (id2)
1827
+ return id2;
1828
+ }
1829
+ }
1830
+ return;
1831
+ };
1832
+ const id = walk(result, 0);
1833
+ if (id)
1834
+ await this.locked(async () => {
1835
+ await this.load();
1836
+ if (this.conversationId && this.conversationId !== id)
1837
+ throw new Error("conversation response mismatch");
1838
+ await this.save(id);
1839
+ });
1840
+ }
1841
+ };
1842
+ }
1843
+ });
1844
+
1532
1845
  // packages/shared/dist/repositoryFingerprint.js
1533
1846
  var repositoryFingerprint_exports = {};
1534
1847
  __export(repositoryFingerprint_exports, {
@@ -1536,7 +1849,7 @@ __export(repositoryFingerprint_exports, {
1536
1849
  canonicalizeRemote: () => canonicalizeRemote,
1537
1850
  selectRemote: () => selectRemote
1538
1851
  });
1539
- import { createHash as createHash3 } from "node:crypto";
1852
+ import { createHash as createHash5 } from "node:crypto";
1540
1853
  import { domainToASCII } from "node:url";
1541
1854
  function canonicalizeRemote(url, { providerRules = REPOSITORY_PROVIDER_RULES } = {}) {
1542
1855
  if (controls.test(url))
@@ -1646,7 +1959,7 @@ function canonicalizeRemote(url, { providerRules = REPOSITORY_PROVIDER_RULES } =
1646
1959
  const canonical = `repo-v1:${prefix}/${parsed.path}`;
1647
1960
  return {
1648
1961
  canonical,
1649
- hash: createHash3("sha256").update(canonical, "utf8").digest("hex"),
1962
+ hash: createHash5("sha256").update(canonical, "utf8").digest("hex"),
1650
1963
  algorithmVersion: 1
1651
1964
  };
1652
1965
  }
@@ -1690,7 +2003,9 @@ var init_repositoryFingerprint = __esm({
1690
2003
  // packages/shared/dist/hookRuntime.js
1691
2004
  var hookRuntime_exports = {};
1692
2005
  __export(hookRuntime_exports, {
2006
+ CONVERSATION_STATE_FILE: () => CONVERSATION_STATE_FILE,
1693
2007
  HOOK_STDIN_MAX_BYTES: () => HOOK_STDIN_MAX_BYTES,
2008
+ HookConversation: () => HookConversation,
1694
2009
  PROJECT_ENSURE_TIMEOUT_MS: () => PROJECT_ENSURE_TIMEOUT_MS,
1695
2010
  PROJECT_SETUP_MANUAL: () => PROJECT_SETUP_MANUAL,
1696
2011
  PROJECT_SETUP_RUNNING: () => PROJECT_SETUP_RUNNING,
@@ -1698,9 +2013,13 @@ __export(hookRuntime_exports, {
1698
2013
  RuntimeError: () => RuntimeError,
1699
2014
  RuntimeReader: () => RuntimeReader,
1700
2015
  aclRecords: () => aclRecords,
2016
+ adoptHookConversation: () => adoptHookConversation,
1701
2017
  auditWindowsPermissions: () => auditWindowsPermissions,
1702
2018
  bindHookContext: () => bindHookContext,
1703
2019
  clearProjectIdentityCache: () => clearProjectIdentityCache,
2020
+ configureHookConversation: () => configureHookConversation,
2021
+ conversationMarker: () => conversationMarker,
2022
+ currentConversationId: () => currentConversationId,
1704
2023
  ensureWindowsPrivateDirectorySync: () => ensureWindowsPrivateDirectorySync,
1705
2024
  execute: () => execute,
1706
2025
  findProjectIdentity: () => findProjectIdentity,
@@ -1724,7 +2043,8 @@ __export(hookRuntime_exports, {
1724
2043
  verifyWindowsPermission: () => verifyWindowsPermission,
1725
2044
  verifyWindowsPermissionSync: () => verifyWindowsPermissionSync,
1726
2045
  versionName: () => versionName,
1727
- windowsCurrentAccountSync: () => windowsCurrentAccountSync
2046
+ windowsCurrentAccountSync: () => windowsCurrentAccountSync,
2047
+ withHookConversation: () => withHookConversation
1728
2048
  });
1729
2049
  import { stderr, stdin } from "node:process";
1730
2050
  function clearProjectIdentityCache() {
@@ -1756,7 +2076,7 @@ async function findProjectIdentityDetailed(startCwd, options = {}) {
1756
2076
  function readHookStdin(options = {}) {
1757
2077
  const stream = options.stream ?? stdin;
1758
2078
  const maxBytes = options.maxBytes ?? HOOK_STDIN_MAX_BYTES;
1759
- return new Promise((resolve4) => {
2079
+ return new Promise((resolve5) => {
1760
2080
  const chunks = [];
1761
2081
  let bytes = 0;
1762
2082
  let settled = false;
@@ -1772,7 +2092,7 @@ function readHookStdin(options = {}) {
1772
2092
  stream.pause();
1773
2093
  stream.destroy?.();
1774
2094
  }
1775
- resolve4(value);
2095
+ resolve5(value);
1776
2096
  };
1777
2097
  const onData = (chunk) => {
1778
2098
  const buffer = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
@@ -1811,21 +2131,21 @@ async function postHookBoundJson(server, path, token, body) {
1811
2131
  }
1812
2132
  async function bindHookContext(input, familyId, hmac, post, options = {}) {
1813
2133
  const { execFile: execFile3 } = await import("node:child_process");
1814
- const { constants: constants3 } = await import("node:fs");
1815
- const { lstat: lstat5, mkdir: mkdir2, open: open3, realpath: realpath2 } = await import("node:fs/promises");
1816
- const { dirname: dirname5 } = await import("node:path");
2134
+ const { constants: constants4 } = await import("node:fs");
2135
+ const { lstat: lstat7, mkdir: mkdir4, open: open5, realpath: realpath3 } = await import("node:fs/promises");
2136
+ const { dirname: dirname7 } = await import("node:path");
1817
2137
  const { selectRemote: selectRemote2 } = await Promise.resolve().then(() => (init_repositoryFingerprint(), repositoryFingerprint_exports));
1818
- const identity = await findProjectIdentityDetailed(await realpath2(input.cwd));
2138
+ const identity = await findProjectIdentityDetailed(await realpath3(input.cwd));
1819
2139
  if (identity.kind === "git_unavailable")
1820
2140
  throw new Error("context unavailable");
1821
- const root = await realpath2(identity.root);
2141
+ const root = await realpath3(identity.root);
1822
2142
  const binding = await hmac(root);
1823
2143
  const gitEnv = { LC_ALL: "C" };
1824
2144
  for (const key2 of ["PATH", "HOME", "LANG", "SYSTEMROOT", "TEMP", "TMP", "USERPROFILE"])
1825
2145
  if (process.env[key2] !== void 0)
1826
2146
  gitEnv[key2] = process.env[key2];
1827
- const git = (args) => new Promise((resolve4) => {
1828
- execFile3("git", args, { cwd: root, env: gitEnv, timeout: 2e3, encoding: "utf8" }, (error, stdout2) => resolve4(error ? [] : stdout2.trim().split(/\r?\n/).filter(Boolean)));
2147
+ const git = (args) => new Promise((resolve5) => {
2148
+ execFile3("git", args, { cwd: root, env: gitEnv, timeout: 2e3, encoding: "utf8" }, (error, stdout2) => resolve5(error ? [] : stdout2.trim().split(/\r?\n/).filter(Boolean)));
1829
2149
  });
1830
2150
  const remotes = identity.repository.kind === "git" ? await git(["remote"]) : [];
1831
2151
  const selection = selectRemote2(await Promise.all(remotes.map(async (name) => ({
@@ -1850,17 +2170,17 @@ async function bindHookContext(input, familyId, hmac, post, options = {}) {
1850
2170
  ...identity.kind === "ok" ? { projectId: identity.identity.projectId } : {}
1851
2171
  };
1852
2172
  const key = JSON.stringify([input.server, familyId, body]);
1853
- const directory = dirname5(input.stateFile);
1854
- for (const path of [dirname5(directory), directory]) {
1855
- const created = await mkdir2(path, { recursive: true, mode: 448 });
1856
- const stat = await lstat5(path);
1857
- if (!stat.isDirectory() || stat.isSymbolicLink() || (options.platform ?? process.platform) !== "win32" && (process.getuid && stat.uid !== process.getuid() || stat.mode & 63))
2173
+ const directory = dirname7(input.stateFile);
2174
+ for (const path of [dirname7(directory), directory]) {
2175
+ const created = await mkdir4(path, { recursive: true, mode: 448 });
2176
+ const stat2 = await lstat7(path);
2177
+ if (!stat2.isDirectory() || stat2.isSymbolicLink() || (options.platform ?? process.platform) !== "win32" && (process.getuid && stat2.uid !== process.getuid() || stat2.mode & 63))
1858
2178
  throw new Error("cache unavailable");
1859
2179
  if ((options.platform ?? process.platform) === "win32") {
1860
2180
  await protectWindowsDirectory(path, created !== void 0, options.run, directory);
1861
2181
  }
1862
2182
  }
1863
- const file = await open3(input.stateFile, constants3.O_CREAT | constants3.O_RDWR | constants3.O_NOFOLLOW, 384);
2183
+ const file = await open5(input.stateFile, constants4.O_CREAT | constants4.O_RDWR | constants4.O_NOFOLLOW, 384);
1864
2184
  try {
1865
2185
  let cache;
1866
2186
  try {
@@ -1900,6 +2220,7 @@ var init_hookRuntime = __esm({
1900
2220
  init_runtimeReader();
1901
2221
  init_runtimeSigners();
1902
2222
  init_projectSetupHandoff();
2223
+ init_hookConversation();
1903
2224
  identityCache = /* @__PURE__ */ new Map();
1904
2225
  HOOK_STDIN_MAX_BYTES = 1048576;
1905
2226
  mismatchEmitted = false;
@@ -1909,7 +2230,9 @@ var init_hookRuntime = __esm({
1909
2230
  // packages/cli/src/runtime/signers.ts
1910
2231
  init_hookRuntime();
1911
2232
  export {
2233
+ CONVERSATION_STATE_FILE,
1912
2234
  HOOK_STDIN_MAX_BYTES,
2235
+ HookConversation,
1913
2236
  PROJECT_ENSURE_TIMEOUT_MS,
1914
2237
  PROJECT_SETUP_MANUAL,
1915
2238
  PROJECT_SETUP_RUNNING,
@@ -1917,9 +2240,13 @@ export {
1917
2240
  RuntimeError,
1918
2241
  RuntimeReader,
1919
2242
  aclRecords,
2243
+ adoptHookConversation,
1920
2244
  auditWindowsPermissions,
1921
2245
  bindHookContext,
1922
2246
  clearProjectIdentityCache,
2247
+ configureHookConversation,
2248
+ conversationMarker,
2249
+ currentConversationId,
1923
2250
  ensureWindowsPrivateDirectorySync,
1924
2251
  execute,
1925
2252
  findProjectIdentity,
@@ -1943,5 +2270,6 @@ export {
1943
2270
  verifyWindowsPermission,
1944
2271
  verifyWindowsPermissionSync,
1945
2272
  versionName,
1946
- windowsCurrentAccountSync
2273
+ windowsCurrentAccountSync,
2274
+ withHookConversation
1947
2275
  };
@@ -824,6 +824,24 @@ process.stdin.on('end', () => {
824
824
  }
825
825
  });
826
826
 
827
+ // packages/shared/dist/settingsIo.js
828
+ var init_settingsIo = __esm({
829
+ "packages/shared/dist/settingsIo.js"() {
830
+ "use strict";
831
+ }
832
+ });
833
+
834
+ // packages/shared/dist/hookConversation.js
835
+ var RETENTION_MS;
836
+ var init_hookConversation = __esm({
837
+ "packages/shared/dist/hookConversation.js"() {
838
+ "use strict";
839
+ init_settingsIo();
840
+ init_runtimeSigners();
841
+ RETENTION_MS = 24 * 60 * 60 * 1e3;
842
+ }
843
+ });
844
+
827
845
  // packages/shared/dist/hookRuntime.js
828
846
  var init_hookRuntime = __esm({
829
847
  "packages/shared/dist/hookRuntime.js"() {
@@ -834,6 +852,7 @@ var init_hookRuntime = __esm({
834
852
  init_runtimeReader();
835
853
  init_runtimeSigners();
836
854
  init_projectSetupHandoff();
855
+ init_hookConversation();
837
856
  }
838
857
  });
839
858