agent-workbench 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // packages/server/src/index.ts
4
4
  import { existsSync as existsSync4 } from "node:fs";
5
5
  import { createServer } from "node:http";
6
- import path49 from "node:path";
6
+ import path51 from "node:path";
7
7
  import { fileURLToPath as fileURLToPath2 } from "node:url";
8
8
  import express from "express";
9
9
 
@@ -153,7 +153,8 @@ function parseSessionSummary(value) {
153
153
  var CONVERSATION_ROLES = ["user", "assistant"];
154
154
  var CONVERSATION_IMAGE_SOURCES = [
155
155
  "content",
156
- "attachment"
156
+ "attachment",
157
+ "pasted"
157
158
  ];
158
159
  var CONVERSATION_NOTICES = [
159
160
  "compacted",
@@ -277,7 +278,7 @@ function parseConversationPart(value) {
277
278
  const index = asFiniteNumber(record["index"]);
278
279
  const mediaType = asString(record["mediaType"]);
279
280
  if (index === null || mediaType === null) return null;
280
- const source = record["source"] === "attachment" ? "attachment" : "content";
281
+ const source = asLiteral(record["source"], CONVERSATION_IMAGE_SOURCES) ?? "content";
281
282
  return { kind: "image", index, mediaType, source };
282
283
  }
283
284
  case "notice": {
@@ -4219,7 +4220,7 @@ function createAntigravityAdapter(options = {}) {
4219
4220
 
4220
4221
  // packages/server/src/agents/claude-code/index.ts
4221
4222
  import { homedir as homedir5 } from "node:os";
4222
- import path19 from "node:path";
4223
+ import path21 from "node:path";
4223
4224
 
4224
4225
  // packages/server/src/agents/claude-code/agent-defaults.ts
4225
4226
  import { readFile as readFile6 } from "node:fs/promises";
@@ -4555,8 +4556,8 @@ function claudeCodeEnvironment(base) {
4555
4556
  }
4556
4557
 
4557
4558
  // packages/server/src/agents/claude-code/history.ts
4558
- import { readdir as readdir7, stat as stat11 } from "node:fs/promises";
4559
- import path18 from "node:path";
4559
+ import { readdir as readdir7, stat as stat12 } from "node:fs/promises";
4560
+ import path20 from "node:path";
4560
4561
 
4561
4562
  // packages/server/src/agents/claude-code/plans-store.ts
4562
4563
  import { readFile as readFile8, stat as stat9 } from "node:fs/promises";
@@ -4730,10 +4731,30 @@ async function readPlan(target, ref) {
4730
4731
  }
4731
4732
 
4732
4733
  // packages/server/src/agents/claude-code/session-follower.ts
4733
- import path17 from "node:path";
4734
+ import path19 from "node:path";
4734
4735
 
4735
4736
  // packages/server/src/agents/claude-code/jsonl-events.ts
4737
+ import path17 from "node:path";
4738
+
4739
+ // packages/server/src/paste-paths.ts
4740
+ import { tmpdir as tmpdir3 } from "node:os";
4736
4741
  import path16 from "node:path";
4742
+ function pasteRoot() {
4743
+ return path16.join(tmpdir3(), "agent-workbench", "pasted");
4744
+ }
4745
+ var PASTED_IMAGE = /(?:^|[\\/])agent-workbench[\\/]pasted[\\/][A-Za-z0-9_-]+[\\/]pegada-\d+-[0-9a-f]{8}\.(png|jpg|gif|webp)$/i;
4746
+ var MEDIA_TYPES = {
4747
+ png: "image/png",
4748
+ jpg: "image/jpeg",
4749
+ gif: "image/gif",
4750
+ webp: "image/webp"
4751
+ };
4752
+ function pastedImageMediaType(filePath) {
4753
+ const extension = PASTED_IMAGE.exec(filePath)?.[1];
4754
+ return extension === void 0 ? null : MEDIA_TYPES[extension.toLowerCase()] ?? null;
4755
+ }
4756
+
4757
+ // packages/server/src/agents/claude-code/jsonl-events.ts
4737
4758
  var HARNESS_TAGS = [
4738
4759
  "system-reminder",
4739
4760
  "task-notification",
@@ -4842,9 +4863,24 @@ function toPart(block, role, index, limits) {
4842
4863
  const { text, truncated } = cut(cleaned, limits.textMaxChars);
4843
4864
  return { kind: "text", text, truncated };
4844
4865
  }
4845
- // El contenido del razonamiento no esta en el JSONL: solo la firma. Se deja
4846
- // la marca de que hubo, sin prometer un texto que no existe.
4847
- case "thinking":
4866
+ /*
4867
+ El contenido del razonamiento no esta en el JSONL: solo la firma. Se deja
4868
+ la marca de que hubo, sin prometer un texto que no existe.
4869
+
4870
+ **Salvo la nota para el usuario** (§4.9). Desde la CLI 2.1.278, alrededor
4871
+ de uno de cada diez bloques trae texto, y no es el razonamiento: es un
4872
+ aviso corto de lo que el agente encontro o va a hacer ("Encontre la
4873
+ causa: …"), en una linea propia detras de la del razonamiento vacio del
4874
+ mismo mensaje. La CLI lo dibuja como un mensaje mas, y aca va como texto:
4875
+ descartado con el resto del razonamiento, el hilo se lo perdia.
4876
+ */
4877
+ case "thinking": {
4878
+ const raw = record["thinking"];
4879
+ const note = typeof raw === "string" ? raw.trim() : "";
4880
+ if (note.length === 0) return { kind: "thinking" };
4881
+ const { text, truncated } = cut(note, limits.textMaxChars);
4882
+ return { kind: "text", text, truncated };
4883
+ }
4848
4884
  case "redacted_thinking":
4849
4885
  return { kind: "thinking" };
4850
4886
  case "tool_use": {
@@ -4916,9 +4952,18 @@ function toQueuedUserEvent(record, lineNumber, limits) {
4916
4952
  if (!isHumanOrigin(attachmentRecord["origin"])) return null;
4917
4953
  const prompt = attachmentRecord["prompt"];
4918
4954
  if (typeof prompt !== "string") return null;
4919
- const cleaned = cleanUserText(prompt);
4920
- if (cleaned.length === 0) return null;
4921
- const { text, truncated } = cut(cleaned, limits.textMaxChars);
4955
+ const { text: cleaned, images } = takePastedImages(cleanUserText(prompt));
4956
+ const parts = images.map((image, index) => ({
4957
+ kind: "image",
4958
+ index,
4959
+ mediaType: image.mediaType,
4960
+ source: "pasted"
4961
+ }));
4962
+ if (cleaned.length > 0) {
4963
+ const { text, truncated } = cut(cleaned, limits.textMaxChars);
4964
+ parts.push({ kind: "text", text, truncated });
4965
+ }
4966
+ if (parts.length === 0) return null;
4922
4967
  const uuid = record["uuid"];
4923
4968
  return {
4924
4969
  eventId: typeof uuid === "string" && uuid.length > 0 ? uuid : `line-${lineNumber}`,
@@ -4926,7 +4971,7 @@ function toQueuedUserEvent(record, lineNumber, limits) {
4926
4971
  // El `timestamp` de la linea es el de cuando se escribio, no el de cuando
4927
4972
  // se absorbio. Es el que corresponde: es cuando el usuario lo mando.
4928
4973
  at: readTimestamp(record["timestamp"] ?? attachmentRecord["timestamp"]),
4929
- parts: [{ kind: "text", text, truncated }],
4974
+ parts,
4930
4975
  model: null,
4931
4976
  usage: null,
4932
4977
  effort: null,
@@ -4992,6 +5037,30 @@ function stripFileReference(text, filename) {
4992
5037
  }
4993
5038
  return text;
4994
5039
  }
5040
+ function sameFilePath(a, b) {
5041
+ return normalizePath(a) === normalizePath(b);
5042
+ }
5043
+ function takePastedImages(text) {
5044
+ const images = [];
5045
+ const tokens = /(^|\s)@(?:"([^"]*)"|(\S+))/g;
5046
+ let match = tokens.exec(text);
5047
+ while (match !== null) {
5048
+ const raw = match[2] ?? match[3] ?? "";
5049
+ const mediaType = pastedImageMediaType(raw);
5050
+ if (mediaType !== null) images.push({ path: raw, mediaType });
5051
+ match = tokens.exec(text);
5052
+ }
5053
+ let rest = text;
5054
+ for (const image of images) rest = stripFileReference(rest, image.path);
5055
+ return { text: rest, images };
5056
+ }
5057
+ function queuedPastedImages(record) {
5058
+ const attachment = recordOf(record["attachment"]);
5059
+ if (attachment === null || attachment["type"] !== "queued_command") return [];
5060
+ const prompt = attachment["prompt"];
5061
+ if (typeof prompt !== "string") return [];
5062
+ return takePastedImages(cleanUserText(prompt)).images.map((image) => image.path);
5063
+ }
4995
5064
  function toConversationEvent(record, lineNumber, limits = TRANSPORT_LIMITS) {
4996
5065
  const type = record["type"];
4997
5066
  if (record["isMeta"] === true) return null;
@@ -5069,7 +5138,7 @@ function documentRefOf(value, target) {
5069
5138
  if (!value.toLowerCase().endsWith(".md")) return null;
5070
5139
  if (value.includes("\0")) return null;
5071
5140
  if (target.cwd.length === 0) return null;
5072
- const absolute = path16.isAbsolute(value) ? value : path16.resolve(target.cwd, value);
5141
+ const absolute = path17.isAbsolute(value) ? value : path17.resolve(target.cwd, value);
5073
5142
  if (target.sessionId.length > 0) {
5074
5143
  const inScratch = relativeInside(scratchRoot(target.cwd, target.sessionId), absolute);
5075
5144
  if (inScratch !== null) return `tmp:${inScratch}`;
@@ -5078,15 +5147,15 @@ function documentRefOf(value, target) {
5078
5147
  return inProject === null ? null : `proj:${inProject}`;
5079
5148
  }
5080
5149
  function relativeInside(root, absolute) {
5081
- const relative = path16.relative(root, absolute);
5150
+ const relative = path17.relative(root, absolute);
5082
5151
  if (relative.length === 0) return null;
5083
- if (relative.startsWith("..") || path16.isAbsolute(relative)) {
5152
+ if (relative.startsWith("..") || path17.isAbsolute(relative)) {
5084
5153
  if (process.platform !== "win32") return null;
5085
- const lower = path16.relative(root.toLowerCase(), absolute.toLowerCase());
5086
- if (lower.length === 0 || lower.startsWith("..") || path16.isAbsolute(lower)) return null;
5087
- return lower.split(path16.sep).join("/");
5154
+ const lower = path17.relative(root.toLowerCase(), absolute.toLowerCase());
5155
+ if (lower.length === 0 || lower.startsWith("..") || path17.isAbsolute(lower)) return null;
5156
+ return lower.split(path17.sep).join("/");
5088
5157
  }
5089
- return relative.split(path16.sep).join("/");
5158
+ return relative.split(path17.sep).join("/");
5090
5159
  }
5091
5160
  function planFileNameOf(value) {
5092
5161
  if (typeof value !== "string" || value.length === 0) return null;
@@ -5636,13 +5705,50 @@ var ConversationFollower = class {
5636
5705
  };
5637
5706
 
5638
5707
  // packages/server/src/agents/claude-code/conversation-image.ts
5639
- import { open as open5 } from "node:fs/promises";
5708
+ import { open as open5, readFile as readFile9, stat as stat10 } from "node:fs/promises";
5709
+ import path18 from "node:path";
5710
+
5711
+ // packages/server/src/image-signature.ts
5712
+ var FORMATS = [
5713
+ {
5714
+ format: { ext: "png", mediaType: "image/png" },
5715
+ test: (b) => b.length > 8 && b.subarray(0, 8).equals(Buffer.from("89504e470d0a1a0a", "hex"))
5716
+ },
5717
+ {
5718
+ format: { ext: "jpg", mediaType: "image/jpeg" },
5719
+ test: (b) => b.length > 3 && b[0] === 255 && b[1] === 216 && b[2] === 255
5720
+ },
5721
+ {
5722
+ format: { ext: "gif", mediaType: "image/gif" },
5723
+ test: (b) => b.length > 6 && b.subarray(0, 4).toString("ascii") === "GIF8"
5724
+ },
5725
+ {
5726
+ format: { ext: "webp", mediaType: "image/webp" },
5727
+ test: (b) => b.length > 12 && b.subarray(0, 4).toString("ascii") === "RIFF" && b.subarray(8, 12).toString("ascii") === "WEBP"
5728
+ }
5729
+ ];
5730
+ var IMAGE_MEDIA_TYPES = new Set(
5731
+ FORMATS.map((entry) => entry.format.mediaType)
5732
+ );
5733
+ function detectImageFormat(bytes) {
5734
+ return FORMATS.find((entry) => entry.test(bytes))?.format ?? null;
5735
+ }
5736
+
5737
+ // packages/server/src/agents/claude-code/conversation-image.ts
5640
5738
  var CHUNK_SIZE2 = 256 * 1024;
5641
5739
  var NEWLINE2 = 10;
5642
5740
  var MAX_IMAGE_BYTES = MAX_SUBMIT_IMAGE_BYTES;
5643
- async function loadConversationImage(filePath, eventId, index, source = "content") {
5741
+ async function loadConversationImage(filePath, eventId, index, source = "content", pastedDir = pasteRoot()) {
5644
5742
  const handle = await open5(filePath, "r").catch(() => null);
5645
5743
  if (handle === null) return null;
5744
+ try {
5745
+ if (source === "pasted") return await findPastedImage(linesOf(handle), eventId, index, pastedDir);
5746
+ return await findInLines(linesOf(handle), eventId, index, source);
5747
+ } finally {
5748
+ await handle.close();
5749
+ }
5750
+ }
5751
+ async function findInLines(lines, eventId, index, source) {
5646
5752
  let seen = 0;
5647
5753
  const chain = /* @__PURE__ */ new Set([eventId]);
5648
5754
  const consider = (line) => {
@@ -5665,56 +5771,126 @@ async function loadConversationImage(filePath, eventId, index, source = "content
5665
5771
  if (typeof ownId === "string" && ownId.length > 0) chain.add(ownId);
5666
5772
  return seen++ === index ? found : null;
5667
5773
  };
5668
- try {
5669
- const buffer = Buffer.allocUnsafe(CHUNK_SIZE2);
5670
- let pending = Buffer.alloc(0);
5671
- let offset = 0;
5672
- for (; ; ) {
5673
- const { bytesRead } = await handle.read(buffer, 0, CHUNK_SIZE2, offset);
5674
- if (bytesRead === 0) break;
5675
- offset += bytesRead;
5676
- const combined = Buffer.concat([pending, buffer.subarray(0, bytesRead)]);
5677
- let start = 0;
5678
- let newlineIndex = combined.indexOf(NEWLINE2, start);
5679
- while (newlineIndex !== -1) {
5680
- const line = combined.subarray(start, newlineIndex).toString("utf8");
5681
- start = newlineIndex + 1;
5682
- newlineIndex = combined.indexOf(NEWLINE2, start);
5683
- const found = consider(line);
5684
- if (found !== null) return found;
5685
- }
5686
- pending = Buffer.from(combined.subarray(start));
5774
+ for await (const line of lines) {
5775
+ const found = consider(line);
5776
+ if (found !== null) return found;
5777
+ }
5778
+ return null;
5779
+ }
5780
+ async function* linesOf(handle) {
5781
+ const buffer = Buffer.allocUnsafe(CHUNK_SIZE2);
5782
+ let pending = Buffer.alloc(0);
5783
+ let offset = 0;
5784
+ for (; ; ) {
5785
+ const { bytesRead } = await handle.read(buffer, 0, CHUNK_SIZE2, offset);
5786
+ if (bytesRead === 0) break;
5787
+ offset += bytesRead;
5788
+ const combined = Buffer.concat([pending, buffer.subarray(0, bytesRead)]);
5789
+ let start = 0;
5790
+ let newlineIndex = combined.indexOf(NEWLINE2, start);
5791
+ while (newlineIndex !== -1) {
5792
+ yield combined.subarray(start, newlineIndex).toString("utf8");
5793
+ start = newlineIndex + 1;
5794
+ newlineIndex = combined.indexOf(NEWLINE2, start);
5795
+ }
5796
+ pending = Buffer.from(combined.subarray(start));
5797
+ }
5798
+ if (pending.length > 0) yield pending.toString("utf8");
5799
+ }
5800
+ async function findPastedImage(lines, eventId, index, pastedDir) {
5801
+ let target = null;
5802
+ let readId = null;
5803
+ for await (const line of lines) {
5804
+ if (target === null) {
5805
+ if (!line.includes(eventId)) continue;
5806
+ const record = parseJsonlLine(line);
5807
+ if (record === null || record["uuid"] !== eventId) continue;
5808
+ target = queuedPastedImages(record)[index] ?? null;
5809
+ if (target === null) return null;
5810
+ const fromDisk = await readPastedFile(target, pastedDir);
5811
+ if (fromDisk !== null) return fromDisk;
5812
+ continue;
5813
+ }
5814
+ if (readId === null) {
5815
+ if (line.includes('"Read"')) readId = readToolUseOf(parseJsonlLine(line), target);
5816
+ continue;
5687
5817
  }
5688
- if (pending.length > 0) return consider(pending.toString("utf8"));
5818
+ if (!line.includes(readId)) continue;
5819
+ const result = toolResultOf(parseJsonlLine(line), readId);
5820
+ if (result === null) continue;
5821
+ const content = result["content"];
5822
+ const image = Array.isArray(content) ? firstImage(content) : null;
5823
+ if (image !== null) return image;
5824
+ readId = null;
5825
+ }
5826
+ return null;
5827
+ }
5828
+ async function readPastedFile(target, pastedDir) {
5829
+ try {
5830
+ const inside = await resolveInside(pastedDir, path18.relative(pastedDir, target), { mustExist: true });
5831
+ const info = await stat10(inside);
5832
+ if (!info.isFile() || info.size === 0 || info.size > MAX_IMAGE_BYTES) return null;
5833
+ const bytes = await readFile9(inside);
5834
+ const format = detectImageFormat(bytes);
5835
+ return format === null ? null : { mediaType: format.mediaType, data: bytes.toString("base64") };
5836
+ } catch {
5689
5837
  return null;
5690
- } finally {
5691
- await handle.close();
5692
5838
  }
5693
5839
  }
5840
+ function readToolUseOf(record, target) {
5841
+ if (record === null || record["type"] !== "assistant") return null;
5842
+ const content = recordOf(record["message"])?.["content"];
5843
+ if (!Array.isArray(content)) return null;
5844
+ for (const block of content) {
5845
+ const tool = recordOf(block);
5846
+ if (tool === null || tool["type"] !== "tool_use" || tool["name"] !== "Read") continue;
5847
+ const id = tool["id"];
5848
+ const filePath = recordOf(tool["input"])?.["file_path"];
5849
+ if (typeof id === "string" && id.length > 0 && typeof filePath === "string" && sameFilePath(filePath, target)) {
5850
+ return id;
5851
+ }
5852
+ }
5853
+ return null;
5854
+ }
5855
+ function toolResultOf(record, toolUseId) {
5856
+ if (record === null || record["type"] !== "user") return null;
5857
+ const content = recordOf(record["message"])?.["content"];
5858
+ if (!Array.isArray(content)) return null;
5859
+ for (const block of content) {
5860
+ const result = recordOf(block);
5861
+ if (result !== null && result["type"] === "tool_result" && result["tool_use_id"] === toolUseId) return result;
5862
+ }
5863
+ return null;
5864
+ }
5694
5865
  function tooBig(data) {
5695
5866
  return data.length > MAX_IMAGE_BYTES * 1.4;
5696
5867
  }
5697
- function imageFromContent(record, eventId, index) {
5698
- if (record["uuid"] !== eventId) return null;
5699
- const message = record["message"];
5700
- if (typeof message !== "object" || message === null) return null;
5701
- const content = message["content"];
5702
- if (!Array.isArray(content)) return null;
5703
- const block = content[index];
5704
- if (typeof block !== "object" || block === null) return null;
5705
- const blockRecord = block;
5706
- if (blockRecord["type"] !== "image") return null;
5707
- const source = blockRecord["source"];
5708
- if (typeof source !== "object" || source === null) return null;
5709
- const sourceRecord = source;
5710
- const data = sourceRecord["data"];
5711
- const mediaType = sourceRecord["media_type"];
5868
+ function imageFromBlock(value) {
5869
+ const block = recordOf(value);
5870
+ if (block === null || block["type"] !== "image") return null;
5871
+ const source = recordOf(block["source"]);
5872
+ if (source === null) return null;
5873
+ const data = source["data"];
5874
+ const mediaType = source["media_type"];
5712
5875
  if (typeof data !== "string" || data.length === 0 || tooBig(data)) return null;
5713
5876
  return {
5714
5877
  mediaType: typeof mediaType === "string" ? mediaType : "image/png",
5715
5878
  data
5716
5879
  };
5717
5880
  }
5881
+ function firstImage(blocks) {
5882
+ for (const block of blocks) {
5883
+ const image = imageFromBlock(block);
5884
+ if (image !== null) return image;
5885
+ }
5886
+ return null;
5887
+ }
5888
+ function imageFromContent(record, eventId, index) {
5889
+ if (record["uuid"] !== eventId) return null;
5890
+ const content = recordOf(record["message"])?.["content"];
5891
+ if (!Array.isArray(content)) return null;
5892
+ return imageFromBlock(content[index]);
5893
+ }
5718
5894
  function imageFromAttachment(record) {
5719
5895
  if (record["type"] !== "attachment") return null;
5720
5896
  const attachment = record["attachment"];
@@ -5786,7 +5962,7 @@ var ClaudeCodeSessionFollower = class {
5786
5962
  }
5787
5963
  noticeChange(filePath) {
5788
5964
  if (isSameFilePath(this.inner.filePath, filePath, process.platform)) return true;
5789
- if (this.inner.getState() === "waiting" && path17.basename(filePath).toLowerCase() === `${this.sessionId.toLowerCase()}.jsonl`) {
5965
+ if (this.inner.getState() === "waiting" && path19.basename(filePath).toLowerCase() === `${this.sessionId.toLowerCase()}.jsonl`) {
5790
5966
  console.warn(
5791
5967
  `[conversation] session ${this.sessionId.slice(0, 8)} wrote to ${filePath}, not to the computed path; moving there.`
5792
5968
  );
@@ -5833,7 +6009,7 @@ var ClaudeCodeSessionFollower = class {
5833
6009
  };
5834
6010
 
5835
6011
  // packages/server/src/agents/claude-code/session-scan.ts
5836
- import { stat as stat10 } from "node:fs/promises";
6012
+ import { stat as stat11 } from "node:fs/promises";
5837
6013
  var HEAD_MAX_LINES = 40;
5838
6014
  var HEAD_MAX_BYTES = 512 * 1024;
5839
6015
  var TAIL_MAX_BYTES = 64 * 1024;
@@ -5858,7 +6034,7 @@ function isRealUserMessage(record) {
5858
6034
  return true;
5859
6035
  }
5860
6036
  async function scanSessionFile(filePath, sessionId) {
5861
- const info = await stat10(filePath);
6037
+ const info = await stat11(filePath);
5862
6038
  const [headLines, tailLines] = await Promise.all([
5863
6039
  readHeadLines(filePath, { maxLines: HEAD_MAX_LINES, maxBytes: HEAD_MAX_BYTES }),
5864
6040
  readTailLines(filePath, { maxBytes: TAIL_MAX_BYTES })
@@ -5931,7 +6107,7 @@ async function scanSessionFile(filePath, sessionId) {
5931
6107
  var SESSION_EXTENSION = /\.jsonl$/i;
5932
6108
  async function pathExists(candidate) {
5933
6109
  try {
5934
- await stat11(candidate);
6110
+ await stat12(candidate);
5935
6111
  return true;
5936
6112
  } catch {
5937
6113
  return false;
@@ -5940,8 +6116,8 @@ async function pathExists(candidate) {
5940
6116
  function toItem(ref, info) {
5941
6117
  return {
5942
6118
  ref,
5943
- sessionId: path18.basename(ref).replace(SESSION_EXTENSION, ""),
5944
- group: path18.basename(path18.dirname(ref)),
6119
+ sessionId: path20.basename(ref).replace(SESSION_EXTENSION, ""),
6120
+ group: path20.basename(path20.dirname(ref)),
5945
6121
  mtimeMs: info.mtimeMs,
5946
6122
  sizeBytes: info.size
5947
6123
  };
@@ -5971,14 +6147,14 @@ function createClaudeCodeHistory(variants) {
5971
6147
  for (const slug of directories) {
5972
6148
  let files;
5973
6149
  try {
5974
- files = (await readdir7(path18.join(root, slug))).filter((name) => name.endsWith(".jsonl"));
6150
+ files = (await readdir7(path20.join(root, slug))).filter((name) => name.endsWith(".jsonl"));
5975
6151
  } catch {
5976
6152
  continue;
5977
6153
  }
5978
6154
  for (const file of files) {
5979
- const ref = path18.join(root, slug, file);
6155
+ const ref = path20.join(root, slug, file);
5980
6156
  try {
5981
- items.push(toItem(ref, await stat11(ref)));
6157
+ items.push(toItem(ref, await stat12(ref)));
5982
6158
  } catch {
5983
6159
  }
5984
6160
  }
@@ -5986,15 +6162,15 @@ function createClaudeCodeHistory(variants) {
5986
6162
  return items;
5987
6163
  },
5988
6164
  async changedRefs(filePath) {
5989
- const relative = path18.relative(sessionsRoot(), filePath);
5990
- const [slug] = relative.split(path18.sep);
6165
+ const relative = path20.relative(sessionsRoot(), filePath);
6166
+ const [slug] = relative.split(path20.sep);
5991
6167
  if (slug === void 0 || slug.length === 0 || slug.startsWith("..")) return null;
5992
6168
  if (!filePath.endsWith(".jsonl")) return null;
5993
6169
  return [filePath];
5994
6170
  },
5995
6171
  async item(ref) {
5996
6172
  try {
5997
- return toItem(ref, await stat11(ref));
6173
+ return toItem(ref, await stat12(ref));
5998
6174
  } catch {
5999
6175
  return null;
6000
6176
  }
@@ -6181,18 +6357,18 @@ function createClaudeCodeAdapter() {
6181
6357
  status,
6182
6358
  defaults: (cwd) => readAgentDefaults(cwd),
6183
6359
  /** La carpeta de la CLI. No se lista ni se entra: regla 2.1. */
6184
- protectedDirs: () => [path19.join(homedir5(), ".claude")],
6360
+ protectedDirs: () => [path21.join(homedir5(), ".claude")],
6185
6361
  dispose: () => status.dispose()
6186
6362
  };
6187
6363
  }
6188
6364
 
6189
6365
  // packages/server/src/agents/codex/index.ts
6190
6366
  import { homedir as homedir7 } from "node:os";
6191
- import path25 from "node:path";
6367
+ import path27 from "node:path";
6192
6368
 
6193
6369
  // packages/server/src/agents/codex/discovery.ts
6194
- import { stat as stat12 } from "node:fs/promises";
6195
- import path22 from "node:path";
6370
+ import { stat as stat13 } from "node:fs/promises";
6371
+ import path24 from "node:path";
6196
6372
 
6197
6373
  // packages/server/src/debug.ts
6198
6374
  var enabled = process.env["AGENT_WORKBENCH_DEBUG"] === "1";
@@ -6203,11 +6379,11 @@ function debugLog(scope, message) {
6203
6379
 
6204
6380
  // packages/server/src/agents/codex/find-rollout.ts
6205
6381
  import { readdir as readdir8 } from "node:fs/promises";
6206
- import path21 from "node:path";
6382
+ import path23 from "node:path";
6207
6383
 
6208
6384
  // packages/server/src/agents/codex/paths.ts
6209
6385
  import { homedir as homedir6 } from "node:os";
6210
- import path20 from "node:path";
6386
+ import path22 from "node:path";
6211
6387
  var INTERACTIVE_SOURCES = ["cli", "vscode"];
6212
6388
  var LISTED_HISTORY_MODES = ["legacy", "paginated"];
6213
6389
  var ROLLOUT_FILE_PATTERN = /^rollout-(\d{4})-(\d{2})-(\d{2})T(\d{2})-(\d{2})-(\d{2})-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
@@ -6219,13 +6395,13 @@ function isUuid(value) {
6219
6395
  }
6220
6396
  var warnedHomes = /* @__PURE__ */ new Set();
6221
6397
  function isFullyQualified(candidate) {
6222
- if (!path20.isAbsolute(candidate)) return false;
6398
+ if (!path22.isAbsolute(candidate)) return false;
6223
6399
  return process.platform !== "win32" || /^([a-zA-Z]:[\\/]|[\\/]{2})/.test(candidate);
6224
6400
  }
6225
6401
  function codexHome() {
6226
6402
  const configured = process.env["CODEX_HOME"];
6227
6403
  if (configured === void 0 || configured.length === 0) {
6228
- return path20.join(homedir6(), ".codex");
6404
+ return path22.join(homedir6(), ".codex");
6229
6405
  }
6230
6406
  if (isFullyQualified(configured)) return configured;
6231
6407
  if (!warnedHomes.has(configured)) {
@@ -6238,11 +6414,11 @@ function codexHome() {
6238
6414
  }
6239
6415
  function codexSessionsRoot() {
6240
6416
  const home = codexHome();
6241
- return home === null ? null : path20.join(home, "sessions");
6417
+ return home === null ? null : path22.join(home, "sessions");
6242
6418
  }
6243
6419
  function codexArchivedRoot() {
6244
6420
  const home = codexHome();
6245
- return home === null ? null : path20.join(home, "archived_sessions");
6421
+ return home === null ? null : path22.join(home, "archived_sessions");
6246
6422
  }
6247
6423
  function parseRolloutFileName(name) {
6248
6424
  const match = ROLLOUT_FILE_PATTERN.exec(name);
@@ -6269,7 +6445,7 @@ function localDayFolder(root, epochMs) {
6269
6445
  const year = String(date.getFullYear()).padStart(4, "0");
6270
6446
  const month = String(date.getMonth() + 1).padStart(2, "0");
6271
6447
  const day = String(date.getDate()).padStart(2, "0");
6272
- return path20.join(root, year, month, day);
6448
+ return path22.join(root, year, month, day);
6273
6449
  }
6274
6450
  function localDayFolders(root, fromMs, toMs) {
6275
6451
  const folders = [];
@@ -6301,14 +6477,14 @@ async function numberedFolders(folder, pattern) {
6301
6477
  return (await entriesOf(folder)).filter((entry) => entry.isDirectory() && pattern.test(entry.name)).map((entry) => entry.name).sort();
6302
6478
  }
6303
6479
  async function rolloutFilesIn(folder) {
6304
- return (await entriesOf(folder)).filter((entry) => entry.isFile() && parseRolloutFileName(entry.name) !== null).map((entry) => entry.name).sort().map((name) => path21.join(folder, name));
6480
+ return (await entriesOf(folder)).filter((entry) => entry.isFile() && parseRolloutFileName(entry.name) !== null).map((entry) => entry.name).sort().map((name) => path23.join(folder, name));
6305
6481
  }
6306
6482
  async function walkSessionFiles(root, limit = Number.POSITIVE_INFINITY) {
6307
6483
  const files = [];
6308
6484
  for (const year of await numberedFolders(root, YEAR)) {
6309
- for (const month of await numberedFolders(path21.join(root, year), TWO_DIGITS)) {
6310
- for (const day of await numberedFolders(path21.join(root, year, month), TWO_DIGITS)) {
6311
- for (const file of await rolloutFilesIn(path21.join(root, year, month, day))) {
6485
+ for (const month of await numberedFolders(path23.join(root, year), TWO_DIGITS)) {
6486
+ for (const day of await numberedFolders(path23.join(root, year, month), TWO_DIGITS)) {
6487
+ for (const file of await rolloutFilesIn(path23.join(root, year, month, day))) {
6312
6488
  files.push(file);
6313
6489
  if (files.length >= limit) return files;
6314
6490
  }
@@ -6319,7 +6495,7 @@ async function walkSessionFiles(root, limit = Number.POSITIVE_INFINITY) {
6319
6495
  }
6320
6496
  function matching(files, sessionId) {
6321
6497
  for (const file of files) {
6322
- if (parseRolloutFileName(path21.basename(file))?.sessionId === sessionId) return file;
6498
+ if (parseRolloutFileName(path23.basename(file))?.sessionId === sessionId) return file;
6323
6499
  }
6324
6500
  return null;
6325
6501
  }
@@ -6687,7 +6863,7 @@ var CodexSessionDiscovery = class {
6687
6863
  if (this.seen.has(filePath)) continue;
6688
6864
  let info;
6689
6865
  try {
6690
- info = await stat12(filePath);
6866
+ info = await stat13(filePath);
6691
6867
  } catch {
6692
6868
  continue;
6693
6869
  }
@@ -6726,7 +6902,7 @@ var CodexSessionDiscovery = class {
6726
6902
  this.stopTimerIfIdle();
6727
6903
  }
6728
6904
  async readHead(filePath, size) {
6729
- const name = parseRolloutFileName(path22.basename(filePath));
6905
+ const name = parseRolloutFileName(path24.basename(filePath));
6730
6906
  if (name === null) return { kind: "rejected" };
6731
6907
  let lines;
6732
6908
  try {
@@ -6774,8 +6950,8 @@ var CodexSessionDiscovery = class {
6774
6950
  };
6775
6951
 
6776
6952
  // packages/server/src/agents/codex/history.ts
6777
- import { stat as stat13 } from "node:fs/promises";
6778
- import path24 from "node:path";
6953
+ import { stat as stat14 } from "node:fs/promises";
6954
+ import path26 from "node:path";
6779
6955
 
6780
6956
  // packages/server/src/agents/codex/rollout-scan.ts
6781
6957
  var TAIL_MAX_BYTES2 = 256 * 1024;
@@ -6849,7 +7025,7 @@ async function scanRollout(item) {
6849
7025
  }
6850
7026
 
6851
7027
  // packages/server/src/agents/codex/session-follower.ts
6852
- import path23 from "node:path";
7028
+ import path25 from "node:path";
6853
7029
 
6854
7030
  // packages/server/src/agents/codex/rollout-image.ts
6855
7031
  import { createReadStream } from "node:fs";
@@ -7217,7 +7393,7 @@ var CodexSessionFollower = class {
7217
7393
  return own === filePath || normalizeCwdKey(own, process.platform) === normalizeCwdKey(filePath, process.platform);
7218
7394
  }
7219
7395
  if (this.sessionId.length === 0) return false;
7220
- if (parseRolloutFileName(path23.basename(filePath))?.sessionId !== this.sessionId.toLowerCase()) return false;
7396
+ if (parseRolloutFileName(path25.basename(filePath))?.sessionId !== this.sessionId.toLowerCase()) return false;
7221
7397
  this.jsonl.setPath(filePath);
7222
7398
  return true;
7223
7399
  }
@@ -7255,26 +7431,26 @@ var CodexSessionFollower = class {
7255
7431
  // packages/server/src/agents/codex/history.ts
7256
7432
  async function pathExists2(candidate) {
7257
7433
  try {
7258
- await stat13(candidate);
7434
+ await stat14(candidate);
7259
7435
  return true;
7260
7436
  } catch {
7261
7437
  return false;
7262
7438
  }
7263
7439
  }
7264
- var accepts = (filePath) => ROLLOUT_FILE_PATTERN.test(path24.basename(filePath));
7440
+ var accepts = (filePath) => ROLLOUT_FILE_PATTERN.test(path26.basename(filePath));
7265
7441
  var CODEX_FOLLOW_POLL_MS = 1e3;
7266
7442
  function isInside2(root, filePath) {
7267
- const relative = path24.relative(root, filePath);
7268
- return relative.length > 0 && !relative.startsWith("..") && !path24.isAbsolute(relative);
7443
+ const relative = path26.relative(root, filePath);
7444
+ return relative.length > 0 && !relative.startsWith("..") && !path26.isAbsolute(relative);
7269
7445
  }
7270
7446
  function toItem2(ref, info) {
7271
- const name = parseRolloutFileName(path24.basename(ref));
7447
+ const name = parseRolloutFileName(path26.basename(ref));
7272
7448
  if (name === null) return null;
7273
7449
  return { ref, sessionId: name.sessionId, group: name.sessionId, mtimeMs: info.mtimeMs, sizeBytes: info.size };
7274
7450
  }
7275
7451
  async function itemOf(ref) {
7276
7452
  try {
7277
- return toItem2(ref, await stat13(ref));
7453
+ return toItem2(ref, await stat14(ref));
7278
7454
  } catch {
7279
7455
  return null;
7280
7456
  }
@@ -7468,7 +7644,7 @@ function createCodexAdapter() {
7468
7644
  * credenciales de antes de cambiarla.
7469
7645
  */
7470
7646
  protectedDirs: () => {
7471
- const dirs = [path25.join(homedir7(), ".codex")];
7647
+ const dirs = [path27.join(homedir7(), ".codex")];
7472
7648
  const home = codexHome();
7473
7649
  if (home !== null && !dirs.includes(home)) dirs.unshift(home);
7474
7650
  return dirs;
@@ -7661,7 +7837,7 @@ var DbChangeSignal = class {
7661
7837
  };
7662
7838
 
7663
7839
  // packages/server/src/agents/opencode/history.ts
7664
- import { stat as stat14 } from "node:fs/promises";
7840
+ import { stat as stat15 } from "node:fs/promises";
7665
7841
 
7666
7842
  // packages/server/src/agents/opencode/events.ts
7667
7843
  var TOOL_INPUT_PARSE_MAX_CHARS = 16e3;
@@ -8612,7 +8788,7 @@ function createOpenCodeHistory(deps) {
8612
8788
  async rootExists() {
8613
8789
  if (dbFile === null) return false;
8614
8790
  try {
8615
- await stat14(dbFile);
8791
+ await stat15(dbFile);
8616
8792
  return true;
8617
8793
  } catch {
8618
8794
  return false;
@@ -8644,13 +8820,13 @@ function createOpenCodeHistory(deps) {
8644
8820
  var OPENCODE_SESSION_ID_PATTERN = /^ses_[0-9A-Za-z]{20,40}$/;
8645
8821
 
8646
8822
  // packages/server/src/agents/opencode/paths.ts
8647
- import path26 from "node:path";
8823
+ import path28 from "node:path";
8648
8824
  function isFullyQualified2(candidate, platform) {
8649
8825
  if (platform === "win32") return /^([a-zA-Z]:[\\/]|[\\/]{2})/.test(candidate);
8650
- return path26.posix.isAbsolute(candidate);
8826
+ return path28.posix.isAbsolute(candidate);
8651
8827
  }
8652
8828
  function resolveOpenCodePaths(env, home, platform) {
8653
- const flavor = platform === "win32" ? path26.win32 : path26.posix;
8829
+ const flavor = platform === "win32" ? path28.win32 : path28.posix;
8654
8830
  const variable = (name) => {
8655
8831
  const value = env[name];
8656
8832
  return value === void 0 || value.length === 0 ? null : value;
@@ -9098,8 +9274,8 @@ function launchWithServe(deps, input) {
9098
9274
  import { execFile as execFile2, spawn as nodeSpawn } from "node:child_process";
9099
9275
  import { randomBytes } from "node:crypto";
9100
9276
  import { mkdir as mkdir4 } from "node:fs/promises";
9101
- import { tmpdir as tmpdir3 } from "node:os";
9102
- import path27 from "node:path";
9277
+ import { tmpdir as tmpdir4 } from "node:os";
9278
+ import path29 from "node:path";
9103
9279
  var OPENCODE_SERVE_HOST = "127.0.0.1";
9104
9280
  var OPENCODE_SERVE_START_TIMEOUT_MS = 2e4;
9105
9281
  var OPENCODE_SERVE_IDLE_STOP_MS = 3e5;
@@ -9131,7 +9307,7 @@ function parseListeningLine(line) {
9131
9307
  return `http://${OPENCODE_SERVE_HOST}:${found.port}`;
9132
9308
  }
9133
9309
  function defaultServeCwd() {
9134
- return path27.join(tmpdir3(), "agent-workbench", "opencode-serve");
9310
+ return path29.join(tmpdir4(), "agent-workbench", "opencode-serve");
9135
9311
  }
9136
9312
  var ServeStartError = class extends Error {
9137
9313
  };
@@ -10366,7 +10542,7 @@ async function migrateLegacyConfigDir() {
10366
10542
 
10367
10543
  // packages/server/src/conversation-hub.ts
10368
10544
  import { EventEmitter } from "node:events";
10369
- import path28 from "node:path";
10545
+ import path30 from "node:path";
10370
10546
  var INITIAL_EVENT_LIMIT = 300;
10371
10547
  var MAX_PAGE_LIMIT = 500;
10372
10548
  var PENDING_QUESTION_LOOKBACK = 50;
@@ -10427,7 +10603,7 @@ var ConversationHub = class extends EventEmitter {
10427
10603
  await entry.follower.start();
10428
10604
  debugLog(
10429
10605
  "conversation",
10430
- `following ${path28.basename(entry.follower.label)} for ${terminalId.slice(0, 8)}`
10606
+ `following ${path30.basename(entry.follower.label)} for ${terminalId.slice(0, 8)}`
10431
10607
  );
10432
10608
  entry = this.entries.get(terminalId) ?? entry;
10433
10609
  }
@@ -10592,7 +10768,7 @@ var ConversationHub = class extends EventEmitter {
10592
10768
  this.startFollowTicker(terminalId, next);
10593
10769
  debugLog(
10594
10770
  "conversation",
10595
- `${terminalId.slice(0, 8)} changed session: following ${path28.basename(next.follower.label)}`
10771
+ `${terminalId.slice(0, 8)} changed session: following ${path30.basename(next.follower.label)}`
10596
10772
  );
10597
10773
  await next.follower.start();
10598
10774
  next.primed = true;
@@ -10956,18 +11132,18 @@ function openBrowser(url) {
10956
11132
  // packages/server/src/pty-helper.ts
10957
11133
  import { chmodSync, statSync } from "node:fs";
10958
11134
  import { createRequire } from "node:module";
10959
- import path29 from "node:path";
11135
+ import path31 from "node:path";
10960
11136
  function helperCandidates(packageDir, platform, arch) {
10961
11137
  return [
10962
- path29.join(packageDir, "build", "Release", "spawn-helper"),
10963
- path29.join(packageDir, "build", "Debug", "spawn-helper"),
10964
- path29.join(packageDir, "prebuilds", `${platform}-${arch}`, "spawn-helper")
11138
+ path31.join(packageDir, "build", "Release", "spawn-helper"),
11139
+ path31.join(packageDir, "build", "Debug", "spawn-helper"),
11140
+ path31.join(packageDir, "prebuilds", `${platform}-${arch}`, "spawn-helper")
10965
11141
  ];
10966
11142
  }
10967
11143
  function locateNodePty() {
10968
11144
  try {
10969
11145
  const require2 = createRequire(import.meta.url);
10970
- return path29.resolve(path29.dirname(require2.resolve("node-pty")), "..");
11146
+ return path31.resolve(path31.dirname(require2.resolve("node-pty")), "..");
10971
11147
  } catch {
10972
11148
  return null;
10973
11149
  }
@@ -11628,24 +11804,24 @@ var RemoteAccessService = class {
11628
11804
  };
11629
11805
 
11630
11806
  // packages/server/src/remote-devices-store.ts
11631
- import { chmod, mkdir as mkdir6, readFile as readFile9, stat as stat16 } from "node:fs/promises";
11632
- import path31 from "node:path";
11807
+ import { chmod, mkdir as mkdir6, readFile as readFile10, stat as stat17 } from "node:fs/promises";
11808
+ import path33 from "node:path";
11633
11809
 
11634
11810
  // packages/server/src/vault/write.ts
11635
11811
  import { randomBytes as randomBytes2 } from "node:crypto";
11636
- import { mkdir as mkdir5, rename as rename3, rm as rm2, stat as stat15, writeFile as writeFile3 } from "node:fs/promises";
11812
+ import { mkdir as mkdir5, rename as rename3, rm as rm2, stat as stat16, writeFile as writeFile3 } from "node:fs/promises";
11637
11813
 
11638
11814
  // packages/server/src/vault/paths.ts
11639
11815
  import { createHash as createHash2 } from "node:crypto";
11640
- import path30 from "node:path";
11816
+ import path32 from "node:path";
11641
11817
  function defaultVaultDir() {
11642
- return path30.join(appConfigDir(), "vault");
11818
+ return path32.join(appConfigDir(), "vault");
11643
11819
  }
11644
11820
  function vaultMarkerFile(dir) {
11645
- return path30.join(dir, "vault.json");
11821
+ return path32.join(dir, "vault.json");
11646
11822
  }
11647
11823
  function sessionsRoot2(dir) {
11648
- return path30.join(dir, "sessions");
11824
+ return path32.join(dir, "sessions");
11649
11825
  }
11650
11826
  function isSafeSessionId(sessionId) {
11651
11827
  return isVaultSessionId(sessionId);
@@ -11659,41 +11835,41 @@ function assertSessionKey(agent, sessionId) {
11659
11835
  }
11660
11836
  function agentSessionsDir(dir, agent) {
11661
11837
  if (!isSessionAgentId(agent)) throw new Error(`Unknown source for the local copy: ${JSON.stringify(agent)}`);
11662
- return path30.join(sessionsRoot2(dir), agent);
11838
+ return path32.join(sessionsRoot2(dir), agent);
11663
11839
  }
11664
11840
  function sessionFile(dir, agent, sessionId) {
11665
11841
  assertSessionKey(agent, sessionId);
11666
- return path30.join(sessionsRoot2(dir), agent, `${sessionId}.jsonl`);
11842
+ return path32.join(sessionsRoot2(dir), agent, `${sessionId}.jsonl`);
11667
11843
  }
11668
11844
  function assetsDir(dir, agent, sessionId) {
11669
11845
  assertSessionKey(agent, sessionId);
11670
- return path30.join(sessionsRoot2(dir), agent, `${sessionId}.assets`);
11846
+ return path32.join(sessionsRoot2(dir), agent, `${sessionId}.assets`);
11671
11847
  }
11672
11848
  function assetFile(dir, agent, sessionId, asset) {
11673
11849
  if (!VAULT_ASSET_NAME_PATTERN.test(asset)) throw new Error(`Invalid asset name: ${JSON.stringify(asset)}`);
11674
- return path30.join(assetsDir(dir, agent, sessionId), asset);
11850
+ return path32.join(assetsDir(dir, agent, sessionId), asset);
11675
11851
  }
11676
11852
  function projectFolderName(cwd, platform) {
11677
11853
  const hash = createHash2("sha256").update(normalizeCwdKey(cwd, platform)).digest("hex").slice(0, 8);
11678
- const base = (platform === "win32" ? path30.win32 : path30.posix).basename(cwd);
11854
+ const base = (platform === "win32" ? path32.win32 : path32.posix).basename(cwd);
11679
11855
  const name = base.length === 0 ? "proyecto" : sanitizeFileName(base, 60);
11680
11856
  return `${name}-${hash}`;
11681
11857
  }
11682
11858
  function memoryDir(dir, cwd, platform) {
11683
- return path30.join(dir, "memory", projectFolderName(cwd, platform));
11859
+ return path32.join(dir, "memory", projectFolderName(cwd, platform));
11684
11860
  }
11685
11861
  function projectExportDir(dir, cwd, platform) {
11686
- return path30.join(dir, "export", projectFolderName(cwd, platform));
11862
+ return path32.join(dir, "export", projectFolderName(cwd, platform));
11687
11863
  }
11688
11864
  function projectExportDirFor(dir, project, platform) {
11689
11865
  if (project.cwd.length > 0) return projectExportDir(dir, project.cwd, platform);
11690
11866
  const hash = createHash2("sha256").update(project.key).digest("hex").slice(0, 8);
11691
11867
  const name = project.fallbackName.length > 0 ? sanitizeFileName(project.fallbackName, 60) : "proyecto";
11692
- return path30.join(dir, "export", `${name}-${hash}`);
11868
+ return path32.join(dir, "export", `${name}-${hash}`);
11693
11869
  }
11694
11870
  function sessionExportFile(dir, agent, sessionId) {
11695
11871
  assertSessionKey(agent, sessionId);
11696
- return path30.join(dir, "export", "sesiones", `${agent}-${sessionId}.md`);
11872
+ return path32.join(dir, "export", "sesiones", `${agent}-${sessionId}.md`);
11697
11873
  }
11698
11874
  var RESERVED_WINDOWS_NAME = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;
11699
11875
  function sanitizeFileName(text, max = 80) {
@@ -11748,7 +11924,7 @@ async function writeFileAtomic(target, data, deps = DEFAULT_WRITE_DEPS) {
11748
11924
  async function ensureVaultMarker(dir, deps = DEFAULT_WRITE_DEPS) {
11749
11925
  const marker = vaultMarkerFile(dir);
11750
11926
  try {
11751
- await stat15(marker);
11927
+ await stat16(marker);
11752
11928
  return;
11753
11929
  } catch {
11754
11930
  }
@@ -11758,7 +11934,7 @@ async function ensureVaultMarker(dir, deps = DEFAULT_WRITE_DEPS) {
11758
11934
  }
11759
11935
  async function assetPresent(file, size) {
11760
11936
  try {
11761
- const info = await stat15(file);
11937
+ const info = await stat16(file);
11762
11938
  return info.isFile() && info.size === size;
11763
11939
  } catch {
11764
11940
  return false;
@@ -11800,7 +11976,7 @@ var RemoteDevicesStore = class {
11800
11976
  async load() {
11801
11977
  let raw;
11802
11978
  try {
11803
- raw = await readFile9(this.filePath, "utf8");
11979
+ raw = await readFile10(this.filePath, "utf8");
11804
11980
  } catch {
11805
11981
  return [];
11806
11982
  }
@@ -11821,7 +11997,7 @@ var RemoteDevicesStore = class {
11821
11997
  save(devices) {
11822
11998
  const text = serializeRemoteDevices(devices);
11823
11999
  const run = async () => {
11824
- await mkdir6(path31.dirname(this.filePath), { recursive: true });
12000
+ await mkdir6(path33.dirname(this.filePath), { recursive: true });
11825
12001
  await writeFileAtomic(this.filePath, text);
11826
12002
  await chmod(this.filePath, 384).catch(() => void 0);
11827
12003
  };
@@ -11840,7 +12016,7 @@ var RemoteDevicesStore = class {
11840
12016
  */
11841
12017
  async stamp() {
11842
12018
  try {
11843
- const info = await stat16(this.filePath);
12019
+ const info = await stat17(this.filePath);
11844
12020
  return `${info.mtimeMs}:${info.size}`;
11845
12021
  } catch {
11846
12022
  return "";
@@ -11954,8 +12130,8 @@ import chokidar from "chokidar";
11954
12130
 
11955
12131
  // packages/server/src/git-repo.ts
11956
12132
  import { execFile as execFile4 } from "node:child_process";
11957
- import { readFile as readFile10 } from "node:fs/promises";
11958
- import path32 from "node:path";
12133
+ import { readFile as readFile11 } from "node:fs/promises";
12134
+ import path34 from "node:path";
11959
12135
  function gitOutputText(output, command) {
11960
12136
  return output.length > 0 ? serverText("raw", { text: output }) : serverText("gitCommandFailed", { command });
11961
12137
  }
@@ -12016,7 +12192,7 @@ async function resolveRepo(cwd) {
12016
12192
  const result = await runGit(cwd, ["rev-parse", "--show-toplevel"]);
12017
12193
  if (result.code === 0) {
12018
12194
  const root = result.stdout.trim();
12019
- if (root.length > 0) return { state: "ready", root: path32.resolve(root), message: null };
12195
+ if (root.length > 0) return { state: "ready", root: path34.resolve(root), message: null };
12020
12196
  }
12021
12197
  const stderr = result.stderr.trim();
12022
12198
  if (/not a git repository|no es un repositorio/i.test(stderr)) {
@@ -12043,7 +12219,7 @@ async function findGitDir(cwd) {
12043
12219
  const result = await runGit(cwd, ["rev-parse", "--absolute-git-dir"]);
12044
12220
  if (result.code !== 0) return null;
12045
12221
  const dir = result.stdout.trim();
12046
- return dir.length > 0 ? path32.resolve(dir) : null;
12222
+ return dir.length > 0 ? path34.resolve(dir) : null;
12047
12223
  }
12048
12224
  function letterToKind(letter) {
12049
12225
  switch (letter) {
@@ -12146,7 +12322,7 @@ function parseWorktrees(raw, currentRoot) {
12146
12322
  worktrees.push({
12147
12323
  path: current.path,
12148
12324
  branch: current.branch,
12149
- isCurrent: path32.resolve(current.path) === path32.resolve(currentRoot)
12325
+ isCurrent: path34.resolve(current.path) === path34.resolve(currentRoot)
12150
12326
  });
12151
12327
  current = null;
12152
12328
  };
@@ -12289,7 +12465,7 @@ function looksBinary(buffer) {
12289
12465
  async function readUntrackedAsDiff(absolutePath, relativePath) {
12290
12466
  let content;
12291
12467
  try {
12292
- content = await readFile10(absolutePath);
12468
+ content = await readFile11(absolutePath);
12293
12469
  } catch {
12294
12470
  return {
12295
12471
  path: relativePath,
@@ -12337,7 +12513,7 @@ async function readUntrackedAsDiff(absolutePath, relativePath) {
12337
12513
  async function readDiff(options) {
12338
12514
  const { repoRoot: repoRoot2, relativePath, staged, untracked } = options;
12339
12515
  if (untracked && !staged) {
12340
- return readUntrackedAsDiff(path32.join(repoRoot2, relativePath), relativePath);
12516
+ return readUntrackedAsDiff(path34.join(repoRoot2, relativePath), relativePath);
12341
12517
  }
12342
12518
  const args = [
12343
12519
  "diff",
@@ -12557,7 +12733,7 @@ var RepoHub = class extends EventEmitter2 {
12557
12733
  // packages/server/src/memory-hub.ts
12558
12734
  import { EventEmitter as EventEmitter3 } from "node:events";
12559
12735
  import { homedir as homedir10 } from "node:os";
12560
- import path34 from "node:path";
12736
+ import path36 from "node:path";
12561
12737
  import chokidar2 from "chokidar";
12562
12738
 
12563
12739
  // packages/server/src/memory-bridge.ts
@@ -12567,14 +12743,14 @@ import {
12567
12743
  mkdir as mkdir7,
12568
12744
  open as open6,
12569
12745
  readdir as readdir9,
12570
- readFile as readFile11,
12746
+ readFile as readFile12,
12571
12747
  realpath as realpath2,
12572
12748
  rename as rename4,
12573
- stat as stat17,
12749
+ stat as stat18,
12574
12750
  unlink as unlink3
12575
12751
  } from "node:fs/promises";
12576
12752
  import { homedir as homedir9 } from "node:os";
12577
- import path33 from "node:path";
12753
+ import path35 from "node:path";
12578
12754
  var MemoryBridgeError = class extends ServerTextError {
12579
12755
  };
12580
12756
  var MAX_LISTED_NOTES = 500;
@@ -12592,9 +12768,9 @@ var GITIGNORE_MEMORY_LINE = `${MEMORY_DIR}/`;
12592
12768
  var BOM2 = Buffer.from([239, 187, 191]);
12593
12769
  async function inspectMemory(cwd, home = homedir9()) {
12594
12770
  await assertMemoryDirInside(cwd);
12595
- const folder = path33.join(cwd, MEMORY_DIR);
12771
+ const folder = path35.join(cwd, MEMORY_DIR);
12596
12772
  const folderExists = await isDirectory2(folder);
12597
- const indexExists = folderExists && await isFile(path33.join(folder, MEMORY_INDEX_FILE));
12773
+ const indexExists = folderExists && await isFile(path35.join(folder, MEMORY_INDEX_FILE));
12598
12774
  const noteNames = folderExists ? await listNoteFiles(folder) : [];
12599
12775
  const notes = await describeNotes(folder, noteNames);
12600
12776
  const files = await Promise.all(
@@ -12626,7 +12802,7 @@ async function installMemory(cwd, options, home = homedir9()) {
12626
12802
  }
12627
12803
  async function importNativeMemory(cwd, home = homedir9()) {
12628
12804
  await assertMemoryDirInside(cwd);
12629
- if (!await isDirectory2(path33.join(cwd, MEMORY_DIR))) {
12805
+ if (!await isDirectory2(path35.join(cwd, MEMORY_DIR))) {
12630
12806
  throw new MemoryBridgeError(serverText("memoryNotInstalled"));
12631
12807
  }
12632
12808
  const plan = new PlanBuilder(cwd);
@@ -12644,9 +12820,9 @@ async function readMemoryNote(cwd, name) {
12644
12820
  }
12645
12821
  const absolute = await resolveInside(cwd, `${MEMORY_DIR}/${name}`);
12646
12822
  try {
12647
- const info = await stat17(absolute);
12823
+ const info = await stat18(absolute);
12648
12824
  if (!info.isFile()) return null;
12649
- const content = await readFile11(absolute);
12825
+ const content = await readFile12(absolute);
12650
12826
  return {
12651
12827
  name,
12652
12828
  text: content.subarray(0, MAX_NOTE_BYTES).toString("utf8"),
@@ -12661,7 +12837,7 @@ function isNoteName(name) {
12661
12837
  if (name.length === 0 || name.length > 255) return false;
12662
12838
  if (name.includes("\0")) return false;
12663
12839
  if (name === "." || name === "..") return false;
12664
- if (path33.basename(name) !== name) return false;
12840
+ if (path35.basename(name) !== name) return false;
12665
12841
  if (/[\\/:*?"<>|\u0000-\u001f]/.test(name)) return false;
12666
12842
  return name.toLowerCase().endsWith(".md");
12667
12843
  }
@@ -12698,7 +12874,7 @@ var PlanBuilder = class {
12698
12874
  /** Nombres de nota que habria en la carpeta, en minusculas. */
12699
12875
  async noteNames() {
12700
12876
  const names = /* @__PURE__ */ new Map();
12701
- for (const name of await listNoteFiles(path33.join(this.cwd, MEMORY_DIR))) {
12877
+ for (const name of await listNoteFiles(path35.join(this.cwd, MEMORY_DIR))) {
12702
12878
  names.set(name.toLowerCase(), name);
12703
12879
  }
12704
12880
  for (const rel of this.files.keys()) {
@@ -12741,7 +12917,7 @@ var PlanBuilder = class {
12741
12917
  continue;
12742
12918
  }
12743
12919
  const target = await realpath2(absolute);
12744
- const info = await stat17(target);
12920
+ const info = await stat18(target);
12745
12921
  if (info.nlink > 1) {
12746
12922
  throw new MemoryBridgeError(serverText("memoryHardLinks", { file: file.rel }));
12747
12923
  }
@@ -12760,7 +12936,7 @@ var PlanBuilder = class {
12760
12936
  );
12761
12937
  if (pending.length === 0) return;
12762
12938
  if (pending.some((file) => file.rel.startsWith(`${MEMORY_DIR}/`))) {
12763
- await mkdir7(path33.join(this.cwd, MEMORY_DIR), { recursive: true });
12939
+ await mkdir7(path35.join(this.cwd, MEMORY_DIR), { recursive: true });
12764
12940
  }
12765
12941
  for (const file of pending) {
12766
12942
  if (file.target === null) throw new Error(`Unvalidated plan: ${file.rel}`);
@@ -12871,7 +13047,7 @@ async function planNativeImport(plan, cwd, home) {
12871
13047
  const local = await plan.noteNames();
12872
13048
  for (const name of await listNoteFiles(folder)) {
12873
13049
  const key = name.toLowerCase();
12874
- const content = await readIfFile(path33.join(folder, name), name);
13050
+ const content = await readIfFile(path35.join(folder, name), name);
12875
13051
  if (content === null) continue;
12876
13052
  const already = chosen.get(key);
12877
13053
  if (already !== void 0) {
@@ -12885,7 +13061,7 @@ async function planNativeImport(plan, cwd, home) {
12885
13061
  if (existing !== null && !existing.equals(content)) skipped.push(name);
12886
13062
  continue;
12887
13063
  }
12888
- plan.put(`${MEMORY_DIR}/${name}`, null, content, "copy", "", path33.join(folder, name));
13064
+ plan.put(`${MEMORY_DIR}/${name}`, null, content, "copy", "", path35.join(folder, name));
12889
13065
  copied.push(name);
12890
13066
  }
12891
13067
  }
@@ -12895,7 +13071,7 @@ async function planNativeImport(plan, cwd, home) {
12895
13071
  const known = new Set(linkTargets(index.toString("utf8")));
12896
13072
  const additions = [];
12897
13073
  for (const folder of folders) {
12898
- const nativeIndex = await readIfFile(path33.join(folder, MEMORY_INDEX_FILE), MEMORY_INDEX_FILE);
13074
+ const nativeIndex = await readIfFile(path35.join(folder, MEMORY_INDEX_FILE), MEMORY_INDEX_FILE);
12899
13075
  if (nativeIndex === null) continue;
12900
13076
  for (const line of nativeIndex.toString("utf8").replace(/^\uFEFF/, "").split(/\r?\n/)) {
12901
13077
  const target = linkTargets(line)[0];
@@ -13120,7 +13296,7 @@ async function describeNotes(folder, names) {
13120
13296
  const stated = [];
13121
13297
  for (const name of names) {
13122
13298
  try {
13123
- const info = await stat17(path33.join(folder, name));
13299
+ const info = await stat18(path35.join(folder, name));
13124
13300
  if (info.isFile()) stated.push({ name, sizeBytes: info.size, modifiedAt: info.mtimeMs });
13125
13301
  } catch {
13126
13302
  }
@@ -13128,7 +13304,7 @@ async function describeNotes(folder, names) {
13128
13304
  stated.sort((a, b) => b.modifiedAt - a.modifiedAt || a.name.localeCompare(b.name));
13129
13305
  const notes = [];
13130
13306
  for (const item of stated.slice(0, MAX_LISTED_NOTES)) {
13131
- const head = await readHead(path33.join(folder, item.name));
13307
+ const head = await readHead(path35.join(folder, item.name));
13132
13308
  notes.push({ ...item, ...describeNoteText(head, item.name) });
13133
13309
  }
13134
13310
  return notes;
@@ -13186,28 +13362,28 @@ async function countNativeMemory(cwd, home, sharedNames) {
13186
13362
  return { available: names.size, pending };
13187
13363
  }
13188
13364
  async function nativeMemoryFolders(cwd, home) {
13189
- const root = path33.join(home, ".claude", "projects");
13365
+ const root = path35.join(home, ".claude", "projects");
13190
13366
  const slug = projectSlugFor(cwd).toLowerCase();
13191
13367
  try {
13192
13368
  const entries = await readdir9(root, { withFileTypes: true });
13193
- return entries.filter((entry) => entry.isDirectory() && entry.name.toLowerCase() === slug).map((entry) => entry.name).sort().map((name) => path33.join(root, name, "memory"));
13369
+ return entries.filter((entry) => entry.isDirectory() && entry.name.toLowerCase() === slug).map((entry) => entry.name).sort().map((name) => path35.join(root, name, "memory"));
13194
13370
  } catch {
13195
13371
  return [];
13196
13372
  }
13197
13373
  }
13198
13374
  function buildGlobalFragments(home) {
13199
13375
  const windows = process.platform === "win32";
13200
- const globalFile = path33.join(home, ...GLOBAL_MEMORY_RELATIVE.split("/"));
13376
+ const globalFile = path35.join(home, ...GLOBAL_MEMORY_RELATIVE.split("/"));
13201
13377
  const hardLink = (link) => windows ? `mklink /H "${link}" "${globalFile}"` : `ln "${globalFile}" "${link}"`;
13202
13378
  const hardLinkNote = windows ? serverText("memoryNoteHardLinkCmd", { path: GLOBAL_MEMORY_RELATIVE }) : serverText("memoryNoteHardLink", { path: GLOBAL_MEMORY_RELATIVE });
13203
- const codexTarget = path33.join(home, ".codex", "AGENTS.md");
13204
- const geminiTarget = path33.join(home, ".gemini", "GEMINI.md");
13379
+ const codexTarget = path35.join(home, ".codex", "AGENTS.md");
13380
+ const geminiTarget = path35.join(home, ".gemini", "GEMINI.md");
13205
13381
  const homeSlashes = home.replace(/\\/g, "/").replace(/\/+$/, "");
13206
13382
  return [
13207
13383
  {
13208
13384
  agent: "claude-code",
13209
13385
  label: MEMORY_AGENT_LABELS["claude-code"],
13210
- target: path33.join(home, ".claude", "CLAUDE.md"),
13386
+ target: path35.join(home, ".claude", "CLAUDE.md"),
13211
13387
  kind: "line",
13212
13388
  text: `@~/${GLOBAL_MEMORY_RELATIVE}`,
13213
13389
  note: serverText("memoryNoteClaude")
@@ -13231,7 +13407,7 @@ function buildGlobalFragments(home) {
13231
13407
  {
13232
13408
  agent: "opencode",
13233
13409
  label: MEMORY_AGENT_LABELS.opencode,
13234
- target: path33.join(home, ".config", "opencode", "opencode.json"),
13410
+ target: path35.join(home, ".config", "opencode", "opencode.json"),
13235
13411
  kind: "json",
13236
13412
  text: `"instructions": ["${homeSlashes}/${GLOBAL_MEMORY_RELATIVE}"]`,
13237
13413
  note: serverText("memoryNoteOpenCode")
@@ -13279,17 +13455,17 @@ async function readGit(cwd) {
13279
13455
  if (!toplevel || !gitDir || !commonDir) return fail(serverText("gitUnexpected", { command: "rev-parse" }));
13280
13456
  const ignored = await checkIgnored(cwd, INDEX_REL);
13281
13457
  if (typeof ignored !== "boolean") return fail(ignored);
13282
- const absoluteGitDir = path33.resolve(cwd, gitDir);
13283
- const absoluteCommonDir = path33.resolve(cwd, commonDir);
13458
+ const absoluteGitDir = path35.resolve(cwd, gitDir);
13459
+ const absoluteCommonDir = path35.resolve(cwd, commonDir);
13284
13460
  let worktree = null;
13285
13461
  let main2 = null;
13286
13462
  if (!await samePath(absoluteGitDir, absoluteCommonDir)) {
13287
- if (path33.basename(absoluteCommonDir).toLowerCase() === ".git") {
13288
- const mainRoot = path33.dirname(absoluteCommonDir);
13289
- const inner = path33.relative(path33.resolve(toplevel), path33.resolve(cwd));
13290
- const sameFolder = inner.length === 0 || inner.startsWith("..") || path33.isAbsolute(inner);
13291
- const mainPath = sameFolder ? mainRoot : path33.join(mainRoot, inner);
13292
- const memoryRel = sameFolder ? MEMORY_DIR : [...inner.split(path33.sep), MEMORY_DIR].join("/");
13463
+ if (path35.basename(absoluteCommonDir).toLowerCase() === ".git") {
13464
+ const mainRoot = path35.dirname(absoluteCommonDir);
13465
+ const inner = path35.relative(path35.resolve(toplevel), path35.resolve(cwd));
13466
+ const sameFolder = inner.length === 0 || inner.startsWith("..") || path35.isAbsolute(inner);
13467
+ const mainPath = sameFolder ? mainRoot : path35.join(mainRoot, inner);
13468
+ const memoryRel = sameFolder ? MEMORY_DIR : [...inner.split(path35.sep), MEMORY_DIR].join("/");
13293
13469
  main2 = { root: mainRoot, memoryRel };
13294
13470
  worktree = {
13295
13471
  mainPath,
@@ -13314,7 +13490,7 @@ async function samePath(a, b) {
13314
13490
  try {
13315
13491
  return await realpath2(value);
13316
13492
  } catch {
13317
- return path33.resolve(value);
13493
+ return path35.resolve(value);
13318
13494
  }
13319
13495
  };
13320
13496
  const [left, right] = await Promise.all([resolve(a), resolve(b)]);
@@ -13356,9 +13532,9 @@ async function listNoteFiles(folder) {
13356
13532
  }
13357
13533
  async function readIfFile(absolute, label) {
13358
13534
  try {
13359
- const info = await stat17(absolute);
13535
+ const info = await stat18(absolute);
13360
13536
  if (!info.isFile()) throw new MemoryBridgeError(serverText("memoryNotAFile", { label }));
13361
- return await readFile11(absolute);
13537
+ return await readFile12(absolute);
13362
13538
  } catch (error) {
13363
13539
  if (isNotFound(error)) return null;
13364
13540
  throw error;
@@ -13380,14 +13556,14 @@ async function readHead(absolute) {
13380
13556
  }
13381
13557
  async function isFile(absolute) {
13382
13558
  try {
13383
- return (await stat17(absolute)).isFile();
13559
+ return (await stat18(absolute)).isFile();
13384
13560
  } catch {
13385
13561
  return false;
13386
13562
  }
13387
13563
  }
13388
13564
  async function isDirectory2(absolute) {
13389
13565
  try {
13390
- return (await stat17(absolute)).isDirectory();
13566
+ return (await stat18(absolute)).isDirectory();
13391
13567
  } catch {
13392
13568
  return false;
13393
13569
  }
@@ -13405,7 +13581,7 @@ async function writeAtomic(target, content, mustNotExist) {
13405
13581
  await handle.close();
13406
13582
  }
13407
13583
  if (mustNotExist && await exists2(target)) {
13408
- throw new MemoryBridgeError(serverText("memoryAppeared", { file: path33.basename(target) }));
13584
+ throw new MemoryBridgeError(serverText("memoryAppeared", { file: path35.basename(target) }));
13409
13585
  }
13410
13586
  await renameWithRetry(temporary, target);
13411
13587
  } catch (error) {
@@ -13416,9 +13592,9 @@ async function writeAtomic(target, content, mustNotExist) {
13416
13592
  async function createTemporary(target) {
13417
13593
  for (let attempt = 1; ; attempt += 1) {
13418
13594
  const suffix = randomBytes4(6).toString("hex");
13419
- const temporary = path33.join(
13420
- path33.dirname(target),
13421
- `.${path33.basename(target)}.${process.pid}.${suffix}.tmp`
13595
+ const temporary = path35.join(
13596
+ path35.dirname(target),
13597
+ `.${path35.basename(target)}.${process.pid}.${suffix}.tmp`
13422
13598
  );
13423
13599
  try {
13424
13600
  return { temporary, handle: await open6(temporary, "wx") };
@@ -13430,7 +13606,7 @@ async function createTemporary(target) {
13430
13606
  }
13431
13607
  async function exists2(absolute) {
13432
13608
  try {
13433
- await stat17(absolute);
13609
+ await stat18(absolute);
13434
13610
  return true;
13435
13611
  } catch {
13436
13612
  return false;
@@ -13656,13 +13832,13 @@ var MemoryHub = class extends EventEmitter3 {
13656
13832
  }
13657
13833
  };
13658
13834
  function keyFor(cwd) {
13659
- const resolved = path34.resolve(cwd);
13835
+ const resolved = path36.resolve(cwd);
13660
13836
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
13661
13837
  }
13662
13838
  function isRelevantPath(cwd, candidate) {
13663
- const relative = path34.relative(cwd, candidate);
13839
+ const relative = path36.relative(cwd, candidate);
13664
13840
  if (relative.length === 0) return true;
13665
- if (relative.startsWith("..") || path34.isAbsolute(relative)) return false;
13841
+ if (relative.startsWith("..") || path36.isAbsolute(relative)) return false;
13666
13842
  const same = (a, b) => process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
13667
13843
  const parts = relative.split(/[\\/]/);
13668
13844
  const [first = "", second = "", third = ""] = parts;
@@ -13680,9 +13856,8 @@ function isRelevantPath(cwd, candidate) {
13680
13856
 
13681
13857
  // packages/server/src/paste-store.ts
13682
13858
  import { createHash as createHash3, randomUUID as randomUUID3 } from "node:crypto";
13683
- import { mkdir as mkdir8, readdir as readdir10, rm as rm3, stat as stat18, writeFile as writeFile4 } from "node:fs/promises";
13684
- import { tmpdir as tmpdir4 } from "node:os";
13685
- import path35 from "node:path";
13859
+ import { mkdir as mkdir8, readdir as readdir10, rm as rm3, stat as stat19, writeFile as writeFile4 } from "node:fs/promises";
13860
+ import path37 from "node:path";
13686
13861
 
13687
13862
  // packages/server/src/vault/markdown.ts
13688
13863
  var pad2 = (value) => String(value).padStart(2, "0");
@@ -14197,8 +14372,8 @@ function buildContinuationMessage(input) {
14197
14372
  }
14198
14373
  return sanitizeForPaste(lines.join("\n"));
14199
14374
  }
14200
- function transcriptReferenceFor(path50, style) {
14201
- return style === "at-quoted" ? fileReference(path50, "at-quoted") : `"${path50}"`;
14375
+ function transcriptReferenceFor(path52, style) {
14376
+ return style === "at-quoted" ? fileReference(path52, "at-quoted") : `"${path52}"`;
14202
14377
  }
14203
14378
  function planTranscript(input) {
14204
14379
  const turns = splitTurns(input.events);
@@ -14322,32 +14497,6 @@ function textWithAttachments(text, lines) {
14322
14497
  return lines.length === 0 ? text : [...lines, text].filter((piece) => piece.trim().length > 0).join("\n");
14323
14498
  }
14324
14499
 
14325
- // packages/server/src/image-signature.ts
14326
- var FORMATS = [
14327
- {
14328
- format: { ext: "png", mediaType: "image/png" },
14329
- test: (b) => b.length > 8 && b.subarray(0, 8).equals(Buffer.from("89504e470d0a1a0a", "hex"))
14330
- },
14331
- {
14332
- format: { ext: "jpg", mediaType: "image/jpeg" },
14333
- test: (b) => b.length > 3 && b[0] === 255 && b[1] === 216 && b[2] === 255
14334
- },
14335
- {
14336
- format: { ext: "gif", mediaType: "image/gif" },
14337
- test: (b) => b.length > 6 && b.subarray(0, 4).toString("ascii") === "GIF8"
14338
- },
14339
- {
14340
- format: { ext: "webp", mediaType: "image/webp" },
14341
- test: (b) => b.length > 12 && b.subarray(0, 4).toString("ascii") === "RIFF" && b.subarray(8, 12).toString("ascii") === "WEBP"
14342
- }
14343
- ];
14344
- var IMAGE_MEDIA_TYPES = new Set(
14345
- FORMATS.map((entry) => entry.format.mediaType)
14346
- );
14347
- function detectImageFormat(bytes) {
14348
- return FORMATS.find((entry) => entry.test(bytes))?.format ?? null;
14349
- }
14350
-
14351
14500
  // packages/server/src/paste-store.ts
14352
14501
  var EXTENSIONS = /* @__PURE__ */ new Map([
14353
14502
  ["image/png", "png"],
@@ -14373,7 +14522,7 @@ var PasteStore = class {
14373
14522
  root;
14374
14523
  counter = 0;
14375
14524
  constructor(root) {
14376
- this.root = root ?? path35.join(tmpdir4(), "agent-workbench", "pasted");
14525
+ this.root = root ?? pasteRoot();
14377
14526
  }
14378
14527
  /**
14379
14528
  * Guarda una imagen y devuelve su ruta.
@@ -14400,11 +14549,11 @@ var PasteStore = class {
14400
14549
  if (signature === null) {
14401
14550
  throw new PasteImageError(serverText("imageUnknownFormat"));
14402
14551
  }
14403
- const directory = path35.join(this.root, safeSegment(terminalId));
14552
+ const directory = path37.join(this.root, safeSegment(terminalId));
14404
14553
  await mkdir8(directory, { recursive: true, mode: DIRECTORY_MODE });
14405
14554
  this.counter += 1;
14406
14555
  const name = `pegada-${this.counter}-${randomUUID3().slice(0, 8)}.${signature.ext}`;
14407
- const file = path35.join(directory, name);
14556
+ const file = path37.join(directory, name);
14408
14557
  await writeFile4(file, bytes, { mode: FILE_MODE });
14409
14558
  return { path: file, bytes: bytes.length };
14410
14559
  }
@@ -14422,11 +14571,11 @@ var PasteStore = class {
14422
14571
  if (bytes.length > MAX_TEXT_BYTES) {
14423
14572
  throw new PasteTextError(`The text is ${bytes.length} bytes; the maximum is ${MAX_TEXT_BYTES}.`);
14424
14573
  }
14425
- const directory = path35.join(this.root, safeSegment(terminalId));
14574
+ const directory = path37.join(this.root, safeSegment(terminalId));
14426
14575
  await mkdir8(directory, { recursive: true, mode: DIRECTORY_MODE });
14427
14576
  this.counter += 1;
14428
14577
  const name = `${baseName}-${this.counter}-${randomUUID3().slice(0, 8)}.md`;
14429
- const file = path35.join(directory, name);
14578
+ const file = path37.join(directory, name);
14430
14579
  await writeFile4(file, bytes, { mode: FILE_MODE });
14431
14580
  return { path: file, bytes: bytes.length };
14432
14581
  }
@@ -14456,17 +14605,17 @@ var PasteStore = class {
14456
14605
  })
14457
14606
  );
14458
14607
  }
14459
- const directory = path35.join(this.root, safeSegment(terminalId));
14608
+ const directory = path37.join(this.root, safeSegment(terminalId));
14460
14609
  await mkdir8(directory, { recursive: true, mode: DIRECTORY_MODE });
14461
14610
  this.counter += 1;
14462
14611
  const name = `adjunto-${this.counter}-${randomUUID3().slice(0, 8)}-${safe}`;
14463
- const file = path35.join(directory, name);
14612
+ const file = path37.join(directory, name);
14464
14613
  await writeFile4(file, bytes, { mode: FILE_MODE });
14465
14614
  return { path: file, bytes: bytes.length, name };
14466
14615
  }
14467
14616
  /** Borra lo de una pestana. Se llama al cerrarla. */
14468
14617
  async clearTerminal(terminalId) {
14469
- await rm3(path35.join(this.root, safeSegment(terminalId)), {
14618
+ await rm3(path37.join(this.root, safeSegment(terminalId)), {
14470
14619
  recursive: true,
14471
14620
  force: true
14472
14621
  }).catch(() => {
@@ -14488,9 +14637,9 @@ var PasteStore = class {
14488
14637
  }
14489
14638
  await Promise.all(
14490
14639
  entries.map(async (entry) => {
14491
- const full = path35.join(this.root, entry);
14640
+ const full = path37.join(this.root, entry);
14492
14641
  try {
14493
- const info = await stat18(full);
14642
+ const info = await stat19(full);
14494
14643
  if (now - info.mtimeMs > STALE_AFTER_MS) {
14495
14644
  await rm3(full, { recursive: true, force: true });
14496
14645
  }
@@ -14507,8 +14656,8 @@ function safeSegment(value) {
14507
14656
 
14508
14657
  // packages/server/src/session-index.ts
14509
14658
  import { EventEmitter as EventEmitter4 } from "node:events";
14510
- import { mkdir as mkdir9, readFile as readFile12, stat as stat19, writeFile as writeFile5 } from "node:fs/promises";
14511
- import path36 from "node:path";
14659
+ import { mkdir as mkdir9, readFile as readFile13, stat as stat20, writeFile as writeFile5 } from "node:fs/promises";
14660
+ import path38 from "node:path";
14512
14661
  var CACHE_VERSION = 4;
14513
14662
  var vaultPairKey = (agent, sessionId) => `${agent}/${sessionId}`;
14514
14663
  function vaultOnlySessions(natives, vault, listState, ready) {
@@ -14560,7 +14709,7 @@ function cacheKey(agent, ref) {
14560
14709
  }
14561
14710
  async function pathExists3(candidate) {
14562
14711
  try {
14563
- await stat19(candidate);
14712
+ await stat20(candidate);
14564
14713
  return true;
14565
14714
  } catch {
14566
14715
  return false;
@@ -14610,7 +14759,7 @@ function migrateEntryV3(filePath, value) {
14610
14759
  return {
14611
14760
  agent: "claude-code",
14612
14761
  ref: filePath,
14613
- group: path36.basename(path36.dirname(filePath)),
14762
+ group: path38.basename(path38.dirname(filePath)),
14614
14763
  mtimeMs,
14615
14764
  sizeBytes,
14616
14765
  cwd,
@@ -14875,7 +15024,7 @@ var SessionIndex = class extends EventEmitter4 {
14875
15024
  }
14876
15025
  async loadCache() {
14877
15026
  try {
14878
- const raw = await readFile12(sessionIndexCachePath(), "utf8");
15027
+ const raw = await readFile13(sessionIndexCachePath(), "utf8");
14879
15028
  const parsed = JSON.parse(raw);
14880
15029
  const migrated = migrateIndexCache(parsed);
14881
15030
  if (migrated === null) return;
@@ -15103,8 +15252,8 @@ var SessionIndex = class extends EventEmitter4 {
15103
15252
  };
15104
15253
 
15105
15254
  // packages/server/src/settings-store.ts
15106
- import { mkdir as mkdir10, readFile as readFile13, stat as stat20 } from "node:fs/promises";
15107
- import path37 from "node:path";
15255
+ import { mkdir as mkdir10, readFile as readFile14, stat as stat21 } from "node:fs/promises";
15256
+ import path39 from "node:path";
15108
15257
  var SETTINGS_VERSION = 1;
15109
15258
  var VAULT_TOOL_RESULT_DEFAULT_CHARS = 64e3;
15110
15259
  var VAULT_TOOL_RESULT_MIN_CHARS = 4e3;
@@ -15121,7 +15270,7 @@ function defaultAppSettings() {
15121
15270
  function isAbsoluteDir(dir, platform) {
15122
15271
  if (dir.length === 0 || dir.includes("\0")) return false;
15123
15272
  if (platform === "win32") return /^[A-Za-z]:[\\/]/.test(dir) || /^\\\\[^\\/]+[\\/][^\\/]+/.test(dir);
15124
- return path37.posix.isAbsolute(dir);
15273
+ return path39.posix.isAbsolute(dir);
15125
15274
  }
15126
15275
  function clampToolResultMaxChars(value) {
15127
15276
  if (typeof value !== "number" || !Number.isFinite(value)) return VAULT_TOOL_RESULT_DEFAULT_CHARS;
@@ -15180,7 +15329,7 @@ var SettingsStore = class {
15180
15329
  async load() {
15181
15330
  let raw;
15182
15331
  try {
15183
- raw = await readFile13(this.filePath, "utf8");
15332
+ raw = await readFile14(this.filePath, "utf8");
15184
15333
  } catch {
15185
15334
  this.current = frozen(defaultAppSettings());
15186
15335
  return;
@@ -15208,7 +15357,7 @@ var SettingsStore = class {
15208
15357
  async reloadRemote() {
15209
15358
  let parsed;
15210
15359
  try {
15211
- parsed = JSON.parse(await readFile13(this.filePath, "utf8"));
15360
+ parsed = JSON.parse(await readFile14(this.filePath, "utf8"));
15212
15361
  } catch {
15213
15362
  return;
15214
15363
  }
@@ -15219,7 +15368,7 @@ var SettingsStore = class {
15219
15368
  /** Una marca que cambia cuando el archivo cambia (fecha y tamano), o '' si no esta. */
15220
15369
  async stamp() {
15221
15370
  try {
15222
- const info = await stat20(this.filePath);
15371
+ const info = await stat21(this.filePath);
15223
15372
  return `${info.mtimeMs}:${info.size}`;
15224
15373
  } catch {
15225
15374
  return "";
@@ -15263,7 +15412,7 @@ var SettingsStore = class {
15263
15412
  if (!isRemotePort(remote.port)) throw new Error(`The remote access port isn't valid: ${JSON.stringify(remote.port)}`);
15264
15413
  next.remote.port = remote.port;
15265
15414
  }
15266
- await mkdir10(path37.dirname(this.filePath), { recursive: true });
15415
+ await mkdir10(path39.dirname(this.filePath), { recursive: true });
15267
15416
  await writeFileAtomic(this.filePath, `${JSON.stringify(next, null, 2)}
15268
15417
  `);
15269
15418
  this.current = frozen(next);
@@ -15351,8 +15500,8 @@ function supportStartupLineColored() {
15351
15500
 
15352
15501
  // packages/server/src/notes-store.ts
15353
15502
  import { randomUUID as randomUUID4 } from "node:crypto";
15354
- import { mkdir as mkdir11, readFile as readFile14, rename as rename5, rm as rm4, writeFile as writeFile6 } from "node:fs/promises";
15355
- import path38 from "node:path";
15503
+ import { mkdir as mkdir11, readFile as readFile15, rename as rename5, rm as rm4, writeFile as writeFile6 } from "node:fs/promises";
15504
+ import path40 from "node:path";
15356
15505
  var STATE_VERSION2 = 1;
15357
15506
  var WRITE_DEBOUNCE_MS2 = 500;
15358
15507
  var NotesError = class extends ServerTextError {
@@ -15395,7 +15544,7 @@ var NotesStore = class {
15395
15544
  }
15396
15545
  async load() {
15397
15546
  try {
15398
- const raw = await readFile14(this.statePath, "utf8");
15547
+ const raw = await readFile15(this.statePath, "utf8");
15399
15548
  this.notes = parseState2(raw) ?? [];
15400
15549
  } catch {
15401
15550
  this.notes = [];
@@ -15512,7 +15661,7 @@ var NotesStore = class {
15512
15661
  const image = this.findImage(imageId);
15513
15662
  if (image === null) return null;
15514
15663
  try {
15515
- const bytes = await readFile14(this.imagePath(image));
15664
+ const bytes = await readFile15(this.imagePath(image));
15516
15665
  return { mediaType: image.mediaType, data: bytes.toString("base64") };
15517
15666
  } catch {
15518
15667
  return null;
@@ -15532,7 +15681,7 @@ var NotesStore = class {
15532
15681
  const images = [];
15533
15682
  for (const image of note.images) {
15534
15683
  try {
15535
- const bytes = await readFile14(this.imagePath(image));
15684
+ const bytes = await readFile15(this.imagePath(image));
15536
15685
  images.push({ mediaType: image.mediaType, data: bytes.toString("base64") });
15537
15686
  } catch {
15538
15687
  }
@@ -15556,7 +15705,7 @@ var NotesStore = class {
15556
15705
  }
15557
15706
  imagePath(image) {
15558
15707
  const safe = image.imageId.replace(/[^a-zA-Z0-9-]/g, "");
15559
- return path38.join(this.imagesDir, `${safe}.${extensionFor(image.mediaType)}`);
15708
+ return path40.join(this.imagesDir, `${safe}.${extensionFor(image.mediaType)}`);
15560
15709
  }
15561
15710
  async unlinkImage(image) {
15562
15711
  await rm4(this.imagePath(image), { force: true }).catch(() => {
@@ -15574,8 +15723,8 @@ var NotesStore = class {
15574
15723
  async writeNow() {
15575
15724
  this.dirty = false;
15576
15725
  try {
15577
- await mkdir11(path38.dirname(this.statePath), { recursive: true });
15578
- const temporary = path38.join(path38.dirname(this.statePath), `notes.${process.pid}.tmp`);
15726
+ await mkdir11(path40.dirname(this.statePath), { recursive: true });
15727
+ const temporary = path40.join(path40.dirname(this.statePath), `notes.${process.pid}.tmp`);
15579
15728
  await writeFile6(
15580
15729
  temporary,
15581
15730
  JSON.stringify({ version: STATE_VERSION2, notes: this.notes }, null, 2),
@@ -15589,8 +15738,8 @@ var NotesStore = class {
15589
15738
  };
15590
15739
 
15591
15740
  // packages/server/src/draft-store.ts
15592
- import { mkdir as mkdir12, readFile as readFile15, rename as rename6, writeFile as writeFile7 } from "node:fs/promises";
15593
- import path39 from "node:path";
15741
+ import { mkdir as mkdir12, readFile as readFile16, rename as rename6, writeFile as writeFile7 } from "node:fs/promises";
15742
+ import path41 from "node:path";
15594
15743
  var STATE_VERSION3 = 1;
15595
15744
  var WRITE_DEBOUNCE_MS3 = 500;
15596
15745
  var MAX_STORED_DRAFTS = 100;
@@ -15684,7 +15833,7 @@ var DraftStore = class {
15684
15833
  }
15685
15834
  async load() {
15686
15835
  try {
15687
- this.drafts = parseState3(await readFile15(this.statePath, "utf8"));
15836
+ this.drafts = parseState3(await readFile16(this.statePath, "utf8"));
15688
15837
  } catch {
15689
15838
  this.drafts = /* @__PURE__ */ new Map();
15690
15839
  }
@@ -15775,7 +15924,7 @@ var DraftStore = class {
15775
15924
  async writeNow() {
15776
15925
  let onDisk = /* @__PURE__ */ new Map();
15777
15926
  try {
15778
- onDisk = parseState3(await readFile15(this.statePath, "utf8"));
15927
+ onDisk = parseState3(await readFile16(this.statePath, "utf8"));
15779
15928
  } catch {
15780
15929
  }
15781
15930
  const changed = this.changed;
@@ -15789,8 +15938,8 @@ var DraftStore = class {
15789
15938
  trimOldest(merged);
15790
15939
  this.drafts = new Map(merged);
15791
15940
  try {
15792
- await mkdir12(path39.dirname(this.statePath), { recursive: true });
15793
- const temporary = path39.join(path39.dirname(this.statePath), `composer-drafts.${process.pid}.tmp`);
15941
+ await mkdir12(path41.dirname(this.statePath), { recursive: true });
15942
+ const temporary = path41.join(path41.dirname(this.statePath), `composer-drafts.${process.pid}.tmp`);
15794
15943
  const payload = {
15795
15944
  version: STATE_VERSION3,
15796
15945
  drafts: [...merged.values()].map(({ agent, sessionId, updatedAt, draft }) => ({ agent, sessionId, updatedAt, draft }))
@@ -15807,7 +15956,7 @@ var DraftStore = class {
15807
15956
  // packages/server/src/shell-locator.ts
15808
15957
  import { access as access2 } from "node:fs/promises";
15809
15958
  import { constants as fsConstants2 } from "node:fs";
15810
- import path40 from "node:path";
15959
+ import path42 from "node:path";
15811
15960
  async function isExecutable(candidate) {
15812
15961
  try {
15813
15962
  await access2(candidate, process.platform === "win32" ? fsConstants2.R_OK : fsConstants2.X_OK);
@@ -15825,21 +15974,21 @@ var SYSTEM_PROBE = {
15825
15974
  function windowsPowerShellPath(env) {
15826
15975
  const systemRoot = env["SystemRoot"] ?? env["windir"];
15827
15976
  if (systemRoot === void 0 || systemRoot.length === 0) return null;
15828
- return path40.win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
15977
+ return path42.win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
15829
15978
  }
15830
15979
  function gitBashCandidates(gitPath, env) {
15831
15980
  const candidates = [];
15832
15981
  if (gitPath !== null) {
15833
- let dir = path40.win32.dirname(gitPath);
15982
+ let dir = path42.win32.dirname(gitPath);
15834
15983
  for (let level = 0; level < 3; level++) {
15835
- candidates.push(path40.win32.join(dir, "bin", "bash.exe"));
15836
- const parent = path40.win32.dirname(dir);
15984
+ candidates.push(path42.win32.join(dir, "bin", "bash.exe"));
15985
+ const parent = path42.win32.dirname(dir);
15837
15986
  if (parent === dir) break;
15838
15987
  dir = parent;
15839
15988
  }
15840
15989
  }
15841
- for (const base of [env["ProgramFiles"], env["LOCALAPPDATA"] === void 0 ? void 0 : path40.win32.join(env["LOCALAPPDATA"], "Programs")]) {
15842
- if (base !== void 0 && base.length > 0) candidates.push(path40.win32.join(base, "Git", "bin", "bash.exe"));
15990
+ for (const base of [env["ProgramFiles"], env["LOCALAPPDATA"] === void 0 ? void 0 : path42.win32.join(env["LOCALAPPDATA"], "Programs")]) {
15991
+ if (base !== void 0 && base.length > 0) candidates.push(path42.win32.join(base, "Git", "bin", "bash.exe"));
15843
15992
  }
15844
15993
  const systemRoot = (env["SystemRoot"] ?? env["windir"] ?? "").toLowerCase();
15845
15994
  return [...new Set(candidates)].filter((candidate) => systemRoot.length === 0 || !candidate.toLowerCase().startsWith(systemRoot));
@@ -15870,7 +16019,7 @@ async function locatePosixShells(probe) {
15870
16019
  const found = [];
15871
16020
  const preferred = probe.env["SHELL"];
15872
16021
  if (preferred !== void 0 && preferred.length > 0 && await probe.isExecutable(preferred)) {
15873
- const name = path40.posix.basename(preferred);
16022
+ const name = path42.posix.basename(preferred);
15874
16023
  found.push({ id: "login", label: name, location: { file: preferred, args: [], name } });
15875
16024
  }
15876
16025
  for (const [id, file] of [["bash", "/bin/bash"], ["sh", "/bin/sh"], ["zsh", "/bin/zsh"]]) {
@@ -15942,13 +16091,13 @@ var AppSettingsService = class extends EventEmitter5 {
15942
16091
  };
15943
16092
 
15944
16093
  // packages/server/src/session-watcher.ts
15945
- import { stat as stat21 } from "node:fs/promises";
16094
+ import { stat as stat22 } from "node:fs/promises";
15946
16095
  import chokidar3 from "chokidar";
15947
16096
  var INDEX_DEBOUNCE_MS = 750;
15948
16097
  var ROOT_PROBE_MS = 5e3;
15949
16098
  async function isDirectory3(target) {
15950
16099
  try {
15951
- return (await stat21(target)).isDirectory();
16100
+ return (await stat22(target)).isDirectory();
15952
16101
  } catch {
15953
16102
  return false;
15954
16103
  }
@@ -16051,7 +16200,7 @@ function watchSessions(index, hub, agents, options = {}) {
16051
16200
  // packages/server/src/terminal-registry.ts
16052
16201
  import { randomUUID as randomUUID5 } from "node:crypto";
16053
16202
  import { EventEmitter as EventEmitter6 } from "node:events";
16054
- import { stat as stat22 } from "node:fs/promises";
16203
+ import { stat as stat23 } from "node:fs/promises";
16055
16204
 
16056
16205
  // packages/server/src/launch-hook-slot.ts
16057
16206
  var LaunchHookSlot = class {
@@ -16313,8 +16462,8 @@ async function resolveLaunchPlan(launch) {
16313
16462
  }
16314
16463
 
16315
16464
  // packages/server/src/workspace-store.ts
16316
- import { mkdir as mkdir13, readFile as readFile16, writeFile as writeFile8 } from "node:fs/promises";
16317
- import path41 from "node:path";
16465
+ import { mkdir as mkdir13, readFile as readFile17, writeFile as writeFile8 } from "node:fs/promises";
16466
+ import path43 from "node:path";
16318
16467
  var STATE_VERSION4 = 1;
16319
16468
  var WRITE_DEBOUNCE_MS4 = 400;
16320
16469
  var LEGACY_TAB_AGENT = "claude-code";
@@ -16436,7 +16585,7 @@ var WorkspaceStore = class {
16436
16585
  pending = null;
16437
16586
  async load() {
16438
16587
  try {
16439
- const raw = await readFile16(workspaceStatePath(), "utf8");
16588
+ const raw = await readFile17(workspaceStatePath(), "utf8");
16440
16589
  return parseState4(raw) ?? EMPTY_STATE();
16441
16590
  } catch {
16442
16591
  return EMPTY_STATE();
@@ -16468,7 +16617,7 @@ var WorkspaceStore = class {
16468
16617
  try {
16469
16618
  await mkdir13(appConfigDir(), { recursive: true });
16470
16619
  const target = workspaceStatePath();
16471
- const temporary = path41.join(path41.dirname(target), `workspace.${process.pid}.tmp`);
16620
+ const temporary = path43.join(path43.dirname(target), `workspace.${process.pid}.tmp`);
16472
16621
  await writeFile8(temporary, JSON.stringify(serializeState(state), null, 2), "utf8");
16473
16622
  const { rename: rename7 } = await import("node:fs/promises");
16474
16623
  await rename7(temporary, target);
@@ -16705,7 +16854,7 @@ var TerminalRegistry = class extends EventEmitter6 {
16705
16854
  async assertDirectory(cwd) {
16706
16855
  let info;
16707
16856
  try {
16708
- info = await stat22(cwd);
16857
+ info = await stat23(cwd);
16709
16858
  } catch {
16710
16859
  throw new TerminalOpenError("invalid-cwd", serverText("cwdMissing", { cwd }));
16711
16860
  }
@@ -17132,19 +17281,19 @@ import { WebSocketServer } from "ws";
17132
17281
 
17133
17282
  // packages/server/src/directory-picker.ts
17134
17283
  import { randomUUID as randomUUID6 } from "node:crypto";
17135
- import { mkdir as mkdir14, readdir as readdir11, stat as stat23 } from "node:fs/promises";
17284
+ import { mkdir as mkdir14, readdir as readdir11, stat as stat24 } from "node:fs/promises";
17136
17285
  import { homedir as homedir11 } from "node:os";
17137
- import path42 from "node:path";
17286
+ import path44 from "node:path";
17138
17287
  var MAX_ENTRIES = 500;
17139
17288
  var MAX_PICKERS = 8;
17140
17289
  var SKIPPED = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "bin", "obj", "$RECYCLE.BIN"]);
17141
17290
  var DirectoryPickerError = class extends ServerTextError {
17142
17291
  };
17143
17292
  function isInsideProtected(target, dirs) {
17144
- const resolved = path42.resolve(target);
17293
+ const resolved = path44.resolve(target);
17145
17294
  return dirs.some((dir) => {
17146
- const protectedDir = path42.resolve(dir);
17147
- return resolved === protectedDir || resolved.startsWith(protectedDir + path42.sep);
17295
+ const protectedDir = path44.resolve(dir);
17296
+ return resolved === protectedDir || resolved.startsWith(protectedDir + path44.sep);
17148
17297
  });
17149
17298
  }
17150
17299
  async function listRoots() {
@@ -17156,7 +17305,7 @@ async function listRoots() {
17156
17305
  letters.map(async (letter) => {
17157
17306
  const drive = `${letter}:\\`;
17158
17307
  try {
17159
- await stat23(drive);
17308
+ await stat24(drive);
17160
17309
  return drive;
17161
17310
  } catch {
17162
17311
  return null;
@@ -17208,14 +17357,14 @@ var DirectoryPickers = class {
17208
17357
  async enter(pickerId, name) {
17209
17358
  const picker = this.require(pickerId);
17210
17359
  if (name === "..") {
17211
- const parent = path42.dirname(picker.current);
17360
+ const parent = path44.dirname(picker.current);
17212
17361
  if (parent !== picker.current) picker.current = parent;
17213
17362
  return this.listing(picker);
17214
17363
  }
17215
17364
  if (!picker.entries.has(name)) {
17216
17365
  throw new DirectoryPickerError(serverText("pickerNotListed"));
17217
17366
  }
17218
- const target = path42.join(picker.current, name);
17367
+ const target = path44.join(picker.current, name);
17219
17368
  if (isInsideProtected(target, this.protectedDirs)) {
17220
17369
  throw new DirectoryPickerError(serverText("pickerCliFolder"));
17221
17370
  }
@@ -17255,7 +17404,7 @@ var DirectoryPickers = class {
17255
17404
  throw new DirectoryPickerError(serverText("pickerNameChars"));
17256
17405
  }
17257
17406
  }
17258
- const target = path42.join(picker.current, clean);
17407
+ const target = path44.join(picker.current, clean);
17259
17408
  if (isInsideProtected(target, this.protectedDirs)) {
17260
17409
  throw new DirectoryPickerError(serverText("pickerNoProjectsHere"));
17261
17410
  }
@@ -17292,7 +17441,7 @@ var DirectoryPickers = class {
17292
17441
  let truncated = false;
17293
17442
  try {
17294
17443
  const found = await readdir11(picker.current, { withFileTypes: true });
17295
- const dirs = found.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => !SKIPPED.has(name)).filter((name) => !isInsideProtected(path42.join(picker.current, name), this.protectedDirs)).sort((a, b) => a.localeCompare(b));
17444
+ const dirs = found.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => !SKIPPED.has(name)).filter((name) => !isInsideProtected(path44.join(picker.current, name), this.protectedDirs)).sort((a, b) => a.localeCompare(b));
17296
17445
  truncated = dirs.length > MAX_ENTRIES;
17297
17446
  names = dirs.slice(0, MAX_ENTRIES);
17298
17447
  } catch {
@@ -17303,15 +17452,15 @@ var DirectoryPickers = class {
17303
17452
  path: picker.current,
17304
17453
  entries: names,
17305
17454
  roots,
17306
- canGoUp: path42.dirname(picker.current) !== picker.current,
17455
+ canGoUp: path44.dirname(picker.current) !== picker.current,
17307
17456
  truncated
17308
17457
  };
17309
17458
  }
17310
17459
  };
17311
17460
 
17312
17461
  // packages/server/src/file-browser.ts
17313
- import { readFile as readFile17, readdir as readdir12, stat as stat24 } from "node:fs/promises";
17314
- import path43 from "node:path";
17462
+ import { readFile as readFile18, readdir as readdir12, stat as stat25 } from "node:fs/promises";
17463
+ import path45 from "node:path";
17315
17464
  var MAX_ENTRIES2 = 1e3;
17316
17465
  var SEARCH_MAX_RESULTS = 200;
17317
17466
  var SEARCH_MAX_DIRECTORIES = 4e3;
@@ -17374,7 +17523,7 @@ function languageFor(fileName) {
17374
17523
  const lower = fileName.toLowerCase();
17375
17524
  const byName = LANGUAGE_BY_NAME[lower];
17376
17525
  if (byName !== void 0) return byName;
17377
- const extension = path43.extname(lower);
17526
+ const extension = path45.extname(lower);
17378
17527
  return LANGUAGE_BY_EXTENSION[extension] ?? null;
17379
17528
  }
17380
17529
  function looksBinary2(buffer) {
@@ -17406,7 +17555,7 @@ async function listDirectory(cwd, relativePath, options = {}) {
17406
17555
  let sizeBytes = 0;
17407
17556
  let modifiedAt = 0;
17408
17557
  try {
17409
- const info = await stat24(path43.join(absolute, candidate.name));
17558
+ const info = await stat25(path45.join(absolute, candidate.name));
17410
17559
  sizeBytes = candidate.isDirectory ? 0 : info.size;
17411
17560
  modifiedAt = info.mtimeMs;
17412
17561
  } catch {
@@ -17439,7 +17588,7 @@ async function readLevel(absolute) {
17439
17588
  const always = ALWAYS_SKIP.has(dirent.name.toLowerCase());
17440
17589
  if (dirent.isSymbolicLink()) {
17441
17590
  try {
17442
- const target = await stat24(path43.join(absolute, dirent.name));
17591
+ const target = await stat25(path45.join(absolute, dirent.name));
17443
17592
  candidates.push({
17444
17593
  name: dirent.name,
17445
17594
  isDirectory: target.isDirectory(),
@@ -17468,7 +17617,7 @@ async function markIgnored(repoRoot2, levels) {
17468
17617
  for (const level of levels) {
17469
17618
  for (const entry of level.entries) {
17470
17619
  if (entry.hidden !== null) continue;
17471
- keys.set(entry, toPosixPath(path43.relative(repoRoot2, path43.join(level.absolute, entry.name))));
17620
+ keys.set(entry, toPosixPath(path45.relative(repoRoot2, path45.join(level.absolute, entry.name))));
17472
17621
  }
17473
17622
  }
17474
17623
  if (keys.size === 0) return;
@@ -17529,7 +17678,7 @@ async function searchFiles(cwd, query, options = {}) {
17529
17678
  let sizeBytes = 0;
17530
17679
  let modifiedAt = 0;
17531
17680
  try {
17532
- const info = await stat24(path43.join(level.absolute, candidate.name));
17681
+ const info = await stat25(path45.join(level.absolute, candidate.name));
17533
17682
  sizeBytes = candidate.isDirectory ? 0 : info.size;
17534
17683
  modifiedAt = info.mtimeMs;
17535
17684
  } catch {
@@ -17563,12 +17712,12 @@ async function searchFiles(cwd, query, options = {}) {
17563
17712
  }
17564
17713
  async function readPreview(cwd, relativePath) {
17565
17714
  const absolute = await resolveInside(cwd, relativePath, { mustExist: true });
17566
- const info = await stat24(absolute);
17715
+ const info = await stat25(absolute);
17567
17716
  if (info.isDirectory()) {
17568
17717
  throw new Error("It's a directory, not a file.");
17569
17718
  }
17570
- const content = await readFile17(absolute);
17571
- const fileName = path43.basename(absolute);
17719
+ const content = await readFile18(absolute);
17720
+ const fileName = path45.basename(absolute);
17572
17721
  if (looksBinary2(content)) {
17573
17722
  return {
17574
17723
  path: relativePath,
@@ -18157,14 +18306,14 @@ var TerminalWriteQueue = class {
18157
18306
  };
18158
18307
 
18159
18308
  // packages/server/src/vault/service.ts
18160
- import { mkdir as mkdir17, stat as stat28 } from "node:fs/promises";
18309
+ import { mkdir as mkdir17, stat as stat29 } from "node:fs/promises";
18161
18310
  import { homedir as homedir12 } from "node:os";
18162
- import path47 from "node:path";
18311
+ import path49 from "node:path";
18163
18312
 
18164
18313
  // packages/server/src/vault/move.ts
18165
18314
  import { constants } from "node:fs";
18166
- import { copyFile as copyFile2, lstat, mkdir as mkdir15, readdir as readdir13, rm as rm5, stat as stat25, utimes, writeFile as writeFile9 } from "node:fs/promises";
18167
- import path44 from "node:path";
18315
+ import { copyFile as copyFile2, lstat, mkdir as mkdir15, readdir as readdir13, rm as rm5, stat as stat26, utimes, writeFile as writeFile9 } from "node:fs/promises";
18316
+ import path46 from "node:path";
18168
18317
  var MOVED_ENTRIES = ["vault.json", "sessions", "memory"];
18169
18318
  var OWN_TEMP_FILE = /\.\d+\.[0-9a-f]{12}\.tmp$/;
18170
18319
  async function copyTree(source, target, result) {
@@ -18177,12 +18326,12 @@ async function copyTree(source, target, result) {
18177
18326
  if (info.isDirectory()) {
18178
18327
  await mkdir15(target, { recursive: true });
18179
18328
  for (const name of await readdir13(source)) {
18180
- await copyTree(path44.join(source, name), path44.join(target, name), result);
18329
+ await copyTree(path46.join(source, name), path46.join(target, name), result);
18181
18330
  }
18182
18331
  return;
18183
18332
  }
18184
18333
  if (!info.isFile() || OWN_TEMP_FILE.test(source)) return;
18185
- await mkdir15(path44.dirname(target), { recursive: true });
18334
+ await mkdir15(path46.dirname(target), { recursive: true });
18186
18335
  try {
18187
18336
  await copyFile2(source, target, constants.COPYFILE_EXCL);
18188
18337
  } catch (error) {
@@ -18199,7 +18348,7 @@ async function moveVault(from, to) {
18199
18348
  const result = { copied: 0, skipped: 0 };
18200
18349
  await mkdir15(to, { recursive: true });
18201
18350
  for (const entry of MOVED_ENTRIES) {
18202
- await copyTree(path44.join(from, entry), path44.join(to, entry), result);
18351
+ await copyTree(path46.join(from, entry), path46.join(to, entry), result);
18203
18352
  }
18204
18353
  return result;
18205
18354
  }
@@ -18231,10 +18380,10 @@ function checkVaultTarget(current, target, options) {
18231
18380
  return { kind: "ok" };
18232
18381
  }
18233
18382
  async function canWriteInto(dir) {
18234
- const probe = temporaryPathFor(path44.join(dir, ".escritura"));
18383
+ const probe = temporaryPathFor(path46.join(dir, ".escritura"));
18235
18384
  try {
18236
18385
  await mkdir15(dir, { recursive: true });
18237
- if (!(await stat25(dir)).isDirectory()) return false;
18386
+ if (!(await stat26(dir)).isDirectory()) return false;
18238
18387
  await writeFile9(probe, "");
18239
18388
  return true;
18240
18389
  } catch {
@@ -18337,12 +18486,12 @@ function serializeSession(input) {
18337
18486
  }
18338
18487
 
18339
18488
  // packages/server/src/vault/writer.ts
18340
- import { readdir as readdir15, rm as rm6, stat as stat27 } from "node:fs/promises";
18341
- import path46 from "node:path";
18489
+ import { readdir as readdir15, rm as rm6, stat as stat28 } from "node:fs/promises";
18490
+ import path48 from "node:path";
18342
18491
 
18343
18492
  // packages/server/src/vault/memory-copy.ts
18344
- import { mkdir as mkdir16, readdir as readdir14, readFile as readFile18, stat as stat26, utimes as utimes2 } from "node:fs/promises";
18345
- import path45 from "node:path";
18493
+ import { mkdir as mkdir16, readdir as readdir14, readFile as readFile19, stat as stat27, utimes as utimes2 } from "node:fs/promises";
18494
+ import path47 from "node:path";
18346
18495
  var MEMORY_FILE_MAX_BYTES = 1024 * 1024;
18347
18496
  var MEMORY_MAX_FILES = 500;
18348
18497
  var MEMORY_SOURCE_FILE = "source.json";
@@ -18367,7 +18516,7 @@ async function listMemoryNotes(cwd) {
18367
18516
  let absolute;
18368
18517
  try {
18369
18518
  absolute = await resolveInside(cwd, `${MEMORY_DIR}/${name}`, { mustExist: true });
18370
- const info = await stat26(absolute);
18519
+ const info = await stat27(absolute);
18371
18520
  if (!info.isFile() || info.size > MEMORY_FILE_MAX_BYTES) continue;
18372
18521
  notes.push({ name, absolute, size: info.size, mtimeMs: info.mtimeMs });
18373
18522
  } catch {
@@ -18381,7 +18530,7 @@ async function measureProjectMemory(cwd) {
18381
18530
  }
18382
18531
  async function isUpToDate(target, note) {
18383
18532
  try {
18384
- const info = await stat26(target);
18533
+ const info = await stat27(target);
18385
18534
  return info.isFile() && info.size === note.size && Math.abs(info.mtimeMs - note.mtimeMs) < SAME_MTIME_MS;
18386
18535
  } catch {
18387
18536
  return false;
@@ -18389,7 +18538,7 @@ async function isUpToDate(target, note) {
18389
18538
  }
18390
18539
  async function exists3(file) {
18391
18540
  try {
18392
- await stat26(file);
18541
+ await stat27(file);
18393
18542
  return true;
18394
18543
  } catch {
18395
18544
  return false;
@@ -18408,11 +18557,11 @@ async function copyProjectMemory(dir, cwd, platform, deps = DEFAULT_WRITE_DEPS)
18408
18557
  await mkdir16(target, { recursive: true });
18409
18558
  };
18410
18559
  for (const note of notes) {
18411
- const destination = path45.join(target, note.name);
18560
+ const destination = path47.join(target, note.name);
18412
18561
  if (await isUpToDate(destination, note)) continue;
18413
18562
  let content;
18414
18563
  try {
18415
- content = await readFile18(note.absolute);
18564
+ content = await readFile19(note.absolute);
18416
18565
  } catch {
18417
18566
  continue;
18418
18567
  }
@@ -18424,7 +18573,7 @@ async function copyProjectMemory(dir, cwd, platform, deps = DEFAULT_WRITE_DEPS)
18424
18573
  result.files += 1;
18425
18574
  result.bytes += content.length;
18426
18575
  }
18427
- const source = path45.join(target, MEMORY_SOURCE_FILE);
18576
+ const source = path47.join(target, MEMORY_SOURCE_FILE);
18428
18577
  if (result.files > 0 || !await exists3(source)) {
18429
18578
  await prepare();
18430
18579
  await writeFileAtomic(source, `${JSON.stringify({ cwd, copiedAt: deps.now() })}
@@ -18434,7 +18583,7 @@ async function copyProjectMemory(dir, cwd, platform, deps = DEFAULT_WRITE_DEPS)
18434
18583
  }
18435
18584
 
18436
18585
  // packages/server/src/vault/writer.ts
18437
- var VAULT_WRITER_REVISION = 1;
18586
+ var VAULT_WRITER_REVISION = 2;
18438
18587
  var CALM_MS = 6e4;
18439
18588
  var PASS_DEBOUNCE_MS = 5e3;
18440
18589
  var TEMP_MAX_AGE_MS = 60 * 60 * 1e3;
@@ -18918,7 +19067,7 @@ var VaultWriter = class {
18918
19067
  }
18919
19068
  async assetOnDisk(dir, agent, sessionId, asset, bytes) {
18920
19069
  try {
18921
- const info = await stat27(assetFile(dir, agent, sessionId, asset));
19070
+ const info = await stat28(assetFile(dir, agent, sessionId, asset));
18922
19071
  return info.isFile() && info.size === bytes;
18923
19072
  } catch {
18924
19073
  return false;
@@ -19018,17 +19167,17 @@ var VaultWriter = class {
19018
19167
  const folders = [dir];
19019
19168
  const subfolders = async (parent) => {
19020
19169
  try {
19021
- return (await readdir15(parent, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => path46.join(parent, entry.name));
19170
+ return (await readdir15(parent, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => path48.join(parent, entry.name));
19022
19171
  } catch {
19023
19172
  return [];
19024
19173
  }
19025
19174
  };
19026
19175
  for (const agentDir of await subfolders(sessionsRoot2(dir))) {
19027
- if (!SESSION_AGENT_IDS.includes(path46.basename(agentDir))) continue;
19176
+ if (!SESSION_AGENT_IDS.includes(path48.basename(agentDir))) continue;
19028
19177
  folders.push(agentDir);
19029
19178
  folders.push(...(await subfolders(agentDir)).filter((folder) => folder.endsWith(".assets")));
19030
19179
  }
19031
- folders.push(...await subfolders(path46.join(dir, "memory")));
19180
+ folders.push(...await subfolders(path48.join(dir, "memory")));
19032
19181
  for (const folder of folders) {
19033
19182
  let names;
19034
19183
  try {
@@ -19038,9 +19187,9 @@ var VaultWriter = class {
19038
19187
  }
19039
19188
  for (const name of names) {
19040
19189
  if (!OWN_TEMP_FILE2.test(name)) continue;
19041
- const file = path46.join(folder, name);
19190
+ const file = path48.join(folder, name);
19042
19191
  try {
19043
- const info = await stat27(file);
19192
+ const info = await stat28(file);
19044
19193
  if (info.isFile() && now - info.mtimeMs > TEMP_MAX_AGE_MS) await rm6(file, { force: true });
19045
19194
  } catch {
19046
19195
  }
@@ -19228,14 +19377,14 @@ var VaultService = class {
19228
19377
  async setDir(target) {
19229
19378
  const { platform, agents, settings: settings2, catalog } = this.options;
19230
19379
  const current = this.dir();
19231
- const protectedDirs = [...agents.protectedDirs(), path47.join(this.options.homeDir ?? homedir12(), ".gemini")];
19380
+ const protectedDirs = [...agents.protectedDirs(), path49.join(this.options.homeDir ?? homedir12(), ".gemini")];
19232
19381
  const check = checkVaultTarget(current, target, { protectedDirs, platform });
19233
19382
  if (check.kind === "refused") throw new VaultError(check.text);
19234
19383
  if (check.kind === "same") return;
19235
19384
  try {
19236
19385
  await this.writer.exclusive("moving", async () => {
19237
19386
  if (!await canWriteInto(target)) throw new VaultError(serverText("vaultCannotWrite"));
19238
- const hadCopy = await stat28(vaultMarkerFile(current)).then(
19387
+ const hadCopy = await stat29(vaultMarkerFile(current)).then(
19239
19388
  () => true,
19240
19389
  () => false
19241
19390
  );
@@ -19269,7 +19418,7 @@ var VaultService = class {
19269
19418
  /** Abre la carpeta de la copia con el explorador del sistema. */
19270
19419
  async reveal() {
19271
19420
  const dir = this.activeDir();
19272
- const isFolder = await stat28(dir).then(
19421
+ const isFolder = await stat29(dir).then(
19273
19422
  (info) => info.isDirectory(),
19274
19423
  () => false
19275
19424
  );
@@ -19293,7 +19442,7 @@ var VaultService = class {
19293
19442
  fromCopy: true
19294
19443
  });
19295
19444
  const file = sessionExportFile(dir, agent, sessionId);
19296
- await mkdir17(path47.dirname(file), { recursive: true });
19445
+ await mkdir17(path49.dirname(file), { recursive: true });
19297
19446
  await writeFileAtomic(file, text, this.writeDeps);
19298
19447
  this.openWithSystem(file);
19299
19448
  return file;
@@ -19397,7 +19546,7 @@ var VaultService = class {
19397
19546
  continue;
19398
19547
  }
19399
19548
  const fileName = this.exportFileName(summary, names);
19400
- await writeFileAtomic(path47.join(folder, fileName), text, this.writeDeps);
19549
+ await writeFileAtomic(path49.join(folder, fileName), text, this.writeDeps);
19401
19550
  entries.push({
19402
19551
  updatedAt: summary.updatedAt,
19403
19552
  agentLabel: this.labelOf(summary.agent),
@@ -19408,9 +19557,9 @@ var VaultService = class {
19408
19557
  });
19409
19558
  await nextTurn3();
19410
19559
  }
19411
- const name = project.cwd.length > 0 ? path47.basename(project.cwd) || project.cwd : project.fallbackName;
19560
+ const name = project.cwd.length > 0 ? path49.basename(project.cwd) || project.cwd : project.fallbackName;
19412
19561
  const index = renderProjectIndex({ name, cwd: project.cwd, exportedAt: this.now(), entries, skipped });
19413
- await writeFileAtomic(path47.join(folder, "index.md"), index, this.writeDeps);
19562
+ await writeFileAtomic(path49.join(folder, "index.md"), index, this.writeDeps);
19414
19563
  this.openWithSystem(folder);
19415
19564
  return { sessions: entries.length, skipped, folder };
19416
19565
  }
@@ -21080,8 +21229,8 @@ function attachTerminalSocket(options) {
21080
21229
  }
21081
21230
 
21082
21231
  // packages/server/src/vault/catalog.ts
21083
- import { open as open7, readdir as readdir16, stat as stat29 } from "node:fs/promises";
21084
- import path48 from "node:path";
21232
+ import { open as open7, readdir as readdir16, stat as stat30 } from "node:fs/promises";
21233
+ import path50 from "node:path";
21085
21234
  import { createInterface as createInterface2 } from "node:readline";
21086
21235
  var HEADER_MAX_BYTES = 64 * 1024;
21087
21236
  var LOAD_CONCURRENCY = 16;
@@ -21101,7 +21250,7 @@ async function assetBytesOf(dir, agent, sessionId) {
21101
21250
  for (const name of names) {
21102
21251
  if (!VAULT_ASSET_NAME_PATTERN.test(name)) continue;
21103
21252
  try {
21104
- const info = await stat29(path48.join(folder, name));
21253
+ const info = await stat30(path50.join(folder, name));
21105
21254
  if (info.isFile()) total += info.size;
21106
21255
  } catch {
21107
21256
  }
@@ -21112,7 +21261,7 @@ async function readEntry(dir, agent, sessionId) {
21112
21261
  const file = sessionFile(dir, agent, sessionId);
21113
21262
  let sizeBytes;
21114
21263
  try {
21115
- const info = await stat29(file);
21264
+ const info = await stat30(file);
21116
21265
  if (!info.isFile()) return { kind: "absent" };
21117
21266
  sizeBytes = info.size;
21118
21267
  } catch {
@@ -21321,15 +21470,15 @@ var VaultCatalog = class {
21321
21470
  };
21322
21471
 
21323
21472
  // packages/server/src/index.ts
21324
- var serverDir = path49.dirname(fileURLToPath2(import.meta.url));
21473
+ var serverDir = path51.dirname(fileURLToPath2(import.meta.url));
21325
21474
  var isPackaged = true;
21326
- var webRoot = isPackaged ? serverDir : path49.resolve(serverDir, "../../web");
21327
- var webDist = isPackaged ? path49.join(serverDir, "web") : path49.join(webRoot, "dist");
21328
- var repoRoot = path49.resolve(serverDir, "../../..");
21475
+ var webRoot = isPackaged ? serverDir : path51.resolve(serverDir, "../../web");
21476
+ var webDist = isPackaged ? path51.join(serverDir, "web") : path51.join(webRoot, "dist");
21477
+ var repoRoot = path51.resolve(serverDir, "../../..");
21329
21478
  var isProduction = isPackaged || process.argv.includes("--prod");
21330
21479
  function resolveDefaultCwd() {
21331
21480
  const override = process.env["AGENT_WORKBENCH_CWD"];
21332
- if (override !== void 0 && override.length > 0) return path49.resolve(override);
21481
+ if (override !== void 0 && override.length > 0) return path51.resolve(override);
21333
21482
  if (isPackaged) return process.cwd();
21334
21483
  return repoRoot;
21335
21484
  }
@@ -21404,7 +21553,7 @@ async function mountDevUi(app, httpServer) {
21404
21553
  }
21405
21554
  var LOOKS_LIKE_FILE = /\.[a-zA-Z0-9]{1,8}$/;
21406
21555
  function mountBuiltUi(app) {
21407
- const indexHtml = path49.join(webDist, "index.html");
21556
+ const indexHtml = path51.join(webDist, "index.html");
21408
21557
  if (!existsSync4(indexHtml)) {
21409
21558
  throw new Error(
21410
21559
  `There's no compiled interface in ${webDist}.