agent-workbench 0.2.0 → 0.3.0

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
@@ -69,6 +69,20 @@ function cycleDistance(from, to, cycle = PERMISSION_MODE_CYCLE) {
69
69
  }
70
70
  var LAUNCH_PERMISSION_MODE = "auto";
71
71
 
72
+ // packages/shared/src/server-text.ts
73
+ function serverText(key, ...params) {
74
+ const [values] = params;
75
+ return values === void 0 ? { key } : { key, params: values };
76
+ }
77
+ var ServerTextError = class extends Error {
78
+ text;
79
+ constructor(text, options) {
80
+ super(text.key, options);
81
+ this.text = text;
82
+ this.name = new.target.name;
83
+ }
84
+ };
85
+
72
86
  // packages/shared/src/agents.ts
73
87
  var AGENT_IDS = ["claude-code", "codex", "opencode", "antigravity"];
74
88
  var IMPORTED_AGENT_IDS = ["gemini-cli", "antigravity-ide"];
@@ -354,21 +368,22 @@ var MEMORY_AGENT_LABELS = {
354
368
  opencode: "OpenCode"
355
369
  };
356
370
  var MEMORY_BLOCK_BODY = [
357
- "## Memoria del proyecto",
371
+ "## Project memory",
358
372
  "",
359
- "La memoria de este proyecto vive en `.agents/memory/` y es compartida: la leen y",
360
- "la escriben todos los agentes que trabajan ac\xE1, con cualquier herramienta.",
373
+ "This project's memory lives in `.agents/memory/` and is shared: every agent",
374
+ "that works here reads and writes it, whatever tool it runs in.",
361
375
  "",
362
- "- Al empezar, le\xE9 `.agents/memory/MEMORY.md`. Es el \xEDndice: una l\xEDnea por nota.",
363
- "- Cuando aprendas algo que sirva en otra sesi\xF3n \u2014una decisi\xF3n y su motivo, una",
364
- " restricci\xF3n, una trampa ya pisada, una preferencia del usuario\u2014, guardalo en",
365
- " un archivo propio dentro de `.agents/memory/` y agreg\xE1 su l\xEDnea al \xEDndice:",
366
- " `- [T\xEDtulo](archivo.md) \u2014 de qu\xE9 trata, en una l\xEDnea`.",
367
- "- Cada nota empieza con `name:` y `description:` entre dos l\xEDneas `---`, y",
368
- " despu\xE9s el hecho. Si ya hay una nota sobre lo mismo, actualizala en vez de",
369
- " crear otra.",
370
- "- No guardes lo que ya cuenta el repositorio ni lo que s\xF3lo importa hoy.",
371
- "- Guard\xE1 la memoria ac\xE1, no en la carpeta de memoria propia de tu herramienta."
376
+ "- When you start, read `.agents/memory/MEMORY.md`. It is the index: one line",
377
+ " per note.",
378
+ "- When you learn something that would help in another session \u2014a decision and",
379
+ " its reason, a constraint, a pitfall already hit, a user preference\u2014, save it",
380
+ " in its own file inside `.agents/memory/` and add its line to the index:",
381
+ " `- [Title](file.md) \u2014 what it is about, in one line`.",
382
+ "- Each note starts with `name:` and `description:` between two `---` lines,",
383
+ " followed by the fact. If there is already a note about the same thing,",
384
+ " update it instead of creating another one.",
385
+ "- Don't save what the repository already records or what only matters today.",
386
+ "- Save memory here, not in your tool's own memory folder."
372
387
  ];
373
388
  function memoryBlock(file) {
374
389
  const lines = [MEMORY_BLOCK_START, ...MEMORY_BLOCK_BODY];
@@ -377,10 +392,10 @@ function memoryBlock(file) {
377
392
  return lines.join("\n");
378
393
  }
379
394
  var MEMORY_INDEX_TEMPLATE = [
380
- "# Memoria del proyecto",
395
+ "# Project memory",
381
396
  "",
382
- "\xCDndice de la memoria compartida entre agentes. Una l\xEDnea por nota, con este",
383
- "formato: `- [T\xEDtulo](archivo.md) \u2014 de qu\xE9 trata, en una l\xEDnea`.",
397
+ "Index of the memory shared between agents. One line per note, in this",
398
+ "format: `- [Title](file.md) \u2014 what it is about, in one line`.",
384
399
  "",
385
400
  ""
386
401
  ].join("\n");
@@ -606,11 +621,14 @@ function parseVaultBodyLine(value) {
606
621
  }
607
622
 
608
623
  // packages/shared/src/protocol.ts
609
- var PROTOCOL_VERSION = 7;
624
+ var PROTOCOL_VERSION = 8;
610
625
  var WS_PATH = "/ws";
611
626
  var TOKEN_QUERY_PARAM = "token";
612
627
  var MAX_SUBMIT_IMAGE_BYTES = 12 * 1024 * 1024;
613
628
  var MAX_SUBMIT_IMAGES = 8;
629
+ var MAX_SUBMIT_FILE_BYTES = 10 * 1024 * 1024;
630
+ var MAX_SUBMIT_FILES = 4;
631
+ var CONTINUE_LABEL_MAX_CHARS = 120;
614
632
  function parseSubmitImage(value) {
615
633
  const record = asRecord(value);
616
634
  if (record === null) return null;
@@ -618,6 +636,13 @@ function parseSubmitImage(value) {
618
636
  const data = asNonEmptyString(record["data"]);
619
637
  return mediaType === null || data === null ? null : { mediaType, data };
620
638
  }
639
+ function parseSubmitFile(value) {
640
+ const record = asRecord(value);
641
+ if (record === null) return null;
642
+ const name = asNonEmptyString(record["name"]);
643
+ const data = asNonEmptyString(record["data"]);
644
+ return name === null || data === null ? null : { name, data };
645
+ }
621
646
  function parseSelection(value) {
622
647
  if (Array.isArray(value)) {
623
648
  const chosen = [];
@@ -648,6 +673,11 @@ function parseClientMessage(raw) {
648
673
  const images = asArrayOf(record["images"], parseSubmitImage);
649
674
  if (terminalId === null || text === null || images === null) return null;
650
675
  const message = { type: "agent.submit", terminalId, text, images };
676
+ if (record["files"] !== void 0) {
677
+ const files = asArrayOf(record["files"], parseSubmitFile);
678
+ if (files === null) return null;
679
+ if (files.length > 0) message.files = files;
680
+ }
651
681
  if (record["send"] === false) message.send = false;
652
682
  return message;
653
683
  }
@@ -909,7 +939,15 @@ function parseClientMessage(raw) {
909
939
  const agent = asLiteral(record["agent"], SESSION_AGENT_IDS);
910
940
  const sessionId = asNonEmptyString(record["sessionId"]);
911
941
  if (requestId === null || agent === null || sessionId === null) return null;
912
- const base = { type: "session.continue", requestId, agent, sessionId };
942
+ const rawLabel = asString(record["label"]);
943
+ const label = rawLabel === null ? "" : rawLabel.replace(/\s+/g, " ").trim().slice(0, CONTINUE_LABEL_MAX_CHARS);
944
+ const base = {
945
+ type: "session.continue",
946
+ requestId,
947
+ agent,
948
+ sessionId,
949
+ ...label.length > 0 ? { label } : {}
950
+ };
913
951
  const rawTarget = record["target"];
914
952
  if (rawTarget === void 0 || rawTarget === null) return null;
915
953
  const target = asLiteral(rawTarget, AGENT_IDS);
@@ -984,6 +1022,14 @@ function insertionIndex(order, describe, descriptor, platform) {
984
1022
  return last === -1 ? order.length : last + 1;
985
1023
  }
986
1024
 
1025
+ // packages/shared/src/pasted-text.ts
1026
+ function pastedBlockPattern() {
1027
+ return /(^|\n)\[Start of pasted text #(\d{1,6})\]\n([\s\S]*?)\n\[End of pasted text #\2\](?=\n|$)/g;
1028
+ }
1029
+ function pastedBoundaryPattern() {
1030
+ return /\[(?:Start|End) of pasted text #\d{1,6}\]/g;
1031
+ }
1032
+
987
1033
  // packages/server/src/archived-sessions.ts
988
1034
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
989
1035
  import path2 from "node:path";
@@ -1126,7 +1172,7 @@ var ArchivedSessions = class {
1126
1172
  );
1127
1173
  await rename(temporary, target);
1128
1174
  } catch (error) {
1129
- console.warn("[archivadas] no se pudo guardar la lista:", error);
1175
+ console.warn("[archived] couldn't save the list:", error);
1130
1176
  }
1131
1177
  }
1132
1178
  };
@@ -1202,7 +1248,7 @@ async function locateCommand(command) {
1202
1248
  };
1203
1249
  }
1204
1250
  function commandNotFoundMessage(command, installUrl) {
1205
- return `No se encontro el comando "${command}" en el PATH. Agent Workbench usa la CLI que ya tengas instalada: no la incluye ni la descarga. Instalala desde ${installUrl} y volve a arrancar.`;
1251
+ return serverText("cliMissing", { command, url: installUrl });
1206
1252
  }
1207
1253
 
1208
1254
  // packages/server/src/agents/antigravity/catalog.ts
@@ -1285,7 +1331,7 @@ function nodeHasUnflaggedSqlite(nodeVersion) {
1285
1331
  return major > 23;
1286
1332
  }
1287
1333
  function sqliteUnavailableText(label, nodeVersion = process.version) {
1288
- return `esta version de Node (${nodeVersion}) no trae node:sqlite; el historial de ${label} necesita Node ${NODE_SQLITE_MIN_VERSION} o posterior.`;
1334
+ return `this Node version (${nodeVersion}) has no node:sqlite; the ${label} history needs Node ${NODE_SQLITE_MIN_VERSION} or later.`;
1289
1335
  }
1290
1336
  function isSqliteExperimentalWarning(warning, typeOrOptions) {
1291
1337
  let type = "Warning";
@@ -1440,7 +1486,7 @@ var ReadOnlyDatabase = class {
1440
1486
  db.close();
1441
1487
  this.state = "schema";
1442
1488
  console.warn(
1443
- `[${this.options.label}] a ${this.file} le falta la columna ${missing.table}.${missing.column}: no se lee hasta reiniciar.`
1489
+ `[${this.options.label}] ${this.file} is missing the column ${missing.table}.${missing.column}: it isn't read until a restart.`
1444
1490
  );
1445
1491
  return null;
1446
1492
  }
@@ -1547,7 +1593,7 @@ var FileStampSignal = class {
1547
1593
  try {
1548
1594
  listener(file);
1549
1595
  } catch (error) {
1550
- console.warn("[antigravity] un aviso de cambio del indice fallo:", error);
1596
+ console.warn("[antigravity] an index change listener failed:", error);
1551
1597
  }
1552
1598
  }
1553
1599
  }
@@ -1981,7 +2027,7 @@ var AntigravityCatalog = class {
1981
2027
  if (signature.db.size > CATALOG_LIMITS.summariesBytes) {
1982
2028
  this.summaries = /* @__PURE__ */ new Map();
1983
2029
  this.summariesStamp = signature.key;
1984
- this.warnOnce(signature.key, `el indice de conversaciones pesa mas de ${CATALOG_LIMITS.summariesBytes / 1024 / 1024} MB; no se lee.`);
2030
+ this.warnOnce(signature.key, `the conversation index is over ${CATALOG_LIMITS.summariesBytes / 1024 / 1024} MB; it isn't read.`);
1985
2031
  return;
1986
2032
  }
1987
2033
  const sqlite = this.sqlite();
@@ -2011,7 +2057,7 @@ var AntigravityCatalog = class {
2011
2057
  this.summaries = next;
2012
2058
  this.summariesStamp = copied.key;
2013
2059
  } catch (error) {
2014
- this.warnOnce(signature.key, `no pude leer el indice de conversaciones: ${error instanceof Error ? error.message : String(error)}`);
2060
+ this.warnOnce(signature.key, `couldn't read the conversation index: ${error instanceof Error ? error.message : String(error)}`);
2015
2061
  } finally {
2016
2062
  await rm(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 }).catch(() => void 0);
2017
2063
  }
@@ -2228,7 +2274,7 @@ var LogWatcher = class {
2228
2274
  if (lines !== null) this.take(lines);
2229
2275
  }
2230
2276
  if (this.reader?.exhausted === true && !this.disposed) {
2231
- this.warn(`[antigravity] el log de ${label} paso 256 MB; dejo de seguirlo`);
2277
+ this.warn(`[antigravity] the log of ${label} went over 256 MB; stopped following it`);
2232
2278
  this.mode = "stopped";
2233
2279
  }
2234
2280
  } catch {
@@ -2237,7 +2283,7 @@ var LogWatcher = class {
2237
2283
  async search(label) {
2238
2284
  const { launchedAt, pid } = this.options;
2239
2285
  if (this.now() - launchedAt > (this.options.fallbackWindowMs ?? FALLBACK_WINDOW_MS)) {
2240
- this.warn(`[antigravity] no pude descubrir la conversacion de ${label}`);
2286
+ this.warn(`[antigravity] couldn't discover the conversation of ${label}`);
2241
2287
  this.mode = "stopped";
2242
2288
  return;
2243
2289
  }
@@ -2278,7 +2324,7 @@ var LogWatcher = class {
2278
2324
  try {
2279
2325
  this.options.onConversation(id);
2280
2326
  } catch (error) {
2281
- console.warn("[antigravity] el aviso de conversacion nueva fallo:", error);
2327
+ console.warn("[antigravity] the new conversation listener failed:", error);
2282
2328
  }
2283
2329
  }
2284
2330
  }
@@ -2292,12 +2338,17 @@ import path7 from "node:path";
2292
2338
  var TITLE_MAX_LENGTH = 90;
2293
2339
  var UNTITLED_SESSION_TITLE = "Sesion sin titulo";
2294
2340
  function toTitle(raw) {
2295
- let text = raw.replace(/<[^>]{1,80}>/g, " ").replace(/```[\s\S]*?```/g, " ").replace(/\s+/g, " ").trim();
2341
+ let text = withoutPastedText(raw).replace(/<[^>]{1,80}>/g, " ").replace(/```[\s\S]*?```/g, " ").replace(/\s+/g, " ").trim();
2296
2342
  if (text.length > TITLE_MAX_LENGTH) {
2297
2343
  text = `${text.slice(0, TITLE_MAX_LENGTH - 1).trimEnd()}\u2026`;
2298
2344
  }
2299
2345
  return text;
2300
2346
  }
2347
+ function withoutPastedText(raw) {
2348
+ const normalized = raw.replace(/\r\n?/g, "\n");
2349
+ const typed = normalized.replace(pastedBlockPattern(), "$1");
2350
+ return typed.trim().length > 0 ? typed : normalized.replace(pastedBoundaryPattern(), " ");
2351
+ }
2301
2352
 
2302
2353
  // packages/server/src/agents/antigravity/session-follower.ts
2303
2354
  import { stat as stat4 } from "node:fs/promises";
@@ -2350,7 +2401,7 @@ var JsonlFollower = class {
2350
2401
  */
2351
2402
  setPath(filePath) {
2352
2403
  if (this.path !== null) {
2353
- throw new Error(`El seguidor ya tiene archivo: ${this.path}`);
2404
+ throw new Error(`The follower already has a file: ${this.path}`);
2354
2405
  }
2355
2406
  this.path = filePath;
2356
2407
  }
@@ -2855,7 +2906,7 @@ var AntigravitySessionFollower = class {
2855
2906
  get label() {
2856
2907
  if (this.jsonl?.filePath !== null && this.jsonl?.filePath !== void 0) return this.jsonl.filePath;
2857
2908
  if (this.paths !== null) return this.paths.full;
2858
- return "antigravity:(sin conversacion)";
2909
+ return "antigravity:(no conversation)";
2859
2910
  }
2860
2911
  /** El valor provisional sale de `defaults`: aca no hay nada que preparar. */
2861
2912
  async start() {
@@ -3841,7 +3892,7 @@ function createAntigravityAdapter(options = {}) {
3841
3892
  try {
3842
3893
  await installStatusLineScript(integrationsDir());
3843
3894
  } catch (error) {
3844
- console.warn(`[antigravity] no pude reinstalar el script de la status line: ${String(error)}`);
3895
+ console.warn(`[antigravity] couldn't reinstall the status line script: ${String(error)}`);
3845
3896
  }
3846
3897
  }
3847
3898
  const changed = settings2.statusLine.state !== state;
@@ -3875,12 +3926,12 @@ function createAntigravityAdapter(options = {}) {
3875
3926
  launch(input) {
3876
3927
  const { resumeSessionId } = input;
3877
3928
  if (resumeSessionId !== null && !CONVERSATION_ID_PATTERN.test(resumeSessionId)) {
3878
- throw new Error("Id de conversacion de Antigravity CLI invalido.");
3929
+ throw new Error("Invalid Antigravity CLI conversation id.");
3879
3930
  }
3880
3931
  let logPath = cliLogPathFor(input.launchToken);
3881
3932
  if (logPath !== null && input.location.prefixArgs.length > 0 && !SHIM_SAFE_PATH.test(logPath)) {
3882
3933
  console.warn(
3883
- "[antigravity] la CLI llega por un shim y la carpeta temporal tiene caracteres que cmd /c corta: sin log propio, una pestana nueva no descubre su conversacion ni ve un /clear."
3934
+ "[antigravity] the CLI runs through a shim and the temp folder has characters that cmd /c cuts: without its own log, a new tab doesn't discover its conversation or see a /clear."
3884
3935
  );
3885
3936
  logPath = null;
3886
3937
  }
@@ -3994,7 +4045,7 @@ function createAntigravityAdapter(options = {}) {
3994
4045
  try {
3995
4046
  await installStatusLineScript(integrationsDir());
3996
4047
  } catch (error) {
3997
- console.warn(`[antigravity] no pude instalar el script de la status line: ${String(error)}`);
4048
+ console.warn(`[antigravity] couldn't install the status line script: ${String(error)}`);
3998
4049
  }
3999
4050
  await removeStaleCliLogs();
4000
4051
  await store.removeStale();
@@ -4370,28 +4421,24 @@ import path15 from "node:path";
4370
4421
  // packages/server/src/path-guard.ts
4371
4422
  import { realpath } from "node:fs/promises";
4372
4423
  import path14 from "node:path";
4373
- var InvalidPathError = class extends Error {
4374
- constructor(message) {
4375
- super(message);
4376
- this.name = "InvalidPathError";
4377
- }
4424
+ var InvalidPathError = class extends ServerTextError {
4378
4425
  };
4379
4426
  function toPosixPath(value) {
4380
4427
  return value.split(path14.sep).join("/");
4381
4428
  }
4382
4429
  function assertRelativeShape(relativePath) {
4383
4430
  if (relativePath.includes("\0")) {
4384
- throw new InvalidPathError("La ruta trae un byte nulo.");
4431
+ throw new InvalidPathError(serverText("pathNullByte"));
4385
4432
  }
4386
4433
  if (path14.isAbsolute(relativePath) || /^[a-zA-Z]:/.test(relativePath)) {
4387
- throw new InvalidPathError("Solo se aceptan rutas relativas al directorio de la pestana.");
4434
+ throw new InvalidPathError(serverText("pathNotRelative"));
4388
4435
  }
4389
4436
  if (relativePath.startsWith("/") || relativePath.startsWith("\\")) {
4390
- throw new InvalidPathError("Solo se aceptan rutas relativas al directorio de la pestana.");
4437
+ throw new InvalidPathError(serverText("pathNotRelative"));
4391
4438
  }
4392
4439
  const segments = relativePath.split(/[\\/]/);
4393
4440
  if (segments.includes("..")) {
4394
- throw new InvalidPathError("La ruta se sale del directorio de la pestana.");
4441
+ throw new InvalidPathError(serverText("pathOutside"));
4395
4442
  }
4396
4443
  }
4397
4444
  function isInside(root, candidate) {
@@ -4403,25 +4450,25 @@ async function resolveInside(root, relativePath, options = {}) {
4403
4450
  assertRelativeShape(relativePath);
4404
4451
  const absolute = path14.resolve(root, relativePath);
4405
4452
  if (!isInside(root, absolute)) {
4406
- throw new InvalidPathError("La ruta se sale del directorio de la pestana.");
4453
+ throw new InvalidPathError(serverText("pathOutside"));
4407
4454
  }
4408
4455
  let realRoot;
4409
4456
  try {
4410
4457
  realRoot = await realpath(root);
4411
4458
  } catch {
4412
- throw new InvalidPathError("El directorio de la pestana ya no existe.");
4459
+ throw new InvalidPathError(serverText("tabDirGone"));
4413
4460
  }
4414
4461
  let realTarget;
4415
4462
  try {
4416
4463
  realTarget = await realpath(absolute);
4417
4464
  } catch {
4418
4465
  if (options.mustExist === true) {
4419
- throw new InvalidPathError("La ruta no existe.");
4466
+ throw new InvalidPathError(serverText("pathMissing"));
4420
4467
  }
4421
4468
  realTarget = await realpathOfNearestParent(absolute);
4422
4469
  }
4423
4470
  if (!isInside(realRoot, realTarget)) {
4424
- throw new InvalidPathError("La ruta se sale del directorio de la pestana.");
4471
+ throw new InvalidPathError(serverText("pathOutside"));
4425
4472
  }
4426
4473
  return absolute;
4427
4474
  }
@@ -4432,7 +4479,7 @@ async function realpathOfNearestParent(absolute) {
4432
4479
  return await realpath(current);
4433
4480
  } catch {
4434
4481
  const parent = path14.dirname(current);
4435
- if (parent === current) throw new InvalidPathError("La ruta no existe.");
4482
+ if (parent === current) throw new InvalidPathError(serverText("pathMissing"));
4436
4483
  current = parent;
4437
4484
  }
4438
4485
  }
@@ -4449,6 +4496,7 @@ function isPlanFileName(fileName) {
4449
4496
  if (fileName.length === 0 || fileName.length > 255) return false;
4450
4497
  if (fileName.includes("\0")) return false;
4451
4498
  if (path15.basename(fileName) !== fileName) return false;
4499
+ if (path15.win32.basename(fileName) !== fileName) return false;
4452
4500
  if (fileName === "." || fileName === "..") return false;
4453
4501
  return fileName.toLowerCase().endsWith(".md");
4454
4502
  }
@@ -4544,6 +4592,7 @@ import path17 from "node:path";
4544
4592
  import path16 from "node:path";
4545
4593
  var HARNESS_TAGS = [
4546
4594
  "system-reminder",
4595
+ "task-notification",
4547
4596
  "command-name",
4548
4597
  "command-message",
4549
4598
  "command-args",
@@ -4558,6 +4607,11 @@ var HARNESS_BLOCK = new RegExp(`<(${HARNESS_TAGS.join("|")})>[\\s\\S]*?</\\1>`,
4558
4607
  function cleanUserText(raw) {
4559
4608
  return raw.replace(HARNESS_BLOCK, "").trim();
4560
4609
  }
4610
+ function isHumanOrigin(origin) {
4611
+ if (typeof origin !== "object" || origin === null) return true;
4612
+ const kind = origin["kind"];
4613
+ return typeof kind !== "string" || kind === "human";
4614
+ }
4561
4615
  function readUsage(value) {
4562
4616
  if (typeof value !== "object" || value === null) return null;
4563
4617
  const record = value;
@@ -4715,10 +4769,7 @@ function toQueuedUserEvent(record, lineNumber, limits) {
4715
4769
  const attachmentRecord = attachment;
4716
4770
  if (attachmentRecord["type"] !== "queued_command") return null;
4717
4771
  if (attachmentRecord["commandMode"] !== "prompt") return null;
4718
- const origin = attachmentRecord["origin"];
4719
- if (typeof origin === "object" && origin !== null) {
4720
- if (origin["kind"] !== "human") return null;
4721
- }
4772
+ if (!isHumanOrigin(attachmentRecord["origin"])) return null;
4722
4773
  const prompt = attachmentRecord["prompt"];
4723
4774
  if (typeof prompt !== "string") return null;
4724
4775
  const cleaned = cleanUserText(prompt);
@@ -4804,6 +4855,7 @@ function toConversationEvent(record, lineNumber, limits = TRANSPORT_LIMITS) {
4804
4855
  if (record["isSidechain"] === true) return null;
4805
4856
  if (type === "attachment") return toQueuedUserEvent(record, lineNumber, limits);
4806
4857
  if (type !== "user" && type !== "assistant") return null;
4858
+ if (type === "user" && !isHumanOrigin(record["origin"])) return null;
4807
4859
  const message = record["message"];
4808
4860
  if (typeof message !== "object" || message === null) return null;
4809
4861
  const messageRecord = message;
@@ -5452,7 +5504,7 @@ var ClaudeCodeSessionFollower = class {
5452
5504
  if (isSameFilePath(this.inner.filePath, filePath, process.platform)) return true;
5453
5505
  if (this.inner.getState() === "waiting" && path17.basename(filePath).toLowerCase() === `${this.sessionId.toLowerCase()}.jsonl`) {
5454
5506
  console.warn(
5455
- `[conversacion] la sesion ${this.sessionId.slice(0, 8)} escribio en ${filePath}, no en la ruta calculada; me mudo ahi.`
5507
+ `[conversation] session ${this.sessionId.slice(0, 8)} wrote to ${filePath}, not to the computed path; moving there.`
5456
5508
  );
5457
5509
  this.inner = new ConversationFollower(filePath, void 0, {
5458
5510
  ...this.options,
@@ -5891,7 +5943,7 @@ function codexHome() {
5891
5943
  if (!warnedHomes.has(configured)) {
5892
5944
  warnedHomes.add(configured);
5893
5945
  console.warn(
5894
- `[codex] CODEX_HOME no es una ruta absoluta (${configured}): no se lee el historial de Codex.`
5946
+ `[codex] CODEX_HOME isn't an absolute path (${configured}): the Codex history isn't read.`
5895
5947
  );
5896
5948
  }
5897
5949
  return null;
@@ -6185,17 +6237,17 @@ function pickPendingTab(candidate, pending, married = []) {
6185
6237
  terminalId: chosen2.terminalId,
6186
6238
  confirmedByText: true,
6187
6239
  uncertain,
6188
- reason: uncertain ? "dos pestanas mandaron el mismo texto" : null
6240
+ reason: uncertain ? "two tabs sent the same text" : null
6189
6241
  };
6190
6242
  }
6191
6243
  const chosen = latest(eligible);
6192
6244
  let reason2 = null;
6193
6245
  if (eligible.some((tab) => tab !== chosen && Math.abs(tab.launchedAt - chosen.launchedAt) < CLOSE_LAUNCH_MS)) {
6194
- reason2 = "dos pestanas del mismo proyecto se lanzaron con menos de 1,5 s de diferencia";
6246
+ reason2 = "two tabs of the same project were launched less than 1.5 s apart";
6195
6247
  } else if (chosen.submitted.length > 0) {
6196
- reason2 = "el primer mensaje no es el que mando el cuadro de escritura";
6248
+ reason2 = "the first message isn't the one the input box sent";
6197
6249
  } else if (married.some((tab) => tab.terminalId !== chosen.terminalId && tab.cwdKey === candidate.cwdKey)) {
6198
- reason2 = "hay otra pestana de Codex abierta en el mismo proyecto";
6250
+ reason2 = "another Codex tab is open in the same project";
6199
6251
  }
6200
6252
  return { terminalId: chosen.terminalId, confirmedByText: false, uncertain: reason2 !== null, reason: reason2 };
6201
6253
  }
@@ -6303,7 +6355,7 @@ var CodexSessionDiscovery = class {
6303
6355
  scanOnce() {
6304
6356
  if (this.running !== null) return this.running;
6305
6357
  const run = this.scan().catch((error) => {
6306
- console.warn("[codex] fallo el descubrimiento de sesiones:", error);
6358
+ console.warn("[codex] session discovery failed:", error);
6307
6359
  }).finally(() => {
6308
6360
  this.running = null;
6309
6361
  });
@@ -6378,9 +6430,9 @@ var CodexSessionDiscovery = class {
6378
6430
  const id8 = candidate.sessionId.slice(0, 8);
6379
6431
  const tab8 = tab.terminalId.slice(0, 8);
6380
6432
  if (picked.uncertain) {
6381
- console.warn(`[codex] la sesion ${id8} se asigno a la pestana ${tab8} sin confirmar: ${picked.reason ?? ""}`);
6433
+ console.warn(`[codex] session ${id8} was assigned to tab ${tab8} without confirmation: ${picked.reason ?? ""}`);
6382
6434
  }
6383
- debugLog("registro", `sesion de codex ${id8} -> ${tab8} (${picked.confirmedByText ? "texto" : "lanzamiento"})`);
6435
+ debugLog("registry", `codex session ${id8} -> ${tab8} (${picked.confirmedByText ? "text" : "launch"})`);
6384
6436
  tab.reportSessionId(candidate.sessionId);
6385
6437
  }
6386
6438
  this.stopTimerIfIdle();
@@ -6455,7 +6507,7 @@ function listableMeta(line, item) {
6455
6507
  if (meta.id.toLowerCase() !== item.sessionId.toLowerCase()) return null;
6456
6508
  if (typeof meta.source !== "string" || !INTERACTIVE_SOURCES.includes(meta.source)) return null;
6457
6509
  if (meta.historyMode !== null && !LISTED_HISTORY_MODES.includes(meta.historyMode)) {
6458
- debugLog("indice", `rollout de codex ${item.sessionId.slice(0, 8)} en modo ${meta.historyMode}: no se lista`);
6510
+ debugLog("index", `codex rollout ${item.sessionId.slice(0, 8)} in ${meta.historyMode} mode: not listed`);
6459
6511
  return null;
6460
6512
  }
6461
6513
  if (meta.cwd.length === 0) return null;
@@ -6698,7 +6750,7 @@ var CodexRolloutSink = class {
6698
6750
  }
6699
6751
  this.warnedForeignMeta = true;
6700
6752
  console.warn(
6701
- `[codex] el archivo de la sesion ${this.sessionId.slice(0, 8)} dice ser ${meta.id.slice(0, 8)}; se sigue leyendo igual.`
6753
+ `[codex] the file of session ${this.sessionId.slice(0, 8)} says it's ${meta.id.slice(0, 8)}; reading it anyway.`
6702
6754
  );
6703
6755
  }
6704
6756
  consumeEvent(kind, payload, lineNumber, at, events, pendingContent) {
@@ -6846,7 +6898,7 @@ var CodexSessionFollower = class {
6846
6898
  get label() {
6847
6899
  const filePath = this.jsonl.filePath;
6848
6900
  if (filePath !== null) return filePath;
6849
- return this.sessionId.length > 0 ? `codex:${this.sessionId.slice(0, 8)}` : "codex:(sin sesion)";
6901
+ return this.sessionId.length > 0 ? `codex:${this.sessionId.slice(0, 8)}` : "codex:(no session)";
6850
6902
  }
6851
6903
  async start() {
6852
6904
  await this.lookup();
@@ -7043,15 +7095,21 @@ var CODEX_CAPABILITIES = {
7043
7095
  // Sin estado publicado no hay espera que mirar: manda el candado de 10.8.
7044
7096
  waitingBlocksSubmit: false
7045
7097
  };
7046
- var CODEX_INPUT = {
7047
- imageReference: "bare-path-paste",
7048
- pieceGapMs: 400,
7049
- pasteMarkers: true,
7050
- enterSeparately: true,
7051
- interruptPresses: 1,
7052
- // `@` abre el buscador de la TUI: el transcript se nombra entre comillas y lo lee el agente.
7053
- transcriptReference: "quoted-path"
7054
- };
7098
+ function codexInput(platform) {
7099
+ const windows = platform === "win32";
7100
+ return {
7101
+ imageReference: "bare-path-paste",
7102
+ pieceGapMs: 400,
7103
+ pasteMarkers: true,
7104
+ enterSeparately: true,
7105
+ interruptPresses: 1,
7106
+ // `@` abre el buscador de la TUI: el transcript se nombra entre comillas y lo lee el agente.
7107
+ transcriptReference: "quoted-path",
7108
+ endBeforeSubmit: windows,
7109
+ readyAfterLaunchMs: windows ? 3e3 : 0
7110
+ };
7111
+ }
7112
+ var CODEX_INPUT = codexInput(process.platform);
7055
7113
  function createCodexAdapter() {
7056
7114
  const discovery = new CodexSessionDiscovery();
7057
7115
  return {
@@ -7077,7 +7135,7 @@ function createCodexAdapter() {
7077
7135
  if (resumeSessionId === null) {
7078
7136
  return { file: input.location.file, args: [...input.location.prefixArgs], session: { kind: "discover" } };
7079
7137
  }
7080
- if (!isUuid(resumeSessionId)) throw new Error("Id de sesion de Codex invalido.");
7138
+ if (!isUuid(resumeSessionId)) throw new Error("Invalid Codex session id.");
7081
7139
  return {
7082
7140
  file: input.location.file,
7083
7141
  args: [...input.location.prefixArgs, "resume", resumeSessionId],
@@ -7286,7 +7344,7 @@ var DbChangeSignal = class {
7286
7344
  try {
7287
7345
  listener();
7288
7346
  } catch (error) {
7289
- console.warn("[opencode] un aviso de cambio de la base fallo:", error);
7347
+ console.warn("[opencode] a database change listener failed:", error);
7290
7348
  }
7291
7349
  }
7292
7350
  }
@@ -7840,7 +7898,7 @@ var OpenCodeSessionFollower = class {
7840
7898
  }
7841
7899
  get label() {
7842
7900
  const base = this.dbFile ?? "opencode";
7843
- return `${base}#${this.sessionId.length > 0 ? this.sessionId : "(sin sesion)"}`;
7901
+ return `${base}#${this.sessionId.length > 0 ? this.sessionId : "(no session)"}`;
7844
7902
  }
7845
7903
  /** Precarga el catalogo, para que la primera lectura no espere 93 ms de JSON. Nunca lanza. */
7846
7904
  async start() {
@@ -7955,7 +8013,7 @@ var OpenCodeSessionFollower = class {
7955
8013
  this.delivered = next;
7956
8014
  const diff = diffEvents(previous, next);
7957
8015
  if (diff === null) {
7958
- debugLog("conversacion", `opencode: evento fuera de orden o desaparecido en ${this.sessionId}, rehago`);
8016
+ debugLog("conversation", `opencode: out-of-order or vanished event in ${this.sessionId}, rebuilding`);
7959
8017
  return { ...emptyResult(), reset: true };
7960
8018
  }
7961
8019
  return { reset: false, added: diff.added, turns: diff.turns, plans: [], parts: diff.parts, usageChanged };
@@ -8120,7 +8178,7 @@ function createOpenCodeHistory(deps) {
8120
8178
  const readable = () => db.status() === "ok" || db.status() === "error";
8121
8179
  const sessionRow = (ref) => {
8122
8180
  const row = db.get(OPENCODE_SQL.sessionById, ref);
8123
- if (db.status() !== "ok") throw new Error(`la base de OpenCode no se pudo leer (${db.status()})`);
8181
+ if (db.status() !== "ok") throw new Error(`the OpenCode database couldn't be read (${db.status()})`);
8124
8182
  return row;
8125
8183
  };
8126
8184
  const unreadable = (ref, error) => {
@@ -8132,12 +8190,12 @@ function createOpenCodeHistory(deps) {
8132
8190
  if (row === void 0 || row.parent_id !== null) return null;
8133
8191
  const defaultTitle = DEFAULT_TITLE_PATTERN.test(row.title);
8134
8192
  const hasMessages = db.get(OPENCODE_SQL.sessionHasMessages, row.id) !== void 0;
8135
- if (db.status() !== "ok") throw new Error(`la base de OpenCode no se pudo leer (${db.status()})`);
8193
+ if (db.status() !== "ok") throw new Error(`the OpenCode database couldn't be read (${db.status()})`);
8136
8194
  if (!hasMessages) {
8137
8195
  if (defaultTitle) return null;
8138
8196
  if (!warnedWithoutMessages) {
8139
8197
  warnedWithoutMessages = true;
8140
- warn("[opencode] hay sesiones sin filas en la tabla message: su conversacion puede verse vacia. Si pasa con una sesion con mensajes, OpenCode cambio donde los guarda.");
8198
+ warn("[opencode] some sessions have no rows in the message table: their conversation may look empty. If it happens with a session that has messages, OpenCode changed where it stores them.");
8141
8199
  }
8142
8200
  }
8143
8201
  let title;
@@ -8192,7 +8250,7 @@ function createOpenCodeHistory(deps) {
8192
8250
  stamps = new Map(rows.map((row) => [row.id, row.time_updated]));
8193
8251
  return rows.map(itemOf2);
8194
8252
  } catch (error) {
8195
- warn(`[opencode] no se pudo listar el historial: ${error instanceof Error ? error.message : String(error)}`);
8253
+ warn(`[opencode] couldn't list the history: ${error instanceof Error ? error.message : String(error)}`);
8196
8254
  return null;
8197
8255
  }
8198
8256
  },
@@ -8510,11 +8568,11 @@ function loopbackBase(url) {
8510
8568
  try {
8511
8569
  parsed = new URL(url);
8512
8570
  } catch {
8513
- throw new Error("La URL del servidor de OpenCode no es valida.");
8571
+ throw new Error("The OpenCode server URL isn't valid.");
8514
8572
  }
8515
8573
  const bare = parsed.pathname === "/" && parsed.search === "" && parsed.hash === "" && parsed.username === "" && parsed.password === "";
8516
8574
  if (parsed.protocol !== "http:" || parsed.hostname !== "127.0.0.1" || parsed.port === "" || !bare) {
8517
- throw new Error("El servidor de OpenCode tiene que escuchar en http://127.0.0.1.");
8575
+ throw new Error("The OpenCode server has to listen on http://127.0.0.1.");
8518
8576
  }
8519
8577
  return `http://127.0.0.1:${parsed.port}`;
8520
8578
  }
@@ -8539,7 +8597,7 @@ var OpenCodeServeClient = class {
8539
8597
  const body = asRecord4(await this.request("POST", "/session", directory, {}));
8540
8598
  const id = body?.["id"];
8541
8599
  if (typeof id !== "string" || !OPENCODE_SESSION_ID_PATTERN.test(id)) {
8542
- throw new ServeRequestError("El servidor de OpenCode devolvio un id de sesion con una forma inesperada.", null);
8600
+ throw new ServeRequestError("The OpenCode server returned a session id with an unexpected shape.", null);
8543
8601
  }
8544
8602
  return id;
8545
8603
  }
@@ -8577,7 +8635,7 @@ var OpenCodeServeClient = class {
8577
8635
  }
8578
8636
  /** Corta lo que la sesion este haciendo (D13). */
8579
8637
  async abort(directory, sessionId) {
8580
- if (!OPENCODE_SESSION_ID_PATTERN.test(sessionId)) throw new Error("Id de sesion de OpenCode invalido.");
8638
+ if (!OPENCODE_SESSION_ID_PATTERN.test(sessionId)) throw new Error("Invalid OpenCode session id.");
8581
8639
  await this.request("POST", `/session/${sessionId}/abort`, directory, {});
8582
8640
  }
8583
8641
  /**
@@ -8609,7 +8667,7 @@ var OpenCodeServeClient = class {
8609
8667
  });
8610
8668
  this.checkResponse(response, "/global/event");
8611
8669
  const body = response.body;
8612
- if (body === null) throw new ServeRequestError("El flujo de eventos llego sin cuerpo.", response.status);
8670
+ if (body === null) throw new ServeRequestError("The event stream arrived without a body.", response.status);
8613
8671
  failures = 0;
8614
8672
  try {
8615
8673
  onReconnect();
@@ -8633,7 +8691,7 @@ var OpenCodeServeClient = class {
8633
8691
  }
8634
8692
  } catch (error) {
8635
8693
  if (closed) return;
8636
- debugLog("opencode-serve", `flujo de eventos cortado: ${error instanceof Error ? error.name : "error"}`);
8694
+ debugLog("opencode-serve", `event stream cut: ${error instanceof Error ? error.name : "error"}`);
8637
8695
  }
8638
8696
  if (closed) return;
8639
8697
  const delays = this.reconnectDelaysMs;
@@ -8653,13 +8711,13 @@ var OpenCodeServeClient = class {
8653
8711
  };
8654
8712
  }
8655
8713
  checkResponse(response, route) {
8656
- if (response.status === 401) throw new ServeAuthError("El servidor de OpenCode rechazo la contrasena.");
8714
+ if (response.status === 401) throw new ServeAuthError("The OpenCode server rejected the password.");
8657
8715
  const type = response.headers.get("content-type") ?? "";
8658
8716
  if (type.includes("text/html")) {
8659
- throw new ServeRouteError(`Esta version de OpenCode no tiene la ruta ${route}.`);
8717
+ throw new ServeRouteError(`This OpenCode version doesn't have the route ${route}.`);
8660
8718
  }
8661
8719
  if (!response.ok) {
8662
- throw new ServeRequestError(`El servidor de OpenCode respondio ${response.status} en ${route}.`, response.status);
8720
+ throw new ServeRequestError(`The OpenCode server answered ${response.status} on ${route}.`, response.status);
8663
8721
  }
8664
8722
  }
8665
8723
  async request(method, route, directory, body) {
@@ -8684,7 +8742,7 @@ var OpenCodeServeClient = class {
8684
8742
  try {
8685
8743
  return JSON.parse(text);
8686
8744
  } catch {
8687
- throw new ServeRequestError(`El servidor de OpenCode respondio algo que no es JSON en ${route}.`, response.status);
8745
+ throw new ServeRequestError(`The OpenCode server answered something that isn't JSON on ${route}.`, response.status);
8688
8746
  }
8689
8747
  }
8690
8748
  };
@@ -8722,21 +8780,21 @@ function reason(error) {
8722
8780
  function launchWithServe(deps, input) {
8723
8781
  const { resumeSessionId } = input;
8724
8782
  if (resumeSessionId !== null && !OPENCODE_SESSION_ID_PATTERN.test(resumeSessionId)) {
8725
- throw new Error("Id de sesion de OpenCode invalido.");
8783
+ throw new Error("Invalid OpenCode session id.");
8726
8784
  }
8727
8785
  return (async () => {
8728
8786
  let endpoint;
8729
8787
  try {
8730
8788
  endpoint = await deps.ensure();
8731
8789
  } catch (error) {
8732
- throw new Error(`No se pudo arrancar el servidor de OpenCode: ${reason(error)}`);
8790
+ throw new Error(`Couldn't start the OpenCode server: ${reason(error)}`);
8733
8791
  }
8734
8792
  let sessionId = resumeSessionId;
8735
8793
  if (sessionId === null) {
8736
8794
  try {
8737
8795
  sessionId = await deps.createSession(endpoint, input.cwd);
8738
8796
  } catch (error) {
8739
- throw new Error(`El servidor de OpenCode no creo la sesion: ${reason(error)}`);
8797
+ throw new Error(`The OpenCode server didn't create the session: ${reason(error)}`);
8740
8798
  }
8741
8799
  }
8742
8800
  deps.track(endpoint, sessionId, input.cwd);
@@ -8828,7 +8886,7 @@ var OpenCodeServeProcess = class {
8828
8886
  * apaga igual al cumplirse.
8829
8887
  */
8830
8888
  ensure() {
8831
- if (this.disposed) return Promise.reject(new ServeStartError("la app se esta apagando"));
8889
+ if (this.disposed) return Promise.reject(new ServeStartError("the app is shutting down"));
8832
8890
  const endpoint = this.running?.endpoint ?? null;
8833
8891
  if (endpoint !== null) {
8834
8892
  this.scheduleIdleStop();
@@ -8878,11 +8936,11 @@ var OpenCodeServeProcess = class {
8878
8936
  }
8879
8937
  async start() {
8880
8938
  await mkdir4(this.cwd, { recursive: true });
8881
- if (this.disposed) throw new ServeStartError("la app se esta apagando");
8939
+ if (this.disposed) throw new ServeStartError("the app is shutting down");
8882
8940
  const env = this.environment();
8883
8941
  const username = env["OPENCODE_SERVER_USERNAME"] || OPENCODE_SERVE_DEFAULT_USERNAME;
8884
8942
  const args = serveArgs(this.options.location);
8885
- debugLog("opencode-serve", "lanzando el serve");
8943
+ debugLog("opencode-serve", "launching serve");
8886
8944
  let child;
8887
8945
  try {
8888
8946
  child = this.spawn(this.options.location.file, args, {
@@ -8929,7 +8987,7 @@ var OpenCodeServeProcess = class {
8929
8987
  const timer = this.timers.setTimeout(
8930
8988
  () => {
8931
8989
  const ms = this.startTimeoutMs;
8932
- fail(`no dijo en que puerto escucha en ${ms >= 1e3 ? `${Math.round(ms / 1e3)} s` : `${ms} ms`}`);
8990
+ fail(`didn't say which port it listens on within ${ms >= 1e3 ? `${Math.round(ms / 1e3)} s` : `${ms} ms`}`);
8933
8991
  },
8934
8992
  this.startTimeoutMs
8935
8993
  );
@@ -8940,13 +8998,13 @@ var OpenCodeServeProcess = class {
8940
8998
  settled = true;
8941
8999
  this.timers.clearTimeout(timer);
8942
9000
  running.endpoint = { url, username, password: this.password };
8943
- debugLog("opencode-serve", `escuchando en ${url}`);
9001
+ debugLog("opencode-serve", `listening on ${url}`);
8944
9002
  this.scheduleIdleStop();
8945
9003
  resolve(running.endpoint);
8946
9004
  return;
8947
9005
  }
8948
9006
  const other = matchListeningLine(line);
8949
- if (other !== null) fail(`escucha en ${other.scheme}://${other.host} y no en http://${OPENCODE_SERVE_HOST}`);
9007
+ if (other !== null) fail(`listens on ${other.scheme}://${other.host} and not on http://${OPENCODE_SERVE_HOST}`);
8950
9008
  };
8951
9009
  const read = (stream) => {
8952
9010
  if (stream === null) return;
@@ -8964,16 +9022,16 @@ var OpenCodeServeProcess = class {
8964
9022
  child.once("error", (error) => fail(error.message));
8965
9023
  child.once("close", (code, signal) => {
8966
9024
  if (this.running === running) this.running = null;
8967
- const how = signal ?? `codigo ${code ?? "?"}`;
9025
+ const how = signal ?? `code ${code ?? "?"}`;
8968
9026
  if (!settled) {
8969
9027
  const tail = running.tail.trim();
8970
- fail(`salio antes de escuchar (${how})${tail.length > 0 ? `: ${tail}` : ""}`);
9028
+ fail(`exited before listening (${how})${tail.length > 0 ? `: ${tail}` : ""}`);
8971
9029
  return;
8972
9030
  }
8973
9031
  const endpoint = running.endpoint;
8974
9032
  if (endpoint === null) return;
8975
9033
  const exit = { requested: running.stopRequested };
8976
- debugLog("opencode-serve", `termino (${how}${exit.requested ? "" : ", sin que la app lo pidiera"})`);
9034
+ debugLog("opencode-serve", `exited (${how}${exit.requested ? "" : ", without the app asking"})`);
8977
9035
  if (this.running === null) this.cancelIdleStop();
8978
9036
  for (const listener of [...this.exitListeners]) {
8979
9037
  try {
@@ -9004,7 +9062,7 @@ var OpenCodeServeProcess = class {
9004
9062
  this.idleTimer = this.timers.setTimeout(() => {
9005
9063
  this.idleTimer = null;
9006
9064
  if (this.retained > 0 || this.disposed) return;
9007
- debugLog("opencode-serve", "sin pestanas de OpenCode: se apaga");
9065
+ debugLog("opencode-serve", "no OpenCode tabs: shutting down");
9008
9066
  this.stop();
9009
9067
  }, this.idleStopMs);
9010
9068
  }
@@ -9057,6 +9115,7 @@ var PASTE_START = "\x1B[200~";
9057
9115
  var PASTE_END = "\x1B[201~";
9058
9116
  var SHIFT_TAB = "\x1B[Z";
9059
9117
  var SUBMIT = "\r";
9118
+ var END_KEY = "\x1B[F";
9060
9119
  var INTERRUPT = "\x1B";
9061
9120
  var MAX_SUBMIT_CHARS = 2e5;
9062
9121
  function sanitizeForPaste(text) {
@@ -9115,53 +9174,47 @@ function buildModeKeys(from, to, cycle = PERMISSION_MODE_CYCLE) {
9115
9174
  if (distance === null) return null;
9116
9175
  return Array.from({ length: distance }, () => SHIFT_TAB);
9117
9176
  }
9118
- var MODE_PENDING_CONFIRMATION_MESSAGE = "Hay una confirmacion pendiente en la CLI: cambiar de modo ahora la aprobaria. Contestala en la solapa CLI.";
9119
- function pendingSubmitMessage(label, waitingFor) {
9120
- return waitingFor === "permission prompt" ? `${label} esta esperando que autorices una herramienta: el Enter del mensaje la aprobaria. Contestala en la solapa CLI.` : `${label} esta esperando una respuesta: el Enter del mensaje la contestaria. Contestala en la solapa CLI.`;
9177
+ var MODE_PENDING_CONFIRMATION_TEXT = serverText("modePendingConfirmation");
9178
+ function pendingSubmitText(label, waitingFor) {
9179
+ return waitingFor === "permission prompt" ? serverText("pendingSubmitPermission", { label }) : serverText("pendingSubmitAnswer", { label });
9121
9180
  }
9122
- var ANSWER_NOT_PENDING_MESSAGE = "Esa pregunta ya no esta esperando respuesta.";
9123
- var ANSWER_INVALID_MESSAGE = "La respuesta no corresponde a la pregunta.";
9124
- var ANSWER_NO_FREE_TEXT_MESSAGE = "Esta pregunta no acepta respuesta escrita: elegi una de las opciones.";
9125
- function answerFailureMessage(outcome) {
9181
+ var ANSWER_NOT_PENDING_TEXT = serverText("answerNotPending");
9182
+ var ANSWER_INVALID_TEXT = serverText("answerInvalid");
9183
+ var ANSWER_NO_FREE_TEXT_TEXT = serverText("answerNoFreeText");
9184
+ function answerFailureText(outcome) {
9126
9185
  switch (outcome) {
9127
9186
  case "answered":
9128
9187
  return null;
9129
9188
  case "not-pending":
9130
- return ANSWER_NOT_PENDING_MESSAGE;
9189
+ return ANSWER_NOT_PENDING_TEXT;
9131
9190
  case "invalid":
9132
- return ANSWER_INVALID_MESSAGE;
9191
+ return ANSWER_INVALID_TEXT;
9133
9192
  case "no-free-text":
9134
- return ANSWER_NO_FREE_TEXT_MESSAGE;
9193
+ return ANSWER_NO_FREE_TEXT_TEXT;
9135
9194
  }
9136
9195
  }
9137
- function waitingSubmitMessage(label) {
9138
- return `${label} esta esperando una respuesta: contestala antes de mandar otro mensaje.`;
9196
+ function waitingSubmitText(label) {
9197
+ return serverText("waitingSubmit", { label });
9139
9198
  }
9140
9199
  function submitRefusal(input) {
9141
9200
  if (input.approvesPendingOnCycle && input.waitingFor !== null) {
9142
- return pendingSubmitMessage(input.label, input.waitingFor);
9201
+ return pendingSubmitText(input.label, input.waitingFor);
9143
9202
  }
9144
9203
  if (input.waitingBlocksSubmit === true && input.activity === "waiting") {
9145
- return waitingSubmitMessage(input.label);
9146
- }
9147
- if (input.blind && input.openToolCall) {
9148
- return `${input.label} tiene una herramienta sin resultado: puede estar pidiendo una aprobacion. Contestala en la solapa CLI.`;
9204
+ return waitingSubmitText(input.label);
9149
9205
  }
9206
+ if (input.blind && input.openToolCall) return serverText("openToolSubmit", { label: input.label });
9150
9207
  return null;
9151
9208
  }
9152
9209
  function planModeChange(input) {
9153
9210
  const { cycle, current, target, waitingFor } = input;
9154
9211
  if (current === target) return { kind: "none" };
9155
9212
  if (cycle.approvesPendingOnCycle && waitingFor !== null) {
9156
- return { kind: "refused", reason: "pending-confirmation", message: MODE_PENDING_CONFIRMATION_MESSAGE };
9213
+ return { kind: "refused", reason: "pending-confirmation", text: MODE_PENDING_CONFIRMATION_TEXT };
9157
9214
  }
9158
9215
  const keys = buildModeKeys(current, target, cycle.modes);
9159
9216
  if (keys === null || keys.length === 0) {
9160
- return {
9161
- kind: "refused",
9162
- reason: "unreachable",
9163
- message: `No se puede llegar a ese modo desde el actual. Cambialo con ${cycle.keyLabel} en la solapa CLI.`
9164
- };
9217
+ return { kind: "refused", reason: "unreachable", text: serverText("modeUnreachable", { key: cycle.keyLabel }) };
9165
9218
  }
9166
9219
  return { kind: "keys", keys };
9167
9220
  }
@@ -9183,6 +9236,7 @@ function buildSubmissionWrites(text, imagePaths, shape, options = {}) {
9183
9236
  const send = options.send ?? true;
9184
9237
  const style = shape.imageReference;
9185
9238
  if (imagePaths.length > 0 && style === null) return null;
9239
+ const submit = shape.endBeforeSubmit === true ? `${END_KEY}${SUBMIT}` : SUBMIT;
9186
9240
  if (style !== "bare-path-paste") {
9187
9241
  const payload = joinedPayload(
9188
9242
  text,
@@ -9190,9 +9244,9 @@ function buildSubmissionWrites(text, imagePaths, shape, options = {}) {
9190
9244
  );
9191
9245
  if (payload === null) return null;
9192
9246
  if (shape.enterSeparately === true) {
9193
- return send ? [pasted(payload, shape.pasteMarkers), SUBMIT] : [pasted(payload, shape.pasteMarkers)];
9247
+ return send ? [pasted(payload, shape.pasteMarkers), submit] : [pasted(payload, shape.pasteMarkers)];
9194
9248
  }
9195
- return [`${pasted(payload, shape.pasteMarkers)}${send ? SUBMIT : ""}`];
9249
+ return [`${pasted(payload, shape.pasteMarkers)}${send ? submit : ""}`];
9196
9250
  }
9197
9251
  const pieces = [];
9198
9252
  for (const imagePath of imagePaths) {
@@ -9202,9 +9256,13 @@ function buildSubmissionWrites(text, imagePaths, shape, options = {}) {
9202
9256
  const clean = sanitizeForPaste(text).trim().slice(0, MAX_SUBMIT_CHARS);
9203
9257
  if (clean.length > 0) pieces.push(pasted(clean, shape.pasteMarkers));
9204
9258
  if (pieces.length === 0) return null;
9205
- if (send) pieces.push(SUBMIT);
9259
+ if (send) pieces.push(submit);
9206
9260
  return pieces;
9207
9261
  }
9262
+ function submitStartDelayMs(launchedAt, now, readyAfterLaunchMs) {
9263
+ if (launchedAt === null || readyAfterLaunchMs <= 0) return 0;
9264
+ return Math.min(readyAfterLaunchMs, Math.max(0, launchedAt + readyAfterLaunchMs - now));
9265
+ }
9208
9266
 
9209
9267
  // packages/server/src/agents/opencode/serve-questions.ts
9210
9268
  var OPENCODE_FREE_ANSWER_MAX_CHARS = 2e3;
@@ -9535,7 +9593,7 @@ var OpenCodeServeStatus = class {
9535
9593
  }
9536
9594
  ok = true;
9537
9595
  } catch (error) {
9538
- debugLog("opencode-serve", `la foto del estado fallo: ${error instanceof Error ? error.name : "error"}`);
9596
+ debugLog("opencode-serve", `the status snapshot failed: ${error instanceof Error ? error.name : "error"}`);
9539
9597
  } finally {
9540
9598
  if (this.snapshots.get(directory) === current) this.snapshots.delete(directory);
9541
9599
  }
@@ -9769,11 +9827,11 @@ function createOpenCodeAdapter(options = {}) {
9769
9827
  * existe hasta la primera consulta, asi que se mira el archivo.
9770
9828
  */
9771
9829
  startupHistoryNote(cliAvailable) {
9772
- if (env["OPENCODE_DB"] === ":memory:") return "en memoria (no hay nada que leer)";
9830
+ if (env["OPENCODE_DB"] === ":memory:") return "in memory (nothing to read)";
9773
9831
  if (paths.dbFile === null) return null;
9774
9832
  const present = existsSync2(paths.dbFile);
9775
- if ("unavailable" in sqlite()) return present || cliAvailable ? `no se lee: ${sqliteUnavailableText(OPENCODE_LABEL)}` : null;
9776
- return present ? `${paths.dbFile} (solo lectura)` : null;
9833
+ if ("unavailable" in sqlite()) return present || cliAvailable ? `not read: ${sqliteUnavailableText(OPENCODE_LABEL)}` : null;
9834
+ return present ? `${paths.dbFile} (read-only)` : null;
9777
9835
  },
9778
9836
  // `opencode.json` no se abre (regla 2.1).
9779
9837
  defaults: () => Promise.resolve(null),
@@ -9808,7 +9866,7 @@ var AgentRegistry = class {
9808
9866
  constructor(adapters) {
9809
9867
  for (const adapter of adapters) {
9810
9868
  if (this.agents.has(adapter.id)) {
9811
- throw new Error(`Adaptador registrado dos veces: ${adapter.id}`);
9869
+ throw new Error(`Adapter registered twice: ${adapter.id}`);
9812
9870
  }
9813
9871
  this.agents.set(adapter.id, { adapter, location: null });
9814
9872
  }
@@ -9842,7 +9900,7 @@ var AgentRegistry = class {
9842
9900
  try {
9843
9901
  await adapter.prepare?.();
9844
9902
  } catch (error) {
9845
- console.warn(`[agentes] ${adapter.label} no pudo preparar lo suyo: ${String(error)}`);
9903
+ console.warn(`[agents] ${adapter.label} couldn't prepare its files: ${String(error)}`);
9846
9904
  }
9847
9905
  })
9848
9906
  );
@@ -9887,7 +9945,7 @@ var AgentRegistry = class {
9887
9945
  /** El adaptador aunque su CLI no este instalada: el historial se lee igual. */
9888
9946
  adapter(id) {
9889
9947
  const entry = this.agents.get(id);
9890
- if (entry === void 0) throw new Error(`No hay adaptador para ${id}`);
9948
+ if (entry === void 0) throw new Error(`No adapter for ${id}`);
9891
9949
  return entry.adapter;
9892
9950
  }
9893
9951
  all() {
@@ -9960,7 +10018,7 @@ var AgentRegistry = class {
9960
10018
  const result = adapter.dispose();
9961
10019
  if (result instanceof Promise) pending.push(result);
9962
10020
  } catch (error) {
9963
- console.warn(`[agentes] ${adapter.label} no se pudo soltar: ${String(error)}`);
10021
+ console.warn(`[agents] ${adapter.label} couldn't be released: ${String(error)}`);
9964
10022
  }
9965
10023
  }
9966
10024
  return Promise.allSettled(pending).then(() => void 0);
@@ -10053,8 +10111,8 @@ var ConversationHub = class extends EventEmitter {
10053
10111
  this.startFollowTicker(terminalId, entry);
10054
10112
  await entry.follower.start();
10055
10113
  debugLog(
10056
- "conversacion",
10057
- `sigo ${path28.basename(entry.follower.label)} de ${terminalId.slice(0, 8)}`
10114
+ "conversation",
10115
+ `following ${path28.basename(entry.follower.label)} for ${terminalId.slice(0, 8)}`
10058
10116
  );
10059
10117
  entry = this.entries.get(terminalId) ?? entry;
10060
10118
  }
@@ -10199,8 +10257,8 @@ var ConversationHub = class extends EventEmitter {
10199
10257
  this.watchStatus(terminalId, next, descriptor.sessionId);
10200
10258
  this.startFollowTicker(terminalId, next);
10201
10259
  debugLog(
10202
- "conversacion",
10203
- `${terminalId.slice(0, 8)} cambio de sesion: sigo ${path28.basename(next.follower.label)}`
10260
+ "conversation",
10261
+ `${terminalId.slice(0, 8)} changed session: following ${path28.basename(next.follower.label)}`
10204
10262
  );
10205
10263
  await next.follower.start();
10206
10264
  next.primed = true;
@@ -10291,7 +10349,7 @@ var ConversationHub = class extends EventEmitter {
10291
10349
  entry.refs -= 1;
10292
10350
  if (entry.refs > 0) return;
10293
10351
  this.release(terminalId, entry);
10294
- debugLog("conversacion", `dejo de seguir ${terminalId.slice(0, 8)}`);
10352
+ debugLog("conversation", `stopped following ${terminalId.slice(0, 8)}`);
10295
10353
  }
10296
10354
  /** La pestana se cerro: se suelta sin importar cuantos la miraban. */
10297
10355
  drop(terminalId) {
@@ -10495,7 +10553,7 @@ var ConversationHub = class extends EventEmitter {
10495
10553
  try {
10496
10554
  result = await entry.follower.poll();
10497
10555
  } catch (error) {
10498
- console.warn(`[conversacion] no se pudo leer ${entry.follower.label}:`, error);
10556
+ console.warn(`[conversation] couldn't read ${entry.follower.label}:`, error);
10499
10557
  return;
10500
10558
  }
10501
10559
  this.refreshOpenToolCall(terminalId, entry);
@@ -10652,6 +10710,9 @@ import chokidar from "chokidar";
10652
10710
  import { execFile as execFile3 } from "node:child_process";
10653
10711
  import { readFile as readFile9 } from "node:fs/promises";
10654
10712
  import path29 from "node:path";
10713
+ function gitOutputText(output, command) {
10714
+ return output.length > 0 ? serverText("raw", { text: output }) : serverText("gitCommandFailed", { command });
10715
+ }
10655
10716
  var MAX_CHANGES = 2e3;
10656
10717
  var MAX_DIFF_LINES = 4e3;
10657
10718
  var MAX_DIFF_BYTES = 512 * 1024;
@@ -10662,7 +10723,7 @@ function isMissingBinary(error) {
10662
10723
  }
10663
10724
  var GitMissingError = class extends Error {
10664
10725
  constructor() {
10665
- super("No se encontro git en el PATH.");
10726
+ super("git wasn't found in the PATH.");
10666
10727
  this.name = "GitMissingError";
10667
10728
  }
10668
10729
  };
@@ -10719,13 +10780,13 @@ async function resolveRepo(cwd) {
10719
10780
  return {
10720
10781
  state: "error",
10721
10782
  root: "",
10722
- message: "No se pudo ejecutar git para leer el estado del repositorio."
10783
+ message: serverText("gitRunFailed")
10723
10784
  };
10724
10785
  }
10725
10786
  return {
10726
10787
  state: "error",
10727
10788
  root: "",
10728
- message: stderr.slice(0, 400) || "git rev-parse fallo."
10789
+ message: gitOutputText(stderr.slice(0, 400), "rev-parse")
10729
10790
  };
10730
10791
  }
10731
10792
  async function findRepoRoot(cwd) {
@@ -10881,13 +10942,13 @@ async function readStatus2(cwd) {
10881
10942
  return {
10882
10943
  ...base,
10883
10944
  state: "git-missing",
10884
- message: "No se encontro git en el PATH. El panel de cambios necesita git instalado; el resto de la app funciona igual."
10945
+ message: serverText("gitMissing")
10885
10946
  };
10886
10947
  }
10887
10948
  throw error;
10888
10949
  }
10889
10950
  if (lookup.state === "not-a-repo") {
10890
- return { ...base, message: "Esta carpeta no esta dentro de un repositorio git." };
10951
+ return { ...base, message: serverText("gitNotRepo") };
10891
10952
  }
10892
10953
  if (lookup.state === "error") {
10893
10954
  return { ...base, state: "error", message: lookup.message };
@@ -10902,7 +10963,7 @@ async function readStatus2(cwd) {
10902
10963
  ...base,
10903
10964
  state: "error",
10904
10965
  repoRoot: repoRoot2,
10905
- message: statusResult.stderr.trim().slice(0, 400) || "git status fallo."
10966
+ message: gitOutputText(statusResult.stderr.trim().slice(0, 400), "status")
10906
10967
  };
10907
10968
  }
10908
10969
  const parsed = parsePorcelainV2(statusResult.stdout);
@@ -10990,7 +11051,7 @@ async function readUntrackedAsDiff(absolutePath, relativePath) {
10990
11051
  lines: [],
10991
11052
  truncated: false,
10992
11053
  binary: false,
10993
- message: "No se pudo leer el archivo."
11054
+ message: serverText("fileReadFailed")
10994
11055
  };
10995
11056
  }
10996
11057
  if (looksBinary(content)) {
@@ -11000,7 +11061,7 @@ async function readUntrackedAsDiff(absolutePath, relativePath) {
11000
11061
  lines: [],
11001
11062
  truncated: false,
11002
11063
  binary: true,
11003
- message: "Archivo binario sin seguimiento."
11064
+ message: serverText("diffBinaryUntracked")
11004
11065
  };
11005
11066
  }
11006
11067
  const truncatedByBytes = content.length > MAX_DIFF_BYTES;
@@ -11024,7 +11085,7 @@ async function readUntrackedAsDiff(absolutePath, relativePath) {
11024
11085
  lines,
11025
11086
  truncated,
11026
11087
  binary: false,
11027
- message: lines.length === 0 ? "Archivo nuevo y vacio." : null
11088
+ message: lines.length === 0 ? serverText("diffNewEmpty") : null
11028
11089
  };
11029
11090
  }
11030
11091
  async function readDiff(options) {
@@ -11051,7 +11112,7 @@ async function readDiff(options) {
11051
11112
  lines: [],
11052
11113
  truncated: false,
11053
11114
  binary: false,
11054
- message: result.stderr.trim().slice(0, 300) || "git diff fallo."
11115
+ message: gitOutputText(result.stderr.trim().slice(0, 300), "diff")
11055
11116
  };
11056
11117
  }
11057
11118
  const capped = result.stdout.length > MAX_DIFF_BYTES;
@@ -11062,7 +11123,7 @@ async function readDiff(options) {
11062
11123
  lines: parsed.lines,
11063
11124
  truncated: parsed.truncated || capped,
11064
11125
  binary: parsed.binary,
11065
- message: parsed.lines.length === 0 ? staged ? "No hay cambios preparados para este archivo." : "No hay cambios sin preparar para este archivo." : null
11126
+ message: parsed.lines.length === 0 ? staged ? serverText("diffNoStaged") : serverText("diffNoUnstaged") : null
11066
11127
  };
11067
11128
  }
11068
11129
  async function filterIgnored(repoRoot2, relativePaths) {
@@ -11165,7 +11226,7 @@ var RepoHub = class extends EventEmitter2 {
11165
11226
  if (entry.pollTimer !== null) clearTimeout(entry.pollTimer);
11166
11227
  void entry.watcher?.close();
11167
11228
  this.entries.delete(terminalId);
11168
- debugLog("git", `dejo de mirar el repo de ${terminalId.slice(0, 8)}`);
11229
+ debugLog("git", `stopped watching the repo of ${terminalId.slice(0, 8)}`);
11169
11230
  }
11170
11231
  /**
11171
11232
  * Observa el `.git` del worktree.
@@ -11235,7 +11296,7 @@ var RepoHub = class extends EventEmitter2 {
11235
11296
  entry.status = status;
11236
11297
  if (!options.silent) this.emit("status", terminalId, status);
11237
11298
  } catch (error) {
11238
- console.warn(`[git] no se pudo leer el estado de ${entry.cwd}:`, error);
11299
+ console.warn(`[git] couldn't read the status of ${entry.cwd}:`, error);
11239
11300
  } finally {
11240
11301
  entry.reading = false;
11241
11302
  if (this.entries.get(terminalId) === entry) {
@@ -11268,11 +11329,7 @@ import {
11268
11329
  } from "node:fs/promises";
11269
11330
  import { homedir as homedir9 } from "node:os";
11270
11331
  import path30 from "node:path";
11271
- var MemoryBridgeError = class extends Error {
11272
- constructor(message) {
11273
- super(message);
11274
- this.name = "MemoryBridgeError";
11275
- }
11332
+ var MemoryBridgeError = class extends ServerTextError {
11276
11333
  };
11277
11334
  var MAX_LISTED_NOTES = 500;
11278
11335
  var MAX_NOTE_BYTES = 256 * 1024;
@@ -11324,7 +11381,7 @@ async function installMemory(cwd, options, home = homedir9()) {
11324
11381
  async function importNativeMemory(cwd, home = homedir9()) {
11325
11382
  await assertMemoryDirInside(cwd);
11326
11383
  if (!await isDirectory2(path30.join(cwd, MEMORY_DIR))) {
11327
- throw new MemoryBridgeError("Instal\xE1 el puente primero: falta la carpeta .agents/memory.");
11384
+ throw new MemoryBridgeError(serverText("memoryNotInstalled"));
11328
11385
  }
11329
11386
  const plan = new PlanBuilder(cwd);
11330
11387
  if (await plan.current(INDEX_REL) === null) {
@@ -11337,7 +11394,7 @@ async function importNativeMemory(cwd, home = homedir9()) {
11337
11394
  }
11338
11395
  async function readMemoryNote(cwd, name) {
11339
11396
  if (!isNoteName(name)) {
11340
- throw new InvalidPathError("Ese nombre no es una nota de la memoria.");
11397
+ throw new InvalidPathError(serverText("memoryNoteName"));
11341
11398
  }
11342
11399
  const absolute = await resolveInside(cwd, `${MEMORY_DIR}/${name}`);
11343
11400
  try {
@@ -11381,14 +11438,16 @@ var PlanBuilder = class {
11381
11438
  if (planned !== void 0) return planned.content;
11382
11439
  return readIfFile(await guard(this.cwd, rel), rel);
11383
11440
  }
11384
- put(rel, original, content, action, preview) {
11441
+ put(rel, original, content, action, preview, copiedFrom = null) {
11385
11442
  const planned = this.files.get(rel);
11386
11443
  if (planned !== void 0) {
11387
11444
  planned.content = content;
11388
- planned.preview.push(preview);
11445
+ if (preview.length > 0) planned.preview.push(preview);
11446
+ planned.copiedFrom ??= copiedFrom;
11389
11447
  return;
11390
11448
  }
11391
- this.files.set(rel, { rel, original, content, action, preview: [preview], target: null });
11449
+ const previews = preview.length > 0 ? [preview] : [];
11450
+ this.files.set(rel, { rel, original, content, action, preview: previews, copiedFrom, target: null });
11392
11451
  }
11393
11452
  /** Nombres de nota que habria en la carpeta, en minusculas. */
11394
11453
  async noteNames() {
@@ -11407,7 +11466,8 @@ var PlanBuilder = class {
11407
11466
  return [...this.files.values()].map((file) => ({
11408
11467
  file: file.rel,
11409
11468
  action: file.action,
11410
- preview: file.preview.join("\n")
11469
+ preview: file.preview.join("\n"),
11470
+ copiedFrom: file.copiedFrom
11411
11471
  }));
11412
11472
  }
11413
11473
  /**
@@ -11437,16 +11497,12 @@ var PlanBuilder = class {
11437
11497
  const target = await realpath2(absolute);
11438
11498
  const info = await stat15(target);
11439
11499
  if (info.nlink > 1) {
11440
- throw new MemoryBridgeError(
11441
- `${file.rel} tiene enlaces duros: la app no lo modifica, porque escribirlo cortaria el enlace. Agreg\xE1 el bloque a mano.`
11442
- );
11500
+ throw new MemoryBridgeError(serverText("memoryHardLinks", { file: file.rel }));
11443
11501
  }
11444
11502
  const key = process.platform === "win32" ? target.toLowerCase() : target;
11445
11503
  const other = seen.get(key);
11446
11504
  if (other !== void 0) {
11447
- throw new MemoryBridgeError(
11448
- `${other} y ${file.rel} son el mismo archivo (un enlace). Eleg\xED uno solo.`
11449
- );
11505
+ throw new MemoryBridgeError(serverText("memorySameFile", { other, file: file.rel }));
11450
11506
  }
11451
11507
  seen.set(key, file.rel);
11452
11508
  file.target = target;
@@ -11461,7 +11517,7 @@ var PlanBuilder = class {
11461
11517
  await mkdir5(path30.join(this.cwd, MEMORY_DIR), { recursive: true });
11462
11518
  }
11463
11519
  for (const file of pending) {
11464
- if (file.target === null) throw new Error(`Plan sin validar: ${file.rel}`);
11520
+ if (file.target === null) throw new Error(`Unvalidated plan: ${file.rel}`);
11465
11521
  await writeAtomic(file.target, file.content, file.original === null);
11466
11522
  }
11467
11523
  }
@@ -11469,7 +11525,7 @@ var PlanBuilder = class {
11469
11525
  async function buildInstallPlan(cwd, options, home) {
11470
11526
  await assertMemoryDirInside(cwd);
11471
11527
  if (options.instructionFiles.length === 0) {
11472
- throw new MemoryBridgeError("Eleg\xED al menos un archivo de instrucciones.");
11528
+ throw new MemoryBridgeError(serverText("memoryChooseFile"));
11473
11529
  }
11474
11530
  const { state: git, main: main2 } = await readGit(cwd);
11475
11531
  const plan = new PlanBuilder(cwd);
@@ -11479,7 +11535,7 @@ async function buildInstallPlan(cwd, options, home) {
11479
11535
  const mainIndexPath = source === null || mainFolder === null ? null : await existingInside(source.root, `${source.memoryRel}/${MEMORY_INDEX_FILE}`, "file");
11480
11536
  const mainIndex = mainIndexPath === null ? null : await readIfFile(mainIndexPath, INDEX_REL);
11481
11537
  if (mainIndex !== null && mainIndexPath !== null) {
11482
- plan.put(INDEX_REL, null, mainIndex, "copy", `Copia de ${mainIndexPath}`);
11538
+ plan.put(INDEX_REL, null, mainIndex, "copy", "", mainIndexPath);
11483
11539
  } else {
11484
11540
  plan.put(
11485
11541
  INDEX_REL,
@@ -11498,7 +11554,7 @@ async function buildInstallPlan(cwd, options, home) {
11498
11554
  if (notePath === null) continue;
11499
11555
  const content = await readIfFile(notePath, name);
11500
11556
  if (content === null) continue;
11501
- plan.put(`${MEMORY_DIR}/${name}`, null, content, "copy", `Copia de ${notePath}`);
11557
+ plan.put(`${MEMORY_DIR}/${name}`, null, content, "copy", "", notePath);
11502
11558
  }
11503
11559
  }
11504
11560
  if (options.importNative) await planNativeImport(plan, cwd, home);
@@ -11515,9 +11571,7 @@ async function buildInstallPlan(cwd, options, home) {
11515
11571
  assertUtf8(name, current);
11516
11572
  const scan = scanBlock(current);
11517
11573
  if (scan.brokenBlock) {
11518
- throw new MemoryBridgeError(
11519
- `${name} tiene las marcas del bloque de memoria mal formadas (una sin su pareja, o el bloque repetido). Arreglalo a mano: la app no toca ese archivo mientras tanto.`
11520
- );
11574
+ throw new MemoryBridgeError(serverText("memoryBrokenBlock", { file: name }));
11521
11575
  }
11522
11576
  const eol = dominantEol(current);
11523
11577
  if (!scan.hasBlock) {
@@ -11533,9 +11587,7 @@ async function buildInstallPlan(cwd, options, home) {
11533
11587
  }
11534
11588
  if (options.gitMode === "ignore") {
11535
11589
  if (git.kind === "error") {
11536
- throw new MemoryBridgeError(
11537
- `No se pudo consultar git (${git.message}). Sin eso no se sabe si hay que tocar .gitignore.`
11538
- );
11590
+ throw new MemoryBridgeError(serverText("memoryGitFailed", { detail: git.message }));
11539
11591
  }
11540
11592
  if (git.kind === "repo") {
11541
11593
  const wanted = [];
@@ -11587,7 +11639,7 @@ async function planNativeImport(plan, cwd, home) {
11587
11639
  if (existing !== null && !existing.equals(content)) skipped.push(name);
11588
11640
  continue;
11589
11641
  }
11590
- plan.put(`${MEMORY_DIR}/${name}`, null, content, "copy", `Copia de ${path30.join(folder, name)}`);
11642
+ plan.put(`${MEMORY_DIR}/${name}`, null, content, "copy", "", path30.join(folder, name));
11591
11643
  copied.push(name);
11592
11644
  }
11593
11645
  }
@@ -11697,9 +11749,7 @@ function isUtf8Compatible(content) {
11697
11749
  }
11698
11750
  function assertUtf8(rel, content) {
11699
11751
  if (isUtf8Compatible(content)) return;
11700
- throw new MemoryBridgeError(
11701
- `${rel} no est\xE1 en UTF-8 (parece UTF-16, lo que deja \`>\` en Windows PowerShell 5.1). Guardalo como UTF-8: la app no lo toca mientras tanto.`
11702
- );
11752
+ throw new MemoryBridgeError(serverText("memoryNotUtf8", { file: rel }));
11703
11753
  }
11704
11754
  function isBlockCurrent(content, scan, name) {
11705
11755
  if (!scan.hasBlock) return false;
@@ -11781,14 +11831,14 @@ async function inspectInstructionFile(cwd, name) {
11781
11831
  };
11782
11832
  }
11783
11833
  function problemText(file) {
11784
- if (file.problem === "outside") return `${file.name} apunta fuera del proyecto`;
11785
- if (file.problem === "encoding") return `${file.name} no est\xE1 en UTF-8`;
11834
+ if (file.problem === "outside") return serverText("memoryViaOutside", { file: file.name });
11835
+ if (file.problem === "encoding") return serverText("memoryViaEncoding", { file: file.name });
11786
11836
  return null;
11787
11837
  }
11788
11838
  function computeReach(folderExists, indexExists, files) {
11789
11839
  const agents = files.find((file) => file.name === "AGENTS.md");
11790
11840
  const claude = files.find((file) => file.name === "CLAUDE.md");
11791
- const missing = !folderExists ? "falta la carpeta .agents/memory" : !indexExists ? "falta .agents/memory/MEMORY.md" : null;
11841
+ const missing = !folderExists ? serverText("memoryViaNoFolder") : !indexExists ? serverText("memoryViaNoIndex") : null;
11792
11842
  const entry = (agent, reaches, via) => ({
11793
11843
  agent,
11794
11844
  label: MEMORY_AGENT_LABELS[agent],
@@ -11799,21 +11849,21 @@ function computeReach(folderExists, indexExists, files) {
11799
11849
  const claudeHasBlock = claude?.hasBlock === true;
11800
11850
  const agentsProblem = agents === void 0 ? null : problemText(agents);
11801
11851
  const claudeProblem = claude === void 0 ? null : problemText(claude);
11802
- const viaAgents = agentsHasBlock ? "AGENTS.md" : agentsProblem ?? "falta el bloque en AGENTS.md";
11852
+ const viaAgents = agentsHasBlock ? serverText("memoryViaFile", { file: "AGENTS.md" }) : agentsProblem ?? serverText("memoryViaNoBlock", { file: "AGENTS.md" });
11803
11853
  let openCode;
11804
- if (agentsHasBlock) openCode = { reaches: true, via: "AGENTS.md" };
11854
+ if (agentsHasBlock) openCode = { reaches: true, via: serverText("memoryViaFile", { file: "AGENTS.md" }) };
11805
11855
  else if (agents?.exists !== true && claudeHasBlock) {
11806
- openCode = { reaches: true, via: "CLAUDE.md (respaldo)" };
11856
+ openCode = { reaches: true, via: serverText("memoryViaFallback") };
11807
11857
  } else if (agents?.exists === true) {
11808
11858
  openCode = { reaches: false, via: viaAgents };
11809
11859
  } else {
11810
- openCode = { reaches: false, via: "falta el bloque en AGENTS.md o en CLAUDE.md" };
11860
+ openCode = { reaches: false, via: serverText("memoryViaNoBlockEither") };
11811
11861
  }
11812
11862
  return [
11813
11863
  entry(
11814
11864
  "claude-code",
11815
11865
  claudeHasBlock,
11816
- claudeHasBlock ? "CLAUDE.md" : claudeProblem ?? "falta el bloque en CLAUDE.md"
11866
+ claudeHasBlock ? serverText("memoryViaFile", { file: "CLAUDE.md" }) : claudeProblem ?? serverText("memoryViaNoBlock", { file: "CLAUDE.md" })
11817
11867
  ),
11818
11868
  entry("codex", agentsHasBlock, viaAgents),
11819
11869
  entry("antigravity", agentsHasBlock, viaAgents),
@@ -11903,7 +11953,7 @@ function buildGlobalFragments(home) {
11903
11953
  const windows = process.platform === "win32";
11904
11954
  const globalFile = path30.join(home, ...GLOBAL_MEMORY_RELATIVE.split("/"));
11905
11955
  const hardLink = (link) => windows ? `mklink /H "${link}" "${globalFile}"` : `ln "${globalFile}" "${link}"`;
11906
- const hardLinkNote = `Enlace duro${windows ? ", en cmd" : ""}. \`~/${GLOBAL_MEMORY_RELATIVE}\` tiene que existir antes, y el archivo de la CLI no: si ya existe, pas\xE1 lo que tenga a \`global.md\` y borralo. Si tu editor guarda reemplazando el archivo, el enlace se corta: en ese caso copi\xE1 el contenido.`;
11956
+ const hardLinkNote = windows ? serverText("memoryNoteHardLinkCmd", { path: GLOBAL_MEMORY_RELATIVE }) : serverText("memoryNoteHardLink", { path: GLOBAL_MEMORY_RELATIVE });
11907
11957
  const codexTarget = path30.join(home, ".codex", "AGENTS.md");
11908
11958
  const geminiTarget = path30.join(home, ".gemini", "GEMINI.md");
11909
11959
  const homeSlashes = home.replace(/\\/g, "/").replace(/\/+$/, "");
@@ -11914,7 +11964,7 @@ function buildGlobalFragments(home) {
11914
11964
  target: path30.join(home, ".claude", "CLAUDE.md"),
11915
11965
  kind: "line",
11916
11966
  text: `@~/${GLOBAL_MEMORY_RELATIVE}`,
11917
- note: "Agreg\xE1 esta l\xEDnea al final. Claude Code importa el archivo al arrancar."
11967
+ note: serverText("memoryNoteClaude")
11918
11968
  },
11919
11969
  {
11920
11970
  agent: "codex",
@@ -11938,7 +11988,7 @@ function buildGlobalFragments(home) {
11938
11988
  target: path30.join(home, ".config", "opencode", "opencode.json"),
11939
11989
  kind: "json",
11940
11990
  text: `"instructions": ["${homeSlashes}/${GLOBAL_MEMORY_RELATIVE}"]`,
11941
- note: "Dentro del objeto ra\xEDz. Ruta absoluta: OpenCode no expande `~` en esta clave (sin verificar)."
11991
+ note: serverText("memoryNoteOpenCode")
11942
11992
  }
11943
11993
  ];
11944
11994
  }
@@ -11960,7 +12010,7 @@ function runGit2(cwd, args) {
11960
12010
  }
11961
12011
  resolve({
11962
12012
  kind: "spawn-failed",
11963
- message: code === "ENOENT" ? "No se encontr\xF3 git en el PATH." : error.message
12013
+ text: code === "ENOENT" ? serverText("gitNotFound") : serverText("raw", { text: error.message })
11964
12014
  });
11965
12015
  }
11966
12016
  );
@@ -11969,18 +12019,20 @@ function runGit2(cwd, args) {
11969
12019
  async function readGit(cwd) {
11970
12020
  const fail = (message) => ({ state: { kind: "error", message }, main: null });
11971
12021
  const result = await runGit2(cwd, ["rev-parse", "--show-toplevel", "--git-dir", "--git-common-dir"]);
11972
- if (result.kind === "spawn-failed") return fail(result.message);
12022
+ if (result.kind === "spawn-failed") return fail(result.text);
11973
12023
  if (result.code !== 0) {
11974
12024
  const stderr = result.stderr.trim();
11975
12025
  if (/not a git repository|no es un repositorio/i.test(stderr)) {
11976
12026
  return { state: { kind: "not-repo" }, main: null };
11977
12027
  }
11978
- return fail(stderr.slice(0, 400) || "git rev-parse fall\xF3.");
12028
+ return fail(
12029
+ stderr.length > 0 ? serverText("raw", { text: stderr.slice(0, 400) }) : serverText("gitCommandFailed", { command: "rev-parse" })
12030
+ );
11979
12031
  }
11980
12032
  const [toplevel, gitDir, commonDir] = result.stdout.split(/\r?\n/).map((line) => line.trim());
11981
- if (!toplevel || !gitDir || !commonDir) return fail("Respuesta inesperada de git rev-parse.");
12033
+ if (!toplevel || !gitDir || !commonDir) return fail(serverText("gitUnexpected", { command: "rev-parse" }));
11982
12034
  const ignored = await checkIgnored(cwd, INDEX_REL);
11983
- if (typeof ignored === "string") return fail(ignored);
12035
+ if (typeof ignored !== "boolean") return fail(ignored);
11984
12036
  const absoluteGitDir = path30.resolve(cwd, gitDir);
11985
12037
  const absoluteCommonDir = path30.resolve(cwd, commonDir);
11986
12038
  let worktree = null;
@@ -12005,10 +12057,11 @@ async function readGit(cwd) {
12005
12057
  }
12006
12058
  async function checkIgnored(cwd, rel) {
12007
12059
  const result = await runGit2(cwd, ["check-ignore", "-q", "--", rel]);
12008
- if (result.kind === "spawn-failed") return result.message;
12060
+ if (result.kind === "spawn-failed") return result.text;
12009
12061
  if (result.code === 0) return true;
12010
12062
  if (result.code === 1) return false;
12011
- return result.stderr.trim().slice(0, 400) || "git check-ignore fall\xF3.";
12063
+ const stderr = result.stderr.trim().slice(0, 400);
12064
+ return stderr.length > 0 ? serverText("raw", { text: stderr }) : serverText("gitCommandFailed", { command: "check-ignore" });
12012
12065
  }
12013
12066
  async function samePath(a, b) {
12014
12067
  const resolve = async (value) => {
@@ -12025,10 +12078,8 @@ async function guard(cwd, rel) {
12025
12078
  try {
12026
12079
  return await resolveInside(cwd, rel);
12027
12080
  } catch (error) {
12028
- if (error instanceof InvalidPathError && !/ya no existe/.test(error.message)) {
12029
- throw new InvalidPathError(
12030
- `${rel} apunta fuera del proyecto (un enlace o una junction): la app no escribe ah\xED.`
12031
- );
12081
+ if (error instanceof InvalidPathError && error.text.key !== "tabDirGone") {
12082
+ throw new InvalidPathError(serverText("memoryLinkOutside", { path: rel }));
12032
12083
  }
12033
12084
  throw error;
12034
12085
  }
@@ -12060,7 +12111,7 @@ async function listNoteFiles(folder) {
12060
12111
  async function readIfFile(absolute, label) {
12061
12112
  try {
12062
12113
  const info = await stat15(absolute);
12063
- if (!info.isFile()) throw new MemoryBridgeError(`${label} existe pero no es un archivo.`);
12114
+ if (!info.isFile()) throw new MemoryBridgeError(serverText("memoryNotAFile", { label }));
12064
12115
  return await readFile10(absolute);
12065
12116
  } catch (error) {
12066
12117
  if (isNotFound(error)) return null;
@@ -12108,9 +12159,7 @@ async function writeAtomic(target, content, mustNotExist) {
12108
12159
  await handle.close();
12109
12160
  }
12110
12161
  if (mustNotExist && await exists2(target)) {
12111
- throw new MemoryBridgeError(
12112
- `${path30.basename(target)} apareci\xF3 mientras se instalaba. Volv\xE9 a mirar los cambios.`
12113
- );
12162
+ throw new MemoryBridgeError(serverText("memoryAppeared", { file: path30.basename(target) }));
12114
12163
  }
12115
12164
  await renameWithRetry(temporary, target);
12116
12165
  } catch (error) {
@@ -12159,7 +12208,7 @@ async function renameWithRetry(from, to) {
12159
12208
  var WATCH_DEBOUNCE_MS2 = 300;
12160
12209
  var UnknownTerminalError = class extends Error {
12161
12210
  constructor() {
12162
- super("La terminal ya no existe.");
12211
+ super("The terminal no longer exists.");
12163
12212
  this.name = "UnknownTerminalError";
12164
12213
  }
12165
12214
  };
@@ -12293,7 +12342,7 @@ var MemoryHub = class extends EventEmitter3 {
12293
12342
  async dispose(entry) {
12294
12343
  if (entry.debounceTimer !== null) clearTimeout(entry.debounceTimer);
12295
12344
  this.watches.delete(entry.key);
12296
- debugLog("memory", `dejo de mirar la memoria de ${entry.cwd}`);
12345
+ debugLog("memory", `stopped watching the memory of ${entry.cwd}`);
12297
12346
  await entry.watcher?.close().catch(() => void 0);
12298
12347
  }
12299
12348
  /**
@@ -12334,14 +12383,14 @@ var MemoryHub = class extends EventEmitter3 {
12334
12383
  entry.lastJson = json;
12335
12384
  for (const terminalId of entry.subscribers.keys()) this.emit("status", terminalId, status);
12336
12385
  } catch (error) {
12337
- console.warn(`[memoria] no se pudo leer el estado de ${entry.cwd}:`, error);
12386
+ console.warn(`[memory] couldn't read the status of ${entry.cwd}:`, error);
12338
12387
  }
12339
12388
  }
12340
12389
  async inspectQuietly(cwd) {
12341
12390
  try {
12342
12391
  return await inspectMemory(cwd, this.home);
12343
12392
  } catch (error) {
12344
- console.warn(`[memoria] no se pudo releer el estado de ${cwd}:`, error);
12393
+ console.warn(`[memory] couldn't reread the status of ${cwd}:`, error);
12345
12394
  return null;
12346
12395
  }
12347
12396
  }
@@ -12591,8 +12640,8 @@ var HEADER_TITLE_CHARS = 200;
12591
12640
  var HEADER_CWD_CHARS = 1e3;
12592
12641
  var QUESTION_LABEL_CHARS = 200;
12593
12642
  var TEXT_CHAR_STEPS = [4e3, 2e3, 1e3, 500, 250];
12594
- var HANDOFF_EMPTY_MESSAGE = "Esa conversaci\xF3n no tiene mensajes que continuar.";
12595
- var HANDOFF_NO_TRANSCRIPT_MESSAGE = "Esa conversaci\xF3n no tiene mensajes que continuar: esa CLI no dej\xF3 transcript legible.";
12643
+ var HANDOFF_EMPTY_TEXT = serverText("handoffEmpty");
12644
+ var HANDOFF_NO_TRANSCRIPT_TEXT = serverText("handoffNoTranscript");
12596
12645
  function opensTurn(event) {
12597
12646
  return event.role === "user" && event.parts.some((part) => part.kind === "text" && part.text.trim().length > 0);
12598
12647
  }
@@ -12649,36 +12698,46 @@ function renderText(text, truncated, maxChars) {
12649
12698
  const clean = sanitizeForPaste(text).replace(/\s+$/u, "");
12650
12699
  if (clean.trim().length === 0) return null;
12651
12700
  const { text: kept, cut: cut2 } = cutChars(clean, maxChars);
12652
- return cut2 || truncated ? `${kept}\u2026 (recortado)` : kept;
12701
+ return cut2 || truncated ? `${kept}\u2026 (truncated)` : kept;
12702
+ }
12703
+ var imagesText = (count) => count === 1 ? "[image not included]" : `[${count} images not included]`;
12704
+ function noticeLine(part) {
12705
+ switch (part.notice) {
12706
+ case "compacted":
12707
+ return part.detail === "auto" ? "Context compacted automatically" : "Context compacted";
12708
+ case "interrupted":
12709
+ return "Interrupted";
12710
+ case "error":
12711
+ return part.detail.length > 0 ? `The CLI returned an error: ${part.detail}` : "The CLI returned an error";
12712
+ }
12653
12713
  }
12654
- var imagesText = (count) => count === 1 ? "[imagen no incluida]" : `[${count} im\xE1genes no incluidas]`;
12655
12714
  function renderResult(result) {
12656
12715
  const { text, cut: cut2 } = cutChars(oneLine2(result.text), HANDOFF_TOOL_RESULT_CHARS);
12657
12716
  const marks = [];
12658
12717
  if (result.isError) marks.push("error");
12659
- if (cut2 || result.truncated) marks.push("recortado");
12718
+ if (cut2 || result.truncated) marks.push("truncated");
12660
12719
  let body = cut2 ? `${text}\u2026` : text;
12661
12720
  if (result.imageCount > 0) body = body.length > 0 ? `${body} ${imagesText(result.imageCount)}` : imagesText(result.imageCount);
12662
- if (body.length === 0) body = "sin texto";
12721
+ if (body.length === 0) body = "no text";
12663
12722
  return marks.length > 0 ? `${marks.join(", ")}: ${body}` : body;
12664
12723
  }
12665
12724
  function renderToolLine(part, result) {
12666
12725
  const name = cutChars(oneLine2(part.name), 80).text;
12667
12726
  const { text: input, cut: cut2 } = cutChars(oneLine2(part.input), HANDOFF_TOOL_INPUT_CHARS);
12668
- const head = `- ${inlineCode2(name.length > 0 ? name : "herramienta")}`;
12727
+ const head = `- ${inlineCode2(name.length > 0 ? name : "tool")}`;
12669
12728
  const withInput = input.length > 0 ? `${head}: ${inlineCode2(cut2 || part.truncated ? `${input}\u2026` : input)}` : head;
12670
- return `${withInput} \u2192 ${result !== void 0 ? renderResult(result) : "sin resultado"}`;
12729
+ return `${withInput} \u2192 ${result !== void 0 ? renderResult(result) : "no result"}`;
12671
12730
  }
12672
12731
  function renderQuestionLine(part, result) {
12673
12732
  const labels = part.questions.map((item) => {
12674
12733
  const label = oneLine2(item.question).length > 0 ? oneLine2(item.question) : oneLine2(item.header);
12675
12734
  return `"${cutChars(label, QUESTION_LABEL_CHARS).text}"`;
12676
12735
  }).join(", ");
12677
- const title = part.questions.length === 1 ? `- Pregunta ${labels}` : `- Preguntas ${labels}`;
12678
- if (result === void 0) return `${title}: sin responder`;
12736
+ const title = part.questions.length === 1 ? `- Question ${labels}` : `- Questions ${labels}`;
12737
+ if (result === void 0) return `${title}: unanswered`;
12679
12738
  const { text, cut: cut2 } = cutChars(oneLine2(result.text), HANDOFF_TOOL_RESULT_CHARS);
12680
12739
  const answer = `"${cut2 ? `${text}\u2026` : text}"`;
12681
- return `${title}: ${result.isError ? "no respondida" : "respondida"} ${answer}`;
12740
+ return `${title}: ${result.isError ? "not answered" : "answered"} ${answer}`;
12682
12741
  }
12683
12742
  function renderRequestBody(event, textChars) {
12684
12743
  const blocks = [];
@@ -12694,7 +12753,7 @@ function renderRequestBody(event, textChars) {
12694
12753
  }
12695
12754
  function renderTurn(turn, textChars) {
12696
12755
  const [request, ...rest] = turn.events;
12697
- if (request === void 0) return `## Turno ${turn.number} \xB7 usuario`;
12756
+ if (request === void 0) return `## Turn ${turn.number} \xB7 user`;
12698
12757
  const results = /* @__PURE__ */ new Map();
12699
12758
  const calls = /* @__PURE__ */ new Set();
12700
12759
  for (const event of turn.events) {
@@ -12703,7 +12762,7 @@ function renderTurn(turn, textChars) {
12703
12762
  if (part.kind === "tool-call" || part.kind === "question") calls.add(part.toolUseId);
12704
12763
  }
12705
12764
  }
12706
- const heading = `## Turno ${turn.number} \xB7 usuario${request.queued ? " (enviado mientras trabajaba)" : ""}`;
12765
+ const heading = `## Turn ${turn.number} \xB7 user${request.queued ? " (sent while the agent was working)" : ""}`;
12707
12766
  const userBlocks = [heading, ...renderRequestBody(request, textChars)];
12708
12767
  const answer = [];
12709
12768
  let listOpen = false;
@@ -12731,13 +12790,13 @@ ${line}`;
12731
12790
  pushLine(renderQuestionLine(part, results.get(part.toolUseId)));
12732
12791
  return;
12733
12792
  case "tool-result":
12734
- if (!calls.has(part.toolUseId)) pushLine(`- Resultado de una herramienta anterior \u2192 ${renderResult(part)}`);
12793
+ if (!calls.has(part.toolUseId)) pushLine(`- Result of an earlier tool call \u2192 ${renderResult(part)}`);
12735
12794
  return;
12736
12795
  case "image":
12737
- pushLine(event.role === "user" ? `- ${imagesText(1)} (del usuario)` : `- ${imagesText(1)}`);
12796
+ pushLine(event.role === "user" ? `- ${imagesText(1)} (from the user)` : `- ${imagesText(1)}`);
12738
12797
  return;
12739
12798
  case "notice":
12740
- pushLine(`- Aviso: ${oneLine2(noticeText(part))}`);
12799
+ pushLine(`- Notice: ${oneLine2(noticeLine(part))}`);
12741
12800
  return;
12742
12801
  case "thinking":
12743
12802
  return;
@@ -12750,24 +12809,25 @@ ${line}`;
12750
12809
  for (const part of event.parts) renderAnswerPart(event, part);
12751
12810
  }
12752
12811
  const blocks = [userBlocks.join("\n\n")];
12753
- if (answer.length > 0) blocks.push([`## Turno ${turn.number} \xB7 asistente`, ...answer].join("\n\n"));
12812
+ if (answer.length > 0) blocks.push([`## Turn ${turn.number} \xB7 assistant`, ...answer].join("\n\n"));
12754
12813
  return blocks.join("\n\n");
12755
12814
  }
12756
12815
  function renderOpening(turn, textChars) {
12757
12816
  const request = turn.events[0];
12758
12817
  const body = request === void 0 ? [] : renderRequestBody(request, textChars);
12759
- return ["## Pedido inicial", ...body].join("\n\n");
12818
+ return ["## Initial request", ...body].join("\n\n");
12760
12819
  }
12761
12820
  function coverageSentence(included, total, complete) {
12762
12821
  let what;
12822
+ const last = included === 1 ? "the last one" : `the last ${included}`;
12763
12823
  if (!complete) {
12764
- what = `Incluye ${included === 1 ? "el \xFAltimo" : `los \xFAltimos ${included}`} de m\xE1s de ${total} turnos, numerados desde el m\xE1s viejo que se ley\xF3.`;
12824
+ what = `Includes ${last} of more than ${total} turns, numbered from the oldest one that was read.`;
12765
12825
  } else if (included >= total) {
12766
- what = total === 1 ? "Incluye el \xFAnico turno." : `Incluye los ${total} turnos.`;
12826
+ what = total === 1 ? "Includes the only turn." : `Includes all ${total} turns.`;
12767
12827
  } else {
12768
- what = `Incluye ${included === 1 ? "el \xFAltimo" : `los \xFAltimos ${included}`} de ${total} turnos.`;
12828
+ what = `Includes ${last} of ${total} turns.`;
12769
12829
  }
12770
- return `${what} Los resultados de herramientas est\xE1n recortados y las im\xE1genes no se incluyen.`;
12830
+ return `${what} Tool results are truncated and images are not included.`;
12771
12831
  }
12772
12832
  function renderHeader(header, included, selection) {
12773
12833
  const label = cutChars(oneLine2(header.sourceLabel), 80).text;
@@ -12775,14 +12835,15 @@ function renderHeader(header, included, selection) {
12775
12835
  const cwd = cutChars(sanitizeForPaste(header.cwd).replace(/[\n\t]/g, " ").trim(), HEADER_CWD_CHARS).text;
12776
12836
  const agent = cutChars(oneLine2(header.agent), 80).text;
12777
12837
  const sessionId = cutChars(oneLine2(header.sessionId), 200).text;
12838
+ const named = title.length > 0 && title !== UNTITLED_SESSION_TITLE ? title : "untitled";
12778
12839
  const facts = [
12779
- `- Proyecto: ${cwd.trim().length > 0 ? inlineCode2(cwd) : "desconocido"}`,
12780
- `- Conversaci\xF3n: ${title.length > 0 ? title : UNTITLED_SESSION_TITLE} (${agent}, ${sessionId})`
12840
+ `- Project: ${cwd.trim().length > 0 ? inlineCode2(cwd) : "unknown"}`,
12841
+ `- Conversation: ${named} (${agent}, ${sessionId})`
12781
12842
  ];
12782
- if (header.lastAt !== null && header.lastAt > 0) facts.push(`- \xDAltima respuesta: ${formatStamp(header.lastAt)}`);
12843
+ if (header.lastAt !== null && header.lastAt > 0) facts.push(`- Last reply: ${formatStamp(header.lastAt)}`);
12783
12844
  facts.push(`- ${coverageSentence(included, selection.totalTurns, selection.complete)}`);
12784
- if (header.partial) facts.push("- Historial parcial: la conversaci\xF3n original no se pudo leer entera.");
12785
- return `# Continuaci\xF3n de una conversaci\xF3n con ${label}
12845
+ if (header.partial) facts.push("- Partial history: the original conversation couldn't be read in full.");
12846
+ return `# Continuation of a conversation with ${label}
12786
12847
 
12787
12848
  ${facts.join("\n")}`;
12788
12849
  }
@@ -12808,7 +12869,7 @@ function elide(block, maxBytes, maxLines) {
12808
12869
  const lines = block.split("\n");
12809
12870
  const heading = lines[0] ?? "";
12810
12871
  const body = lines.slice(1);
12811
- const marker = (omitted2) => `[\u2026 ${omitted2} l\xEDneas omitidas para que el transcript entre en su tope \u2026]`;
12872
+ const marker = (omitted2) => `[\u2026 ${omitted2} lines omitted to keep the transcript within its limit \u2026]`;
12812
12873
  const markerBytes = Buffer.byteLength(marker(body.length), "utf8") + 1;
12813
12874
  const available = Math.max(0, maxBytes - Buffer.byteLength(heading, "utf8") - 1 - markerBytes);
12814
12875
  const availableLines = Math.max(0, maxLines - 3);
@@ -12880,13 +12941,13 @@ function quote(text) {
12880
12941
  function buildContinuationMessage(input) {
12881
12942
  const label = oneLine2(input.sourceLabel);
12882
12943
  const lines = [
12883
- `Esto contin\xFAa una conversaci\xF3n que empez\xF3 con otro asistente (${label}).`,
12884
- input.includedTurns === 1 ? `El transcript del \xFAltimo turno est\xE1 en ${input.reference}.` : `El transcript de los \xFAltimos ${input.includedTurns} turnos est\xE1 en ${input.reference}.`
12944
+ `This continues a conversation that started with another assistant (${label}).`,
12945
+ input.includedTurns === 1 ? `The transcript of the last turn is in ${input.reference}.` : `The transcript of the last ${input.includedTurns} turns is in ${input.reference}.`
12885
12946
  ];
12886
12947
  if (input.lastRequest !== null && sanitizeForPaste(input.lastRequest).trim().length > 0) {
12887
- lines.push("Leelo entero y segu\xED desde el \xFAltimo pedido, que fue:", "", quote(input.lastRequest));
12948
+ lines.push("Read all of it and continue from the last request, which was:", "", quote(input.lastRequest));
12888
12949
  } else {
12889
- lines.push("Leelo entero y segu\xED desde donde qued\xF3.");
12950
+ lines.push("Read all of it and continue from where it left off.");
12890
12951
  }
12891
12952
  return sanitizeForPaste(lines.join("\n"));
12892
12953
  }
@@ -12898,7 +12959,7 @@ function planTranscript(input) {
12898
12959
  if (turns.length === 0) {
12899
12960
  return {
12900
12961
  ok: false,
12901
- message: input.state === "no-transcript" ? HANDOFF_NO_TRANSCRIPT_MESSAGE : HANDOFF_EMPTY_MESSAGE
12962
+ text: input.state === "no-transcript" ? HANDOFF_NO_TRANSCRIPT_TEXT : HANDOFF_EMPTY_TEXT
12902
12963
  };
12903
12964
  }
12904
12965
  const complete = input.complete ?? true;
@@ -12919,6 +12980,102 @@ function planTranscript(input) {
12919
12980
  };
12920
12981
  }
12921
12982
 
12983
+ // packages/server/src/attachments.ts
12984
+ var MAX_NAME_CHARS = 60;
12985
+ var MAX_DISPLAY_CHARS = 100;
12986
+ var BLOCKED_EXTENSIONS = /* @__PURE__ */ new Set([
12987
+ "exe",
12988
+ "dll",
12989
+ "msi",
12990
+ "msp",
12991
+ "scr",
12992
+ "com",
12993
+ "pif",
12994
+ "cpl",
12995
+ "sys",
12996
+ "lnk",
12997
+ "hta",
12998
+ "jar",
12999
+ "app",
13000
+ "dmg"
13001
+ ]);
13002
+ var AT_ATTACHABLE_EXTENSIONS = /* @__PURE__ */ new Set([
13003
+ "txt",
13004
+ "log",
13005
+ "md",
13006
+ "markdown",
13007
+ "json",
13008
+ "jsonl",
13009
+ "csv",
13010
+ "tsv",
13011
+ "xml",
13012
+ "html",
13013
+ "htm",
13014
+ "css",
13015
+ "yaml",
13016
+ "yml",
13017
+ "ini",
13018
+ "conf",
13019
+ "cfg",
13020
+ "toml",
13021
+ "sql",
13022
+ "ps1",
13023
+ "sh",
13024
+ "bat",
13025
+ "cmd",
13026
+ "py",
13027
+ "js",
13028
+ "mjs",
13029
+ "cjs",
13030
+ "ts",
13031
+ "tsx",
13032
+ "jsx",
13033
+ "cs",
13034
+ "java",
13035
+ "go",
13036
+ "rs",
13037
+ "rb",
13038
+ "php",
13039
+ "c",
13040
+ "h",
13041
+ "cpp",
13042
+ "pdf"
13043
+ ]);
13044
+ function extensionOf(name) {
13045
+ const match = /\.([A-Za-z0-9]{1,10})$/.exec(name);
13046
+ return match?.[1]?.toLowerCase() ?? "";
13047
+ }
13048
+ function isBlockedExtension(name) {
13049
+ return BLOCKED_EXTENSIONS.has(extensionOf(name));
13050
+ }
13051
+ function safeFileName(hint) {
13052
+ const last = hint.split(/[\\/]/).pop() ?? "";
13053
+ const clean = last.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/[^A-Za-z0-9._-]+/g, "_").replace(/_{2,}/g, "_").replace(/\.{2,}/g, ".").replace(/^[._-]+/, "").replace(/[._-]+$/, "");
13054
+ if (clean.length === 0) return "archivo";
13055
+ if (clean.length <= MAX_NAME_CHARS) return clean;
13056
+ const extension = extensionOf(clean);
13057
+ if (extension.length === 0) return clean.slice(0, MAX_NAME_CHARS);
13058
+ const stem = clean.slice(0, clean.length - extension.length - 1);
13059
+ return `${stem.slice(0, MAX_NAME_CHARS - extension.length - 1)}.${extension}`;
13060
+ }
13061
+ function attachmentReference(filePath, style) {
13062
+ const effective = style === "at-quoted" && AT_ATTACHABLE_EXTENSIONS.has(extensionOf(filePath)) ? "at-quoted" : "quoted-path";
13063
+ return transcriptReferenceFor(filePath, effective);
13064
+ }
13065
+ function formatAttachmentBytes(bytes) {
13066
+ if (bytes < 1024) return `${bytes} B`;
13067
+ if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
13068
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
13069
+ }
13070
+ function attachmentLine(originalName, bytes, reference) {
13071
+ const display = Array.from(originalName).map((char) => (char.codePointAt(0) ?? 0) < 32 || char === "\x7F" || char === '"' ? " " : char).join("").replace(/\s+/g, " ").trim().slice(0, MAX_DISPLAY_CHARS);
13072
+ const label = display.length > 0 ? display : "unnamed";
13073
+ return `Attached file (${label}, ${formatAttachmentBytes(bytes)}): ${reference}`;
13074
+ }
13075
+ function textWithAttachments(text, lines) {
13076
+ return lines.length === 0 ? text : [...lines, text].filter((piece) => piece.trim().length > 0).join("\n");
13077
+ }
13078
+
12922
13079
  // packages/server/src/image-signature.ts
12923
13080
  var FORMATS = [
12924
13081
  {
@@ -12954,10 +13111,14 @@ var EXTENSIONS = /* @__PURE__ */ new Map([
12954
13111
  ]);
12955
13112
  var MAX_IMAGE_BYTES3 = MAX_SUBMIT_IMAGE_BYTES;
12956
13113
  var MAX_IMAGES_PER_SUBMIT = MAX_SUBMIT_IMAGES;
13114
+ var MAX_FILE_BYTES = MAX_SUBMIT_FILE_BYTES;
13115
+ var MAX_FILES_PER_SUBMIT = MAX_SUBMIT_FILES;
12957
13116
  var STALE_AFTER_MS = 24 * 60 * 60 * 1e3;
12958
13117
  var DIRECTORY_MODE = 448;
12959
13118
  var FILE_MODE = 384;
12960
- var PasteImageError = class extends Error {
13119
+ var PasteImageError = class extends ServerTextError {
13120
+ };
13121
+ var PasteFileError = class extends ServerTextError {
12961
13122
  };
12962
13123
  var MAX_TEXT_BYTES = HANDOFF_MAX_BYTES + 4 * 1024;
12963
13124
  var PasteTextError = class extends Error {
@@ -12977,18 +13138,21 @@ var PasteStore = class {
12977
13138
  async save(terminalId, mediaType, base64) {
12978
13139
  const declared = EXTENSIONS.get(mediaType);
12979
13140
  if (declared === void 0) {
12980
- throw new PasteImageError(`Tipo de imagen no admitido: ${mediaType}`);
13141
+ throw new PasteImageError(serverText("imageTypeUnsupported", { type: mediaType }));
12981
13142
  }
12982
13143
  const bytes = Buffer.from(base64, "base64");
12983
- if (bytes.length === 0) throw new PasteImageError("La imagen llego vacia.");
13144
+ if (bytes.length === 0) throw new PasteImageError(serverText("imageEmpty"));
12984
13145
  if (bytes.length > MAX_IMAGE_BYTES3) {
12985
13146
  throw new PasteImageError(
12986
- `La imagen pesa ${Math.round(bytes.length / 1024 / 1024)} MB; el maximo son ${MAX_IMAGE_BYTES3 / 1024 / 1024} MB.`
13147
+ serverText("imageTooLarge", {
13148
+ size: Math.round(bytes.length / 1024 / 1024),
13149
+ max: MAX_IMAGE_BYTES3 / 1024 / 1024
13150
+ })
12987
13151
  );
12988
13152
  }
12989
13153
  const signature = detectImageFormat(bytes);
12990
13154
  if (signature === null) {
12991
- throw new PasteImageError("El contenido no es una imagen de un formato conocido.");
13155
+ throw new PasteImageError(serverText("imageUnknownFormat"));
12992
13156
  }
12993
13157
  const directory = path32.join(this.root, safeSegment(terminalId));
12994
13158
  await mkdir6(directory, { recursive: true, mode: DIRECTORY_MODE });
@@ -13010,7 +13174,7 @@ var PasteStore = class {
13010
13174
  async saveText(terminalId, baseName, content) {
13011
13175
  const bytes = Buffer.from(content, "utf8");
13012
13176
  if (bytes.length > MAX_TEXT_BYTES) {
13013
- throw new PasteTextError(`El texto pesa ${bytes.length} bytes; el maximo son ${MAX_TEXT_BYTES}.`);
13177
+ throw new PasteTextError(`The text is ${bytes.length} bytes; the maximum is ${MAX_TEXT_BYTES}.`);
13014
13178
  }
13015
13179
  const directory = path32.join(this.root, safeSegment(terminalId));
13016
13180
  await mkdir6(directory, { recursive: true, mode: DIRECTORY_MODE });
@@ -13020,6 +13184,40 @@ var PasteStore = class {
13020
13184
  await writeFile3(file, bytes, { mode: FILE_MODE });
13021
13185
  return { path: file, bytes: bytes.length };
13022
13186
  }
13187
+ /**
13188
+ * Guarda un archivo adjunto del cuadro de escritura (hito 33, §6.21).
13189
+ *
13190
+ * Mismas reglas que una imagen, con una diferencia: un documento no tiene una
13191
+ * firma que comprobar, asi que lo que se cuida es el nombre. El cliente manda
13192
+ * una pista y `safeFileName` la reduce a `[A-Za-z0-9._-]`; va **detras** del
13193
+ * prefijo que pone el servidor —`adjunto-<n>-<8 hex>-`—, asi que ni un nombre
13194
+ * reservado de Windows ni un `..` llegan a ser el nombre del archivo. Lo que
13195
+ * el sistema ejecuta con un doble clic no se guarda.
13196
+ */
13197
+ async saveAttachment(terminalId, nameHint, base64) {
13198
+ const safe = safeFileName(nameHint);
13199
+ if (isBlockedExtension(safe)) {
13200
+ throw new PasteFileError(serverText("attachmentExecutable", { name: safe }));
13201
+ }
13202
+ const bytes = Buffer.from(base64, "base64");
13203
+ if (bytes.length === 0) throw new PasteFileError(serverText("attachmentEmpty", { name: safe }));
13204
+ if (bytes.length > MAX_FILE_BYTES) {
13205
+ throw new PasteFileError(
13206
+ serverText("attachmentTooLarge", {
13207
+ name: safe,
13208
+ size: Math.round(bytes.length / 1024 / 1024),
13209
+ max: MAX_FILE_BYTES / 1024 / 1024
13210
+ })
13211
+ );
13212
+ }
13213
+ const directory = path32.join(this.root, safeSegment(terminalId));
13214
+ await mkdir6(directory, { recursive: true, mode: DIRECTORY_MODE });
13215
+ this.counter += 1;
13216
+ const name = `adjunto-${this.counter}-${randomUUID2().slice(0, 8)}-${safe}`;
13217
+ const file = path32.join(directory, name);
13218
+ await writeFile3(file, bytes, { mode: FILE_MODE });
13219
+ return { path: file, bytes: bytes.length, name };
13220
+ }
13023
13221
  /** Borra lo de una pestana. Se llama al cerrarla. */
13024
13222
  async clearTerminal(terminalId) {
13025
13223
  await rm2(path32.join(this.root, safeSegment(terminalId)), {
@@ -13444,7 +13642,7 @@ var SessionIndex = class extends EventEmitter4 {
13444
13642
  await mkdir7(appConfigDir(), { recursive: true });
13445
13643
  await writeFile4(sessionIndexCachePath(), JSON.stringify(this.cache), "utf8");
13446
13644
  } catch (error) {
13447
- console.warn("[indice] no se pudo guardar la cache:", error);
13645
+ console.warn("[index] couldn't save the cache:", error);
13448
13646
  }
13449
13647
  }
13450
13648
  /**
@@ -13467,7 +13665,7 @@ var SessionIndex = class extends EventEmitter4 {
13467
13665
  try {
13468
13666
  return { items: await source.history.list(), threw: false };
13469
13667
  } catch (error) {
13470
- console.warn(`[indice] no se pudo listar el historial de ${source.agent}:`, error);
13668
+ console.warn(`[index] couldn't list the history of ${source.agent}:`, error);
13471
13669
  return { items: null, threw: true };
13472
13670
  }
13473
13671
  })
@@ -13679,11 +13877,11 @@ function isSessionAgentId(value) {
13679
13877
  return SESSION_AGENT_IDS.includes(value);
13680
13878
  }
13681
13879
  function assertSessionKey(agent, sessionId) {
13682
- if (!isSessionAgentId(agent)) throw new Error(`Fuente desconocida para la copia: ${JSON.stringify(agent)}`);
13683
- if (!isSafeSessionId(sessionId)) throw new Error(`Id de sesion que no se copia: ${JSON.stringify(sessionId)}`);
13880
+ if (!isSessionAgentId(agent)) throw new Error(`Unknown source for the local copy: ${JSON.stringify(agent)}`);
13881
+ if (!isSafeSessionId(sessionId)) throw new Error(`Session id that isn't copied: ${JSON.stringify(sessionId)}`);
13684
13882
  }
13685
13883
  function agentSessionsDir(dir, agent) {
13686
- if (!isSessionAgentId(agent)) throw new Error(`Fuente desconocida para la copia: ${JSON.stringify(agent)}`);
13884
+ if (!isSessionAgentId(agent)) throw new Error(`Unknown source for the local copy: ${JSON.stringify(agent)}`);
13687
13885
  return path34.join(sessionsRoot2(dir), agent);
13688
13886
  }
13689
13887
  function sessionFile(dir, agent, sessionId) {
@@ -13695,7 +13893,7 @@ function assetsDir(dir, agent, sessionId) {
13695
13893
  return path34.join(sessionsRoot2(dir), agent, `${sessionId}.assets`);
13696
13894
  }
13697
13895
  function assetFile(dir, agent, sessionId, asset) {
13698
- if (!VAULT_ASSET_NAME_PATTERN.test(asset)) throw new Error(`Nombre de asset invalido: ${JSON.stringify(asset)}`);
13896
+ if (!VAULT_ASSET_NAME_PATTERN.test(asset)) throw new Error(`Invalid asset name: ${JSON.stringify(asset)}`);
13699
13897
  return path34.join(assetsDir(dir, agent, sessionId), asset);
13700
13898
  }
13701
13899
  function projectFolderName(cwd, platform) {
@@ -13842,7 +14040,7 @@ function parseAppSettings(value, platform, warn = () => void 0) {
13842
14040
  if (typeof dir === "string" && isAbsoluteDir(dir, platform)) {
13843
14041
  settings2.vault.dir = dir;
13844
14042
  } else if (dir !== null && dir !== void 0) {
13845
- warn(`[ajustes] la carpeta de la copia ${JSON.stringify(dir)} no es una ruta absoluta: se usa la de por defecto`);
14043
+ warn(`[settings] the local copy folder ${JSON.stringify(dir)} isn't an absolute path: using the default one`);
13846
14044
  }
13847
14045
  settings2.vault.toolResultMaxChars = clampToolResultMaxChars(vault["toolResultMaxChars"]);
13848
14046
  return settings2;
@@ -13876,13 +14074,13 @@ var SettingsStore = class {
13876
14074
  try {
13877
14075
  parsed = JSON.parse(raw);
13878
14076
  } catch {
13879
- this.log.warn("[ajustes] settings.json no es JSON valido: se usan los valores por defecto");
14077
+ this.log.warn("[settings] settings.json isn't valid JSON: using the defaults");
13880
14078
  this.current = frozen(defaultAppSettings());
13881
14079
  return;
13882
14080
  }
13883
14081
  const settings2 = parseAppSettings(parsed, this.platform, (message) => this.log.warn(message));
13884
14082
  if (settings2 === null) {
13885
- this.log.warn("[ajustes] settings.json es de otra version: se usan los valores por defecto");
14083
+ this.log.warn("[settings] settings.json is from another version: using the defaults");
13886
14084
  }
13887
14085
  this.current = frozen(settings2 ?? defaultAppSettings());
13888
14086
  }
@@ -13901,7 +14099,7 @@ var SettingsStore = class {
13901
14099
  if (vault.enabled !== void 0) next.vault.enabled = vault.enabled;
13902
14100
  if (vault.dir !== void 0) {
13903
14101
  if (vault.dir !== null && !isAbsoluteDir(vault.dir, this.platform)) {
13904
- throw new Error(`La carpeta de la copia tiene que ser una ruta absoluta: ${JSON.stringify(vault.dir)}`);
14102
+ throw new Error(`The local copy folder has to be an absolute path: ${JSON.stringify(vault.dir)}`);
13905
14103
  }
13906
14104
  next.vault.dir = vault.dir;
13907
14105
  }
@@ -13924,39 +14122,48 @@ var STATUS_LINE_LABEL = "Status line";
13924
14122
  function statusLineStartupText(state) {
13925
14123
  switch (state) {
13926
14124
  case "active":
13927
- return "configurada";
14125
+ return "configured";
13928
14126
  case "missing":
13929
- return "sin configurar (sin estado ni medidor; se configura desde el medidor)";
14127
+ return "not configured (no status or meter; set it up from the meter)";
13930
14128
  case "other-command":
13931
- return "hay otra configurada (sin estado ni medidor)";
14129
+ return "another one is configured (no status or meter)";
13932
14130
  case "disabled":
13933
- return "desactivada con enabled: false";
14131
+ return "disabled with enabled: false";
13934
14132
  case "unreadable":
13935
- return "no pude leer su settings.json";
14133
+ return "couldn't read its settings.json";
14134
+ }
14135
+ }
14136
+ var AVAILABLE_AGENTS_LABEL = "Available CLIs";
14137
+ var HISTORY_LABEL = "History";
14138
+ function missingText(text) {
14139
+ if (text.key !== "cliMissing") {
14140
+ const raw = text.params?.["text"];
14141
+ return typeof raw === "string" ? raw : text.key;
13936
14142
  }
14143
+ const command = String(text.params?.["command"] ?? "");
14144
+ const url = String(text.params?.["url"] ?? "");
14145
+ return `The "${command}" command wasn't found in the PATH. Agent Workbench uses the CLI you already have installed: it doesn't include or download it. Install it from ${url} and start again.`;
13937
14146
  }
13938
- var AVAILABLE_AGENTS_LABEL = "CLIs disponibles";
13939
- var HISTORY_LABEL = "Historial";
13940
14147
  function startupAgentLines(agents) {
13941
14148
  const available = agents.filter((agent) => agent.resolvedPath !== null);
13942
14149
  const lines = [
13943
- ` ${AVAILABLE_AGENTS_LABEL} ${available.length === 0 ? "ninguna" : available.map((agent) => agent.id).join(", ")}`
14150
+ ` ${AVAILABLE_AGENTS_LABEL} ${available.length === 0 ? "none" : available.map((agent) => agent.id).join(", ")}`
13944
14151
  ];
13945
14152
  for (const agent of available) {
13946
14153
  const title = available.length === 1 ? "CLI " : `CLI (${agent.label}) `;
13947
- lines.push(` ${title}${agent.version ?? "version desconocida"}`);
13948
- lines.push(` Binario ${agent.resolvedPath ?? ""}`);
14154
+ lines.push(` ${title}${agent.version ?? "unknown version"}`);
14155
+ lines.push(` Binary ${agent.resolvedPath ?? ""}`);
13949
14156
  const note = agent.historyNote ?? null;
13950
- if (note !== null) lines.push(` ${HISTORY_LABEL} ${note}`);
14157
+ if (note !== null) lines.push(` ${HISTORY_LABEL} ${note}`);
13951
14158
  const statusLine = agent.statusLine ?? null;
13952
14159
  if (statusLine !== null) lines.push(` ${STATUS_LINE_LABEL} ${statusLineStartupText(statusLine)}`);
13953
14160
  }
13954
14161
  const first = agents[0];
13955
14162
  if (available.length === 0 && first !== void 0) {
13956
- lines.push(" CLI NO ENCONTRADA");
13957
- if (first.missingMessage !== null) lines.push("", ` ${first.missingMessage}`);
14163
+ lines.push(" CLI NOT FOUND");
14164
+ if (first.missingMessage !== null) lines.push("", ` ${missingText(first.missingMessage)}`);
13958
14165
  const others = agents.slice(1).map((agent) => agent.label);
13959
- if (others.length > 0) lines.push(` Tambien funciona con: ${others.join(", ")}`);
14166
+ if (others.length > 0) lines.push(` Also works with: ${others.join(", ")}`);
13960
14167
  }
13961
14168
  for (const agent of agents) {
13962
14169
  const note = agent.historyNote ?? null;
@@ -13971,7 +14178,7 @@ import { mkdir as mkdir10, readFile as readFile13, rename as rename5, rm as rm4,
13971
14178
  import path36 from "node:path";
13972
14179
  var STATE_VERSION2 = 1;
13973
14180
  var WRITE_DEBOUNCE_MS2 = 500;
13974
- var NotesError = class extends Error {
14181
+ var NotesError = class extends ServerTextError {
13975
14182
  };
13976
14183
  function parseState2(raw) {
13977
14184
  try {
@@ -14031,10 +14238,10 @@ var NotesStore = class {
14031
14238
  * Un id repetido no crea nada; el cliente ya tiene esa nota.
14032
14239
  */
14033
14240
  create(noteId, now = Date.now()) {
14034
- if (!isValidNoteId(noteId)) throw new NotesError("El id de la nota no es valido.");
14035
- if (this.get(noteId) !== null) throw new NotesError("Esa nota ya existe.");
14241
+ if (!isValidNoteId(noteId)) throw new NotesError(serverText("noteIdInvalid"));
14242
+ if (this.get(noteId) !== null) throw new NotesError(serverText("noteExists"));
14036
14243
  if (this.notes.length >= MAX_NOTES) {
14037
- throw new NotesError(`Hasta ${MAX_NOTES} notas abiertas. Cerra alguna primero.`);
14244
+ throw new NotesError(serverText("notesMax", { max: MAX_NOTES }));
14038
14245
  }
14039
14246
  const note = { noteId, text: "", images: [], createdAt: now, updatedAt: now };
14040
14247
  this.notes.push(note);
@@ -14044,9 +14251,9 @@ var NotesStore = class {
14044
14251
  /** Devuelve true si el texto cambio de verdad. */
14045
14252
  update(noteId, text, now = Date.now()) {
14046
14253
  const note = this.get(noteId);
14047
- if (note === null) throw new NotesError("La nota ya no existe.");
14254
+ if (note === null) throw new NotesError(serverText("noteGone"));
14048
14255
  if (text.length > MAX_NOTE_TEXT_CHARS) {
14049
- throw new NotesError(`Una nota admite hasta ${MAX_NOTE_TEXT_CHARS} caracteres.`);
14256
+ throw new NotesError(serverText("noteTooLong", { max: MAX_NOTE_TEXT_CHARS }));
14050
14257
  }
14051
14258
  if (note.text === text) return false;
14052
14259
  note.text = text;
@@ -14073,23 +14280,26 @@ var NotesStore = class {
14073
14280
  */
14074
14281
  async addImage(noteId, declaredType, base64, now = Date.now()) {
14075
14282
  const note = this.get(noteId);
14076
- if (note === null) throw new NotesError("La nota ya no existe.");
14283
+ if (note === null) throw new NotesError(serverText("noteGone"));
14077
14284
  if (note.images.length >= MAX_NOTE_IMAGES) {
14078
- throw new NotesError(`Hasta ${MAX_NOTE_IMAGES} imagenes por nota.`);
14285
+ throw new NotesError(serverText("noteImagesMax", { max: MAX_NOTE_IMAGES }));
14079
14286
  }
14080
14287
  if (!IMAGE_MEDIA_TYPES.has(declaredType)) {
14081
- throw new NotesError(`Tipo de imagen no admitido: ${declaredType}`);
14288
+ throw new NotesError(serverText("imageTypeUnsupported", { type: declaredType }));
14082
14289
  }
14083
14290
  const bytes = Buffer.from(base64, "base64");
14084
- if (bytes.length === 0) throw new NotesError("La imagen llego vacia.");
14291
+ if (bytes.length === 0) throw new NotesError(serverText("imageEmpty"));
14085
14292
  if (bytes.length > MAX_SUBMIT_IMAGE_BYTES) {
14086
14293
  throw new NotesError(
14087
- `La imagen pesa ${Math.round(bytes.length / 1024 / 1024)} MB; el maximo son ${MAX_SUBMIT_IMAGE_BYTES / 1024 / 1024} MB.`
14294
+ serverText("imageTooLarge", {
14295
+ size: Math.round(bytes.length / 1024 / 1024),
14296
+ max: MAX_SUBMIT_IMAGE_BYTES / 1024 / 1024
14297
+ })
14088
14298
  );
14089
14299
  }
14090
14300
  const format = detectImageFormat(bytes);
14091
14301
  if (format === null) {
14092
- throw new NotesError("El contenido no es una imagen de un formato conocido.");
14302
+ throw new NotesError(serverText("imageUnknownFormat"));
14093
14303
  }
14094
14304
  const image = {
14095
14305
  imageId: randomUUID3(),
@@ -14196,7 +14406,7 @@ var NotesStore = class {
14196
14406
  );
14197
14407
  await rename5(temporary, this.statePath);
14198
14408
  } catch (error) {
14199
- console.warn("[notas] no se pudieron guardar:", error);
14409
+ console.warn("[notes] couldn't save the notes:", error);
14200
14410
  }
14201
14411
  }
14202
14412
  };
@@ -14283,7 +14493,7 @@ function watchSessions(index, hub, agents, options = {}) {
14283
14493
  try {
14284
14494
  stops.push(watch((filePath) => onFileEvent(agent, root, filePath)));
14285
14495
  } catch (error) {
14286
- console.warn("[watcher] no se pudo observar el historial:", error);
14496
+ console.warn("[watcher] couldn't watch the history:", error);
14287
14497
  }
14288
14498
  };
14289
14499
  const watchRoot = (agent, root, announceExisting) => {
@@ -14298,7 +14508,7 @@ function watchSessions(index, hub, agents, options = {}) {
14298
14508
  ...root.ignore !== void 0 ? { ignored: root.ignore } : {}
14299
14509
  });
14300
14510
  } catch (error) {
14301
- console.warn("[watcher] no se pudo observar el historial:", error);
14511
+ console.warn("[watcher] couldn't watch the history:", error);
14302
14512
  return;
14303
14513
  }
14304
14514
  watcher.on("add", onRootEvent);
@@ -14347,7 +14557,7 @@ function watchSessions(index, hub, agents, options = {}) {
14347
14557
  try {
14348
14558
  stop();
14349
14559
  } catch (error) {
14350
- console.warn("[watcher] no se pudo dejar de observar el historial:", error);
14560
+ console.warn("[watcher] couldn't stop watching the history:", error);
14351
14561
  }
14352
14562
  }
14353
14563
  };
@@ -14595,12 +14805,11 @@ function wakeActionFor(state) {
14595
14805
  }
14596
14806
 
14597
14807
  // packages/server/src/terminal-open-error.ts
14598
- var TerminalOpenError = class extends Error {
14599
- constructor(code, message, detail) {
14600
- super(message);
14808
+ var TerminalOpenError = class extends ServerTextError {
14809
+ constructor(code, text, detail) {
14810
+ super(text);
14601
14811
  this.code = code;
14602
14812
  this.detail = detail;
14603
- this.name = "TerminalOpenError";
14604
14813
  }
14605
14814
  code;
14606
14815
  detail;
@@ -14612,7 +14821,7 @@ async function resolveLaunchPlan(launch) {
14612
14821
  if (error instanceof TerminalOpenError) throw error;
14613
14822
  throw new TerminalOpenError(
14614
14823
  "spawn-failed",
14615
- "No se pudo abrir la terminal.",
14824
+ serverText("terminalOpenFailed"),
14616
14825
  error instanceof Error ? error.message : String(error)
14617
14826
  );
14618
14827
  }
@@ -14670,7 +14879,7 @@ function readTab(tab, missingAgent) {
14670
14879
  agent = asLiteral(tab["agent"], AGENT_IDS);
14671
14880
  if (agent === null) {
14672
14881
  console.warn(
14673
- `[workspace] no se restaura la pestana de ${cwd}: CLI desconocida ${JSON.stringify(tab["agent"])}. Se conserva en el archivo.`
14882
+ `[workspace] the tab of ${cwd} isn't restored: unknown CLI ${JSON.stringify(tab["agent"])}. It's kept in the file.`
14674
14883
  );
14675
14884
  return { kind: "foreign", raw: tab };
14676
14885
  }
@@ -14779,7 +14988,7 @@ var WorkspaceStore = class {
14779
14988
  const { rename: rename6 } = await import("node:fs/promises");
14780
14989
  await rename6(temporary, target);
14781
14990
  } catch (error) {
14782
- console.warn("[workspace] no se pudo guardar el estado:", error);
14991
+ console.warn("[workspace] couldn't save the state:", error);
14783
14992
  }
14784
14993
  }
14785
14994
  };
@@ -14839,7 +15048,7 @@ var TerminalRegistry = class extends EventEmitter5 {
14839
15048
  if (registered === null || registered.location === null) {
14840
15049
  throw new TerminalOpenError(
14841
15050
  "cli-not-found",
14842
- "La CLI no esta instalada o no se encontro en el PATH."
15051
+ serverText("cliNotFound")
14843
15052
  );
14844
15053
  }
14845
15054
  return {
@@ -14850,7 +15059,7 @@ var TerminalRegistry = class extends EventEmitter5 {
14850
15059
  if (this.shell === null) {
14851
15060
  throw new TerminalOpenError(
14852
15061
  "shell-not-found",
14853
- "No se encontro ninguna consola del sistema para abrir."
15062
+ serverText("shellNotFound")
14854
15063
  );
14855
15064
  }
14856
15065
  return { kind: "shell", shell: this.shell };
@@ -14872,7 +15081,7 @@ var TerminalRegistry = class extends EventEmitter5 {
14872
15081
  if (kind === "agent" && options.agent !== void 0 && this.agents.get(options.agent) === null) {
14873
15082
  throw new TerminalOpenError(
14874
15083
  "agent-unsupported",
14875
- "Este servidor no sabe lanzar esa CLI.",
15084
+ serverText("agentUnknown"),
14876
15085
  options.agent
14877
15086
  );
14878
15087
  }
@@ -14889,7 +15098,7 @@ var TerminalRegistry = class extends EventEmitter5 {
14889
15098
  if (this.terminals.size >= MAX_TERMINALS) {
14890
15099
  throw new TerminalOpenError(
14891
15100
  "too-many-terminals",
14892
- `No se pueden abrir mas de ${MAX_TERMINALS} pestanas a la vez.`
15101
+ serverText("tooManyTabs", { max: MAX_TERMINALS })
14893
15102
  );
14894
15103
  }
14895
15104
  await this.assertDirectory(options.cwd);
@@ -14926,7 +15135,7 @@ var TerminalRegistry = class extends EventEmitter5 {
14926
15135
  try {
14927
15136
  const spawned = await this.spawn(entry, { resume: resumed });
14928
15137
  if (!spawned) {
14929
- throw new TerminalOpenError("spawn-failed", "La pesta\xF1a se cerr\xF3 mientras se abr\xEDa.");
15138
+ throw new TerminalOpenError("spawn-failed", serverText("tabClosedWhileOpening"));
14930
15139
  }
14931
15140
  } catch (error) {
14932
15141
  if (this.terminals.get(terminalId) === entry) this.terminals.delete(terminalId);
@@ -14959,7 +15168,7 @@ var TerminalRegistry = class extends EventEmitter5 {
14959
15168
  entry.launching = true;
14960
15169
  try {
14961
15170
  if (action === "restart" && !await this.endSessionForRestart(entry)) {
14962
- throw new TerminalOpenError("spawn-failed", "No se pudo cerrar la CLI de la pesta\xF1a para relanzarla.");
15171
+ throw new TerminalOpenError("spawn-failed", serverText("relaunchCloseFailed"));
14963
15172
  }
14964
15173
  if (this.terminals.get(terminalId) !== entry) return null;
14965
15174
  await this.assertDirectory(entry.descriptor.cwd);
@@ -14997,7 +15206,7 @@ var TerminalRegistry = class extends EventEmitter5 {
14997
15206
  resolve(entry.session !== session);
14998
15207
  }, RESTART_EXIT_TIMEOUT_MS);
14999
15208
  this.on("exit", onExit);
15000
- debugLog("registro", `relanzando ${terminalId.slice(0, 8)}: su servidor se cerro`);
15209
+ debugLog("registry", `relaunching ${terminalId.slice(0, 8)}: its server closed`);
15001
15210
  session.dispose();
15002
15211
  });
15003
15212
  }
@@ -15007,10 +15216,10 @@ var TerminalRegistry = class extends EventEmitter5 {
15007
15216
  try {
15008
15217
  info = await stat20(cwd);
15009
15218
  } catch {
15010
- throw new TerminalOpenError("invalid-cwd", `El directorio no existe: ${cwd}`);
15219
+ throw new TerminalOpenError("invalid-cwd", serverText("cwdMissing", { cwd }));
15011
15220
  }
15012
15221
  if (!info.isDirectory()) {
15013
- throw new TerminalOpenError("invalid-cwd", `No es un directorio: ${cwd}`);
15222
+ throw new TerminalOpenError("invalid-cwd", serverText("cwdNotDir", { cwd }));
15014
15223
  }
15015
15224
  }
15016
15225
  /**
@@ -15069,7 +15278,7 @@ var TerminalRegistry = class extends EventEmitter5 {
15069
15278
  const listenerCount = target?.listeners.size ?? 0;
15070
15279
  debugLog(
15071
15280
  "pty",
15072
- `salida ${chunk.length} bytes de ${terminalId.slice(0, 8)}, ${listenerCount} oyentes`
15281
+ `output ${chunk.length} bytes from ${terminalId.slice(0, 8)}, ${listenerCount} listeners`
15073
15282
  );
15074
15283
  if (target !== void 0) {
15075
15284
  for (const listener of target.listeners) listener(terminalId, chunk);
@@ -15098,7 +15307,7 @@ var TerminalRegistry = class extends EventEmitter5 {
15098
15307
  } catch (error) {
15099
15308
  throw new TerminalOpenError(
15100
15309
  "spawn-failed",
15101
- "No se pudo abrir la terminal.",
15310
+ serverText("terminalOpenFailed"),
15102
15311
  error instanceof Error ? error.message : String(error)
15103
15312
  );
15104
15313
  }
@@ -15134,7 +15343,7 @@ var TerminalRegistry = class extends EventEmitter5 {
15134
15343
  finished = true;
15135
15344
  const target = this.terminals.get(terminalId);
15136
15345
  if (target !== void 0 && hook !== null) target.launchHook.forget(hook);
15137
- debugLog("registro", `dialogo de reanudar en ${terminalId.slice(0, 8)}: ${outcome}`);
15346
+ debugLog("registry", `resume dialog in ${terminalId.slice(0, 8)}: ${outcome}`);
15138
15347
  },
15139
15348
  reportSessionId: (discovered) => this.reportSessionId(terminalId, discovered)
15140
15349
  });
@@ -15256,7 +15465,7 @@ var TerminalRegistry = class extends EventEmitter5 {
15256
15465
  const { tab } = saved;
15257
15466
  if ((this.agents.get(tab.agent)?.location ?? null) === null) {
15258
15467
  console.warn(
15259
- `[workspace] no se restauro la pestana de ${tab.cwd}: su CLI no esta disponible. Se conserva para cuando vuelva.`
15468
+ `[workspace] the tab of ${tab.cwd} wasn't restored: its CLI isn't available. It's kept for when it's back.`
15260
15469
  );
15261
15470
  this.unavailableTabs.push({ position, tab });
15262
15471
  continue;
@@ -15266,7 +15475,7 @@ var TerminalRegistry = class extends EventEmitter5 {
15266
15475
  await this.assertDirectory(tab.cwd);
15267
15476
  } catch (error) {
15268
15477
  const reason2 = error instanceof Error ? error.message : String(error);
15269
- console.warn(`[workspace] no se restauro la pestana de ${tab.cwd}: ${reason2}`);
15478
+ console.warn(`[workspace] the tab of ${tab.cwd} wasn't restored: ${reason2}`);
15270
15479
  continue;
15271
15480
  }
15272
15481
  const terminalId = randomUUID4();
@@ -15347,8 +15556,8 @@ var TerminalRegistry = class extends EventEmitter5 {
15347
15556
  entry.listeners.add(listener);
15348
15557
  const replay = entry.buffer.read();
15349
15558
  debugLog(
15350
- "registro",
15351
- `attach ${terminalId.slice(0, 8)}: replay de ${replay.length} bytes, ${entry.listeners.size} oyentes`
15559
+ "registry",
15560
+ `attach ${terminalId.slice(0, 8)}: replay of ${replay.length} bytes, ${entry.listeners.size} listeners`
15352
15561
  );
15353
15562
  return { replay, truncated: entry.buffer.isTruncated() };
15354
15563
  }
@@ -15438,7 +15647,7 @@ import path39 from "node:path";
15438
15647
  var MAX_ENTRIES = 500;
15439
15648
  var MAX_PICKERS = 8;
15440
15649
  var SKIPPED = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "bin", "obj", "$RECYCLE.BIN"]);
15441
- var DirectoryPickerError = class extends Error {
15650
+ var DirectoryPickerError = class extends ServerTextError {
15442
15651
  };
15443
15652
  function isInsideProtected(target, dirs) {
15444
15653
  const resolved = path39.resolve(target);
@@ -15481,7 +15690,7 @@ var DirectoryPickers = class {
15481
15690
  /** Abre un selector en el directorio del usuario. */
15482
15691
  async open() {
15483
15692
  if (this.pickers.size >= MAX_PICKERS) {
15484
- throw new DirectoryPickerError("Hay demasiados selectores abiertos.");
15693
+ throw new DirectoryPickerError(serverText("pickerTooMany"));
15485
15694
  }
15486
15695
  const picker = { id: randomUUID5(), current: homedir11(), entries: /* @__PURE__ */ new Set() };
15487
15696
  this.pickers.set(picker.id, picker);
@@ -15513,11 +15722,11 @@ var DirectoryPickers = class {
15513
15722
  return this.listing(picker);
15514
15723
  }
15515
15724
  if (!picker.entries.has(name)) {
15516
- throw new DirectoryPickerError("Esa carpeta no esta en el listado actual.");
15725
+ throw new DirectoryPickerError(serverText("pickerNotListed"));
15517
15726
  }
15518
15727
  const target = path39.join(picker.current, name);
15519
15728
  if (isInsideProtected(target, this.protectedDirs)) {
15520
- throw new DirectoryPickerError("Esa carpeta es de la CLI y no se abre desde aca.");
15729
+ throw new DirectoryPickerError(serverText("pickerCliFolder"));
15521
15730
  }
15522
15731
  picker.current = target;
15523
15732
  return this.listing(picker);
@@ -15527,7 +15736,7 @@ var DirectoryPickers = class {
15527
15736
  const picker = this.require(pickerId);
15528
15737
  const roots = await this.knownRoots();
15529
15738
  const target = roots[index];
15530
- if (target === void 0) throw new DirectoryPickerError("Esa raiz no existe.");
15739
+ if (target === void 0) throw new DirectoryPickerError(serverText("pickerRootMissing"));
15531
15740
  picker.current = target;
15532
15741
  return this.listing(picker);
15533
15742
  }
@@ -15542,29 +15751,29 @@ var DirectoryPickers = class {
15542
15751
  const picker = this.require(pickerId);
15543
15752
  const clean = name.trim();
15544
15753
  if (clean.length === 0 || clean.length > 120) {
15545
- throw new DirectoryPickerError("El nombre de la carpeta no es valido.");
15754
+ throw new DirectoryPickerError(serverText("pickerNameInvalid"));
15546
15755
  }
15547
15756
  if (clean === "." || clean === "..") {
15548
- throw new DirectoryPickerError("El nombre de la carpeta no es valido.");
15757
+ throw new DirectoryPickerError(serverText("pickerNameInvalid"));
15549
15758
  }
15550
15759
  if (/[\\/:*?"<>|]/.test(clean)) {
15551
- throw new DirectoryPickerError("El nombre tiene caracteres que no se pueden usar.");
15760
+ throw new DirectoryPickerError(serverText("pickerNameChars"));
15552
15761
  }
15553
15762
  for (const character of clean) {
15554
15763
  if ((character.codePointAt(0) ?? 0) < 32) {
15555
- throw new DirectoryPickerError("El nombre tiene caracteres que no se pueden usar.");
15764
+ throw new DirectoryPickerError(serverText("pickerNameChars"));
15556
15765
  }
15557
15766
  }
15558
15767
  const target = path39.join(picker.current, clean);
15559
15768
  if (isInsideProtected(target, this.protectedDirs)) {
15560
- throw new DirectoryPickerError("Ahi no se crean proyectos: es la carpeta de la CLI.");
15769
+ throw new DirectoryPickerError(serverText("pickerNoProjectsHere"));
15561
15770
  }
15562
15771
  try {
15563
15772
  await mkdir12(target);
15564
15773
  } catch (error) {
15565
15774
  const code = error.code;
15566
15775
  throw new DirectoryPickerError(
15567
- code === "EEXIST" ? "Ya hay una carpeta con ese nombre." : "No se pudo crear la carpeta ahi."
15776
+ code === "EEXIST" ? serverText("pickerFolderExists") : serverText("pickerCreateFailed")
15568
15777
  );
15569
15778
  }
15570
15779
  picker.current = target;
@@ -15572,7 +15781,7 @@ var DirectoryPickers = class {
15572
15781
  }
15573
15782
  require(pickerId) {
15574
15783
  const picker = this.pickers.get(pickerId);
15575
- if (picker === void 0) throw new DirectoryPickerError("Ese selector ya no esta abierto.");
15784
+ if (picker === void 0) throw new DirectoryPickerError(serverText("pickerClosed"));
15576
15785
  return picker;
15577
15786
  }
15578
15787
  async knownRoots() {
@@ -15865,7 +16074,7 @@ async function readPreview(cwd, relativePath) {
15865
16074
  const absolute = await resolveInside(cwd, relativePath, { mustExist: true });
15866
16075
  const info = await stat22(absolute);
15867
16076
  if (info.isDirectory()) {
15868
- throw new Error("Es un directorio, no un archivo.");
16077
+ throw new Error("It's a directory, not a file.");
15869
16078
  }
15870
16079
  const content = await readFile15(absolute);
15871
16080
  const fileName = path40.basename(absolute);
@@ -15899,7 +16108,7 @@ function yieldToEventLoop() {
15899
16108
  }
15900
16109
  var GlobalSearchAbortedError = class extends Error {
15901
16110
  constructor() {
15902
- super("B\xFAsqueda cancelada.");
16111
+ super("Search cancelled.");
15903
16112
  this.name = "GlobalSearchAbortedError";
15904
16113
  }
15905
16114
  };
@@ -16179,13 +16388,13 @@ var MAX_POLLS = 5;
16179
16388
  var MAX_TOTAL_POLLS = 20;
16180
16389
  var UnsupportedHistoryError = class extends Error {
16181
16390
  constructor(label) {
16182
- super(`${label}: la fuente no declara que su seguidor respete los topes de la copia (wholeRead)`);
16391
+ super(`${label}: the source doesn't declare that its follower respects the local copy's limits (wholeRead)`);
16183
16392
  this.name = "UnsupportedHistoryError";
16184
16393
  }
16185
16394
  };
16186
16395
  var PagedHistoryError = class extends Error {
16187
16396
  constructor(label, events) {
16188
- super(`${label}: el seguidor pagino con maxEvents infinito (${events} eventos y hay mas)`);
16397
+ super(`${label}: the follower paged with infinite maxEvents (${events} ${events === 1 ? "event" : "events"} and more left)`);
16189
16398
  this.name = "PagedHistoryError";
16190
16399
  }
16191
16400
  };
@@ -16257,32 +16466,32 @@ async function readSourceEvents(history, target, maxEvents = HANDOFF_MAX_EVENTS)
16257
16466
  }
16258
16467
  var messageOf = (error) => error instanceof Error ? error.message : String(error);
16259
16468
  async function continueSession(deps, request) {
16260
- const failed = (message2, detail) => ({
16469
+ const failed = (text, detail) => ({
16261
16470
  ok: false,
16262
16471
  code: "continue-failed",
16263
- message: message2,
16472
+ text,
16264
16473
  ...detail !== void 0 ? { detail } : {}
16265
16474
  });
16266
16475
  const target = deps.agents.get(request.target);
16267
16476
  if (target === null) {
16268
- return { ok: false, code: "agent-unsupported", message: "Este servidor no sabe lanzar esa CLI.", detail: request.target };
16477
+ return { ok: false, code: "agent-unsupported", text: serverText("agentUnknown"), detail: request.target };
16269
16478
  }
16270
16479
  if (target.location === null) {
16271
- return { ok: false, code: "cli-not-found", message: `${target.adapter.label} no esta instalada o no se encontro en el PATH.` };
16480
+ return { ok: false, code: "cli-not-found", text: serverText("cliNotInstalled", { label: target.adapter.label }) };
16272
16481
  }
16273
16482
  if (request.agent === request.target) {
16274
- return failed("Esa conversaci\xF3n ya es de esa CLI: para seguirla, abrila desde la barra lateral.");
16483
+ return failed(serverText("continueSameCli"));
16275
16484
  }
16276
16485
  const summary = deps.index.find(request.agent, request.sessionId);
16277
- if (summary === null) return failed("Esa conversaci\xF3n no est\xE1 en el historial: si acaba de empezar, prob\xE1 de nuevo en unos segundos.");
16278
- if (summary.cwd.length === 0) return failed("Esa conversaci\xF3n no dice en qu\xE9 carpeta corri\xF3.");
16486
+ if (summary === null) return failed(serverText("continueNotInHistory"));
16487
+ if (summary.cwd.length === 0) return failed(serverText("continueNoFolder"));
16279
16488
  let source;
16280
16489
  try {
16281
16490
  source = await readEvents(deps, request.agent, summary);
16282
16491
  } catch (error) {
16283
- return failed("No se pudo leer esa conversaci\xF3n.", messageOf(error));
16492
+ return failed(serverText("continueReadFailed"), messageOf(error));
16284
16493
  }
16285
- if (source === null) return failed("No se encontr\xF3 el historial de esa conversaci\xF3n.");
16494
+ if (source === null) return failed(serverText("continueHistoryMissing"));
16286
16495
  const sourceLabel = isAgentId(request.agent) ? deps.agents.get(request.agent)?.adapter.label ?? request.agent : IMPORTED_AGENT_LABELS[request.agent];
16287
16496
  const plan = planTranscript({
16288
16497
  header: {
@@ -16298,27 +16507,27 @@ async function continueSession(deps, request) {
16298
16507
  state: source.state,
16299
16508
  complete: source.complete
16300
16509
  });
16301
- if (!plan.ok) return failed(plan.message);
16510
+ if (!plan.ok) return failed(plan.text);
16302
16511
  let descriptor;
16303
16512
  try {
16304
16513
  descriptor = await deps.registry.open({
16305
16514
  cwd: summary.cwd,
16306
16515
  agent: request.target,
16307
16516
  kind: "agent",
16308
- label: continuationLabel(summary.title)
16517
+ label: request.label ?? continuationLabel(summary.title)
16309
16518
  });
16310
16519
  } catch (error) {
16311
16520
  if (error instanceof TerminalOpenError) {
16312
- return { ok: false, code: error.code, message: error.message, ...error.detail !== void 0 ? { detail: error.detail } : {} };
16521
+ return { ok: false, code: error.code, text: error.text, ...error.detail !== void 0 ? { detail: error.detail } : {} };
16313
16522
  }
16314
- return failed("No se pudo abrir la pesta\xF1a de la continuaci\xF3n.", messageOf(error));
16523
+ return failed(serverText("continueTabFailed"), messageOf(error));
16315
16524
  }
16316
16525
  let saved;
16317
16526
  try {
16318
16527
  saved = await deps.pasteStore.saveText(descriptor.terminalId, "continuacion", plan.markdown);
16319
16528
  } catch (error) {
16320
16529
  deps.closeTab(descriptor.terminalId);
16321
- return failed("No se pudo guardar el transcript de la continuaci\xF3n.", messageOf(error));
16530
+ return failed(serverText("continueTranscriptFailed"), messageOf(error));
16322
16531
  }
16323
16532
  const message = buildContinuationMessage({
16324
16533
  sourceLabel,
@@ -16372,11 +16581,11 @@ function revealPath(absolutePath) {
16372
16581
  try {
16373
16582
  const child = spawn3(command, args, { stdio: "ignore", detached: true, windowsHide: true });
16374
16583
  child.on("error", (error) => {
16375
- console.warn("[archivos] no se pudo abrir la ruta:", error.message);
16584
+ console.warn("[files] couldn't open the path:", error.message);
16376
16585
  });
16377
16586
  child.unref();
16378
16587
  } catch (error) {
16379
- console.warn("[archivos] no se pudo abrir la ruta:", error);
16588
+ console.warn("[files] couldn't open the path:", error);
16380
16589
  }
16381
16590
  }
16382
16591
 
@@ -16408,7 +16617,9 @@ var TerminalWriteQueue = class {
16408
16617
  get interrupted() {
16409
16618
  return interrupted();
16410
16619
  },
16411
- writePieces: async (pieces, gapMs, write, guard2) => {
16620
+ writePieces: async (pieces, gapMs, write, guard2, writeOptions) => {
16621
+ const startAfterMs = writeOptions?.startAfterMs ?? 0;
16622
+ if (startAfterMs > 0) await this.wait(startAfterMs);
16412
16623
  for (const [index, piece] of pieces.entries()) {
16413
16624
  if (index > 0 && gapMs > 0) await this.wait(gapMs);
16414
16625
  if (interrupted()) return "interrupted";
@@ -16428,7 +16639,7 @@ var TerminalWriteQueue = class {
16428
16639
  await job(handle);
16429
16640
  }).catch((error) => {
16430
16641
  console.error(
16431
- `[escritura] fallo un envio a la terminal ${terminalId}:`,
16642
+ `[write] a send to terminal ${terminalId} failed:`,
16432
16643
  error instanceof Error ? error.message : error
16433
16644
  );
16434
16645
  }).finally(() => {
@@ -16511,20 +16722,20 @@ function isSameOrInside(child, parent, platform) {
16511
16722
  }
16512
16723
  function checkVaultTarget(current, target, options) {
16513
16724
  if (!isAbsoluteDir(target, options.platform)) {
16514
- return { kind: "refused", message: "La carpeta de la copia tiene que ser una ruta absoluta." };
16725
+ return { kind: "refused", text: serverText("vaultTargetNotAbsolute") };
16515
16726
  }
16516
16727
  const isProtected = isInsideProtected(target, options.protectedDirs) || options.protectedDirs.some((dir) => isSameOrInside(target, dir, options.platform));
16517
16728
  if (isProtected) {
16518
- return { kind: "refused", message: "Esa carpeta es de una CLI: la copia no se guarda ah\xED." };
16729
+ return { kind: "refused", text: serverText("vaultTargetCliFolder") };
16519
16730
  }
16520
16731
  if (normalizeCwdKey(current, options.platform) === normalizeCwdKey(target, options.platform)) {
16521
16732
  return { kind: "same" };
16522
16733
  }
16523
16734
  if (isSameOrInside(target, current, options.platform)) {
16524
- return { kind: "refused", message: "La carpeta nueva no puede estar dentro de la copia actual." };
16735
+ return { kind: "refused", text: serverText("vaultTargetInsideCurrent") };
16525
16736
  }
16526
16737
  if (isSameOrInside(current, target, options.platform)) {
16527
- return { kind: "refused", message: "La copia actual no puede quedar dentro de la carpeta nueva." };
16738
+ return { kind: "refused", text: serverText("vaultCurrentInsideTarget") };
16528
16739
  }
16529
16740
  return { kind: "ok" };
16530
16741
  }
@@ -16576,7 +16787,7 @@ function buildHeader(input) {
16576
16787
  writtenAt: input.writtenAt
16577
16788
  };
16578
16789
  if (parseVaultHeader(JSON.parse(JSON.stringify(header))) === null) {
16579
- throw new Error(`La cabecera de ${input.agent}/${input.sessionId} no se podria leer despues`);
16790
+ throw new Error(`The header of ${input.agent}/${input.sessionId} couldn't be read back`);
16580
16791
  }
16581
16792
  return header;
16582
16793
  }
@@ -16620,7 +16831,7 @@ function serializeSession(input) {
16620
16831
  truncated: document.truncated
16621
16832
  };
16622
16833
  if (parseVaultBodyLine(line) === null) {
16623
- throw new Error(`Documento que no se podria leer despues: ${JSON.stringify(document.name)}`);
16834
+ throw new Error(`A document that couldn't be read back: ${JSON.stringify(document.name)}`);
16624
16835
  }
16625
16836
  bodyLines.push(JSON.stringify(line));
16626
16837
  }
@@ -16736,18 +16947,20 @@ var VAULT_WRITER_REVISION = 1;
16736
16947
  var CALM_MS = 6e4;
16737
16948
  var PASS_DEBOUNCE_MS = 5e3;
16738
16949
  var TEMP_MAX_AGE_MS = 60 * 60 * 1e3;
16739
- var ORIGIN_NOT_FOUND = "no se encontr\xF3 el origen";
16740
- var FOREIGN_FORMAT = "la copia que ya est\xE1 es de un formato m\xE1s nuevo";
16741
- var UNSAFE_SESSION_ID = "id de sesi\xF3n que no se puede copiar";
16742
- var MEASURE_FIRST_MESSAGE = "Med\xED primero cu\xE1nto ocupar\xEDa.";
16950
+ var ORIGIN_NOT_FOUND = serverText("vaultReasonOriginMissing");
16951
+ var FOREIGN_FORMAT = serverText("vaultReasonNewerFormat");
16952
+ var UNSAFE_SESSION_ID = serverText("vaultReasonUnsafeId");
16953
+ var MEASURE_FIRST_TEXT = serverText("vaultMeasureFirst");
16743
16954
  var PROGRESS_EVERY = 10;
16744
16955
  var MAX_FAILURE_REASONS = 5;
16745
16956
  var OWN_TEMP_FILE2 = /\.\d+\.[0-9a-f]{12}\.tmp$/;
16746
16957
  var VaultBusyError = class extends Error {
16747
16958
  constructor(activity) {
16748
- super(`La copia est\xE1 ocupada (${activity}).`);
16959
+ super(`The local copy is busy (${activity}).`);
16960
+ this.activity = activity;
16749
16961
  this.name = "VaultBusyError";
16750
16962
  }
16963
+ activity;
16751
16964
  };
16752
16965
  var REAL_TIMERS2 = {
16753
16966
  set: (run, ms) => {
@@ -16765,7 +16978,8 @@ function isNoSpace(error) {
16765
16978
  return error?.code === "ENOSPC";
16766
16979
  }
16767
16980
  function addReason(reasons, reason2) {
16768
- if (reasons.length < MAX_FAILURE_REASONS && !reasons.includes(reason2)) reasons.push(reason2);
16981
+ const same = JSON.stringify(reason2);
16982
+ if (reasons.length < MAX_FAILURE_REASONS && !reasons.some((known) => JSON.stringify(known) === same)) reasons.push(reason2);
16769
16983
  }
16770
16984
  function emptyMeasure(agent) {
16771
16985
  return {
@@ -16782,7 +16996,7 @@ function emptyMeasure(agent) {
16782
16996
  };
16783
16997
  }
16784
16998
  function enableRefusal(hasMeasurement, passSessions) {
16785
- return hasMeasurement || passSessions > 0 ? null : MEASURE_FIRST_MESSAGE;
16999
+ return hasMeasurement || passSessions > 0 ? null : MEASURE_FIRST_TEXT;
16786
17000
  }
16787
17001
  function vaultEventLimits(toolResultMaxChars) {
16788
17002
  return {
@@ -16870,7 +17084,7 @@ var VaultWriter = class {
16870
17084
  }
16871
17085
  const dir = this.options.catalog.getDir();
16872
17086
  if (dir === null) {
16873
- this.lastError = "La copia no est\xE1 cargada.";
17087
+ this.lastError = serverText("vaultNotLoaded");
16874
17088
  this.emit();
16875
17089
  return null;
16876
17090
  }
@@ -16927,7 +17141,7 @@ var VaultWriter = class {
16927
17141
  try {
16928
17142
  listener();
16929
17143
  } catch (error) {
16930
- this.log.warn("[copia] un oyente del escritor lanzo:", error);
17144
+ this.log.warn("[vault] a writer listener threw:", error);
16931
17145
  }
16932
17146
  }
16933
17147
  }
@@ -17050,14 +17264,14 @@ var VaultWriter = class {
17050
17264
  const message = messageOf2(error);
17051
17265
  if (isNoSpace(error)) {
17052
17266
  report.aborted = message;
17053
- thrown = `Disco lleno: la pasada se corto (${message})`;
17054
- this.log.warn(`[copia] ${thrown}`);
17267
+ thrown = serverText("vaultDiskFull", { detail: message });
17268
+ this.log.warn(`[vault] disk full: the pass stopped (${message})`);
17055
17269
  break;
17056
17270
  }
17057
17271
  report.failed += 1;
17058
- addReason(report.failureReasons, message);
17059
- thrown = `${agent}/${sessionId}: ${message}`;
17060
- this.log.warn(`[copia] no se pudo copiar ${thrown}`);
17272
+ addReason(report.failureReasons, serverText("raw", { text: message }));
17273
+ thrown = serverText("vaultSessionFailed", { agent, session: sessionId, detail: message });
17274
+ this.log.warn(`[vault] couldn't copy ${agent}/${sessionId}: ${message}`);
17061
17275
  }
17062
17276
  this.setProgress(position + 1, candidates.length);
17063
17277
  await nextTurn2();
@@ -17071,8 +17285,8 @@ var VaultWriter = class {
17071
17285
  report.memoryBytes += copied.bytes;
17072
17286
  } catch (error) {
17073
17287
  const message = messageOf2(error);
17074
- thrown = `memoria de ${project.cwd}: ${message}`;
17075
- this.log.warn(`[copia] no se pudo copiar la ${thrown}`);
17288
+ thrown = serverText("vaultMemoryFailed", { cwd: project.cwd, detail: message });
17289
+ this.log.warn(`[vault] couldn't copy the memory of ${project.cwd}: ${message}`);
17076
17290
  if (isNoSpace(error)) {
17077
17291
  report.aborted = message;
17078
17292
  break;
@@ -17119,7 +17333,7 @@ var VaultWriter = class {
17119
17333
  if (catalog.hasForeignFormat(agent, sessionId)) {
17120
17334
  if (!this.warnedForeign.has(key)) {
17121
17335
  this.warnedForeign.add(key);
17122
- this.log.warn(`[copia] ${key}: la copia en disco es de un formato mas nuevo; no se pisa`);
17336
+ this.log.warn(`[vault] ${key}: the copy on disk is in a newer format; it isn't overwritten`);
17123
17337
  }
17124
17338
  return { kind: "foreign" };
17125
17339
  }
@@ -17167,7 +17381,7 @@ var VaultWriter = class {
17167
17381
  const loaded = await whole.readImage(event.eventId, part.index, part.source);
17168
17382
  if (loaded !== null) images.set(keyOfImage, { kind: "loaded", data: Buffer.from(loaded.data, "base64") });
17169
17383
  } catch (error) {
17170
- this.log.warn(`[copia] ${whole.label}: no se pudo leer una imagen: ${messageOf2(error)}`);
17384
+ this.log.warn(`[vault] ${whole.label}: couldn't read an image: ${messageOf2(error)}`);
17171
17385
  }
17172
17386
  }
17173
17387
  }
@@ -17298,7 +17512,7 @@ var VaultWriter = class {
17298
17512
  row.images += read.serialized.assets.size;
17299
17513
  for (const bytes of read.serialized.assets.values()) row.imageBytes += bytes.length;
17300
17514
  } catch (error) {
17301
- failed(messageOf2(error));
17515
+ failed(serverText("raw", { text: messageOf2(error) }));
17302
17516
  }
17303
17517
  }
17304
17518
  /**
@@ -17346,11 +17560,7 @@ var VaultWriter = class {
17346
17560
 
17347
17561
  // packages/server/src/vault/service.ts
17348
17562
  var STATUS_INTERVAL_MS = 500;
17349
- var VaultError = class extends Error {
17350
- constructor(message) {
17351
- super(message);
17352
- this.name = "VaultError";
17353
- }
17563
+ var VaultError = class extends ServerTextError {
17354
17564
  };
17355
17565
  var REAL_TIMERS3 = {
17356
17566
  set: (run, ms) => {
@@ -17362,8 +17572,15 @@ var REAL_TIMERS3 = {
17362
17572
  };
17363
17573
  var nextTurn3 = () => new Promise((resolve) => setImmediate(resolve));
17364
17574
  var messageOf3 = (error) => error instanceof Error ? error.message : String(error);
17365
- function busyMessage(error) {
17366
- return `${error.message} Prob\xE1 de nuevo cuando termine.`;
17575
+ function busyText(error) {
17576
+ switch (error.activity) {
17577
+ case "measuring":
17578
+ return serverText("vaultBusyMeasuring");
17579
+ case "moving":
17580
+ return serverText("vaultBusyMoving");
17581
+ default:
17582
+ return serverText("vaultBusyWriting");
17583
+ }
17367
17584
  }
17368
17585
  async function collect(lines) {
17369
17586
  const out = [];
@@ -17475,12 +17692,12 @@ var VaultService = class {
17475
17692
  /** La pasada en seco (§7.4). Lanza `VaultError` si hay algo en curso o el indice no termino. */
17476
17693
  async measure() {
17477
17694
  if (this.options.index.getStatus().state !== "ready") {
17478
- throw new VaultError("Todav\xEDa se est\xE1 leyendo el historial. Med\xED cuando termine.");
17695
+ throw new VaultError(serverText("vaultIndexNotReady"));
17479
17696
  }
17480
17697
  try {
17481
17698
  return await this.writer.measure();
17482
17699
  } catch (error) {
17483
- if (error instanceof VaultBusyError) throw new VaultError(busyMessage(error));
17700
+ if (error instanceof VaultBusyError) throw new VaultError(busyText(error));
17484
17701
  throw error;
17485
17702
  }
17486
17703
  }
@@ -17509,7 +17726,7 @@ var VaultService = class {
17509
17726
  /** Muda la copia a donde esta parado un selector de carpetas de quien la pide. */
17510
17727
  async setDirFromPicker(pickers, pickerId) {
17511
17728
  const target = pickers.currentPath(pickerId);
17512
- if (target === null) throw new VaultError("Ese selector ya no est\xE1 abierto.");
17729
+ if (target === null) throw new VaultError(serverText("pickerClosed"));
17513
17730
  await this.setDir(target);
17514
17731
  }
17515
17732
  /**
@@ -17522,11 +17739,11 @@ var VaultService = class {
17522
17739
  const current = this.dir();
17523
17740
  const protectedDirs = [...agents.protectedDirs(), path44.join(this.options.homeDir ?? homedir12(), ".gemini")];
17524
17741
  const check = checkVaultTarget(current, target, { protectedDirs, platform });
17525
- if (check.kind === "refused") throw new VaultError(check.message);
17742
+ if (check.kind === "refused") throw new VaultError(check.text);
17526
17743
  if (check.kind === "same") return;
17527
17744
  try {
17528
17745
  await this.writer.exclusive("moving", async () => {
17529
- if (!await canWriteInto(target)) throw new VaultError("No se puede escribir en esa carpeta.");
17746
+ if (!await canWriteInto(target)) throw new VaultError(serverText("vaultCannotWrite"));
17530
17747
  const hadCopy = await stat26(vaultMarkerFile(current)).then(
17531
17748
  () => true,
17532
17749
  () => false
@@ -17538,9 +17755,9 @@ var VaultService = class {
17538
17755
  await catalog.load(this.dir());
17539
17756
  });
17540
17757
  } catch (error) {
17541
- if (error instanceof VaultBusyError) throw new VaultError(busyMessage(error));
17758
+ if (error instanceof VaultBusyError) throw new VaultError(busyText(error));
17542
17759
  if (error instanceof VaultError) throw error;
17543
- throw new VaultError(`No se pudo mudar la copia: ${messageOf3(error)}`);
17760
+ throw new VaultError(serverText("vaultMoveFailed", { detail: messageOf3(error) }));
17544
17761
  } finally {
17545
17762
  this.scheduleStatus();
17546
17763
  }
@@ -17565,7 +17782,7 @@ var VaultService = class {
17565
17782
  (info) => info.isDirectory(),
17566
17783
  () => false
17567
17784
  );
17568
- if (!isFolder) throw new VaultError("Todav\xEDa no hay copia.");
17785
+ if (!isFolder) throw new VaultError(serverText("vaultNoCopyYet"));
17569
17786
  this.openWithSystem(dir);
17570
17787
  }
17571
17788
  /**
@@ -17576,7 +17793,7 @@ var VaultService = class {
17576
17793
  this.refuseWhileMoving();
17577
17794
  const { catalog } = this.options;
17578
17795
  const header = catalog.header(agent, sessionId);
17579
- if (header === null) throw new VaultError("Esa sesi\xF3n no est\xE1 en la copia.");
17796
+ if (header === null) throw new VaultError(serverText("vaultSessionMissing"));
17580
17797
  const dir = this.activeDir();
17581
17798
  const lines = await collect(catalog.readBody(agent, sessionId));
17582
17799
  const text = renderSessionMarkdown(header, lines, {
@@ -17670,7 +17887,7 @@ var VaultService = class {
17670
17887
  async exportProject(projectKey) {
17671
17888
  this.refuseWhileMoving();
17672
17889
  const project = this.options.index.getProjects().find((candidate) => candidate.key === projectKey);
17673
- if (project === void 0) throw new VaultError("Ese proyecto ya no est\xE1 en la barra.");
17890
+ if (project === void 0) throw new VaultError(serverText("vaultProjectGone"));
17674
17891
  const folder = projectExportDirFor(this.activeDir(), project, this.options.platform);
17675
17892
  await mkdir15(folder, { recursive: true });
17676
17893
  const entries = [];
@@ -17681,7 +17898,7 @@ var VaultService = class {
17681
17898
  try {
17682
17899
  text = await this.renderForExport(summary);
17683
17900
  } catch (error) {
17684
- this.log.warn(`[copia] no se pudo exportar ${summary.agent}/${summary.sessionId}: ${messageOf3(error)}`);
17901
+ this.log.warn(`[vault] couldn't export ${summary.agent}/${summary.sessionId}: ${messageOf3(error)}`);
17685
17902
  text = null;
17686
17903
  }
17687
17904
  if (text === null) {
@@ -17709,7 +17926,7 @@ var VaultService = class {
17709
17926
  // ---- Internos ---------------------------------------------------------------
17710
17927
  refuseWhileMoving() {
17711
17928
  if (this.writer.snapshot().activity === "moving") {
17712
- throw new VaultError("La copia se est\xE1 mudando de carpeta. Prob\xE1 de nuevo cuando termine.");
17929
+ throw new VaultError(serverText("vaultMoving"));
17713
17930
  }
17714
17931
  }
17715
17932
  labelOf(agent) {
@@ -17831,7 +18048,7 @@ var VaultService = class {
17831
18048
  try {
17832
18049
  listener(status);
17833
18050
  } catch (error) {
17834
- this.log.warn("[copia] un oyente del estado lanzo:", error);
18051
+ this.log.warn("[vault] a status listener threw:", error);
17835
18052
  }
17836
18053
  }
17837
18054
  }
@@ -17884,8 +18101,8 @@ function attachTerminalSocket(options) {
17884
18101
  if (socket !== except && socket.readyState === socket.OPEN) socket.send(payload);
17885
18102
  }
17886
18103
  };
17887
- const sendError = (socket, code, message, detail, requestId) => {
17888
- const payload = { type: "error", code, message };
18104
+ const sendError = (socket, code, text, detail, requestId) => {
18105
+ const payload = { type: "error", code, text };
17889
18106
  if (detail !== void 0) payload.detail = detail;
17890
18107
  if (requestId !== void 0) payload.requestId = requestId;
17891
18108
  send(socket, payload);
@@ -17899,13 +18116,13 @@ function attachTerminalSocket(options) {
17899
18116
  if (descriptor === null || descriptor.kind !== "agent" || descriptor.agent === null) return null;
17900
18117
  return agents.get(descriptor.agent)?.adapter ?? null;
17901
18118
  };
17902
- const imageStyleFor = (socket, terminalId, unsupportedMessage) => {
18119
+ const imageStyleFor = (socket, terminalId, unsupported) => {
17903
18120
  if (registry.get(terminalId) === null) {
17904
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
18121
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
17905
18122
  return null;
17906
18123
  }
17907
18124
  const style = adapterOf(terminalId)?.input.imageReference ?? null;
17908
- if (style === null) sendError(socket, "agent-unsupported", unsupportedMessage);
18125
+ if (style === null) sendError(socket, "agent-unsupported", unsupported);
17909
18126
  return style;
17910
18127
  };
17911
18128
  const inputOf = (terminalId) => adapterOf(terminalId)?.input ?? PLAIN_INPUT;
@@ -17922,13 +18139,9 @@ function attachTerminalSocket(options) {
17922
18139
  const writeToTerminal = (socket, terminalId, data) => {
17923
18140
  if (registry.write(terminalId, data)) return true;
17924
18141
  if (registry.get(terminalId) === null) {
17925
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
18142
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
17926
18143
  } else {
17927
- sendError(
17928
- socket,
17929
- "terminal-asleep",
17930
- "Esta pesta\xF1a no tiene la CLI abierta. Abrila para escribirle al agente."
17931
- );
18144
+ sendError(socket, "terminal-asleep", serverText("terminalAsleep"));
17932
18145
  }
17933
18146
  return false;
17934
18147
  };
@@ -18034,12 +18247,12 @@ function attachTerminalSocket(options) {
18034
18247
  const stopVaultStatus = vault.onStatus((status) => broadcast({ type: "vault.status", status }));
18035
18248
  const sendVaultError = (socket, error) => {
18036
18249
  if (error instanceof VaultError) {
18037
- sendError(socket, "vault-failed", error.message);
18250
+ sendError(socket, "vault-failed", error.text);
18038
18251
  return;
18039
18252
  }
18040
18253
  const detail = error instanceof Error ? error.message : String(error);
18041
- console.warn("[copia] un pedido fallo:", detail);
18042
- sendError(socket, "vault-failed", "No se pudo completar la operaci\xF3n de la copia propia.", detail);
18254
+ console.warn("[vault] a request failed:", detail);
18255
+ sendError(socket, "vault-failed", serverText("vaultFailed"), detail);
18043
18256
  };
18044
18257
  const onUpgrade = (request, socket, head) => {
18045
18258
  let pathname;
@@ -18053,7 +18266,7 @@ function attachTerminalSocket(options) {
18053
18266
  if (rejection !== null) {
18054
18267
  socket.write("HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n");
18055
18268
  socket.destroy();
18056
- console.warn(`[seguridad] upgrade rechazado en ${WS_PATH}: ${rejection}`);
18269
+ console.warn(`[security] upgrade rejected on ${WS_PATH}: ${rejection}`);
18057
18270
  return;
18058
18271
  }
18059
18272
  wss.handleUpgrade(request, socket, head, (client) => {
@@ -18078,7 +18291,7 @@ function attachTerminalSocket(options) {
18078
18291
  };
18079
18292
  const sendPathError = (error, fallback) => {
18080
18293
  if (error instanceof InvalidPathError) {
18081
- sendError(socket, "invalid-path", error.message);
18294
+ sendError(socket, "invalid-path", error.text);
18082
18295
  return;
18083
18296
  }
18084
18297
  sendError(
@@ -18090,30 +18303,30 @@ function attachTerminalSocket(options) {
18090
18303
  };
18091
18304
  const sendNotesError = (error) => {
18092
18305
  if (error instanceof NotesError) {
18093
- sendError(socket, "notes-failed", error.message);
18306
+ sendError(socket, "notes-failed", error.text);
18094
18307
  return;
18095
18308
  }
18096
18309
  sendError(
18097
18310
  socket,
18098
18311
  "internal",
18099
- "No se pudo guardar la nota.",
18312
+ serverText("noteSaveFailed"),
18100
18313
  error instanceof Error ? error.message : String(error)
18101
18314
  );
18102
18315
  };
18103
18316
  const sendMemoryError = (error, requestId) => {
18104
18317
  if (error instanceof UnknownTerminalError) {
18105
- sendError(socket, "unknown-terminal", "La terminal ya no existe.", void 0, requestId);
18318
+ sendError(socket, "unknown-terminal", serverText("terminalGone"), void 0, requestId);
18106
18319
  return;
18107
18320
  }
18108
18321
  if (error instanceof InvalidPathError) {
18109
- sendError(socket, "invalid-path", error.message, void 0, requestId);
18322
+ sendError(socket, "invalid-path", error.text, void 0, requestId);
18110
18323
  return;
18111
18324
  }
18112
18325
  const detail = error instanceof Error ? error.message : String(error);
18113
18326
  sendError(
18114
18327
  socket,
18115
18328
  "memory-failed",
18116
- error instanceof MemoryBridgeError ? detail : `No se pudo completar la operaci\xF3n: ${detail}`,
18329
+ error instanceof MemoryBridgeError ? error.text : serverText("operationFailed", { detail }),
18117
18330
  detail,
18118
18331
  requestId
18119
18332
  );
@@ -18139,11 +18352,11 @@ function attachTerminalSocket(options) {
18139
18352
  const text = Array.isArray(raw) ? Buffer.concat(raw).toString("utf8") : Buffer.from(raw).toString("utf8");
18140
18353
  const message = parseClientMessage(text);
18141
18354
  if (message === null) {
18142
- debugLog("socket", `mensaje rechazado: ${text.slice(0, 160)}`);
18143
- sendError(socket, "bad-message", "Mensaje no reconocido por el protocolo.");
18355
+ debugLog("socket", `message rejected: ${text.slice(0, 160)}`);
18356
+ sendError(socket, "bad-message", serverText("badMessage"));
18144
18357
  return;
18145
18358
  }
18146
- if (message.type !== "input") debugLog("socket", `recibido ${message.type}`);
18359
+ if (message.type !== "input") debugLog("socket", `received ${message.type}`);
18147
18360
  switch (message.type) {
18148
18361
  case "input":
18149
18362
  writeToTerminal(socket, message.terminalId, message.data);
@@ -18162,15 +18375,24 @@ function attachTerminalSocket(options) {
18162
18375
  */
18163
18376
  case "agent.submit": {
18164
18377
  const { terminalId, text: text2, images } = message;
18378
+ const files = message.files ?? [];
18379
+ if (files.length > MAX_FILES_PER_SUBMIT) {
18380
+ sendError(
18381
+ socket,
18382
+ "submit-failed",
18383
+ serverText("filesPerMessage", { max: MAX_FILES_PER_SUBMIT })
18384
+ );
18385
+ break;
18386
+ }
18165
18387
  if (images.length > MAX_IMAGES_PER_SUBMIT) {
18166
18388
  sendError(
18167
18389
  socket,
18168
18390
  "submit-failed",
18169
- `Se pueden adjuntar hasta ${MAX_IMAGES_PER_SUBMIT} imagenes por mensaje.`
18391
+ serverText("imagesPerMessage", { max: MAX_IMAGES_PER_SUBMIT })
18170
18392
  );
18171
18393
  break;
18172
18394
  }
18173
- if (images.length > 0 && imageStyleFor(socket, terminalId, "Esta CLI no recibe imagenes desde el cuadro de escritura.") === null) {
18395
+ if (images.length > 0 && imageStyleFor(socket, terminalId, serverText("imagesUnsupported")) === null) {
18174
18396
  break;
18175
18397
  }
18176
18398
  const submitting = adapterOf(terminalId);
@@ -18202,7 +18424,7 @@ function attachTerminalSocket(options) {
18202
18424
  return !guardsPending || await conversations.checkWaitingFor(terminalId) === null;
18203
18425
  } : void 0;
18204
18426
  const input = inputOf(terminalId);
18205
- const interrupted = () => sendError(socket, "submit-failed", "El mensaje no se termino de mandar: lo corto la interrupcion.");
18427
+ const interrupted = () => sendError(socket, "submit-failed", serverText("submitInterrupted"));
18206
18428
  void writeQueue.enqueue(
18207
18429
  terminalId,
18208
18430
  async (lane) => {
@@ -18217,21 +18439,46 @@ function attachTerminalSocket(options) {
18217
18439
  sendError(
18218
18440
  socket,
18219
18441
  "submit-failed",
18220
- error instanceof PasteImageError ? detail : "No se pudo guardar la imagen pegada.",
18442
+ error instanceof PasteImageError ? error.text : serverText("pasteImageFailed"),
18221
18443
  detail
18222
18444
  );
18223
18445
  return;
18224
18446
  }
18225
- const pieces = buildSubmissionWrites(text2, imagePaths, input, {
18447
+ const attachmentLines = [];
18448
+ try {
18449
+ for (const file of files) {
18450
+ const stored = await pasteStore.saveAttachment(terminalId, file.name, file.data);
18451
+ attachmentLines.push(
18452
+ attachmentLine(file.name, stored.bytes, attachmentReference(stored.path, input.transcriptReference))
18453
+ );
18454
+ }
18455
+ } catch (error) {
18456
+ const detail = error instanceof Error ? error.message : String(error);
18457
+ sendError(
18458
+ socket,
18459
+ "submit-failed",
18460
+ error instanceof PasteFileError ? error.text : serverText("attachmentSaveFailed"),
18461
+ detail
18462
+ );
18463
+ return;
18464
+ }
18465
+ const fullText = textWithAttachments(text2, attachmentLines);
18466
+ const pieces = buildSubmissionWrites(fullText, imagePaths, input, {
18226
18467
  send: message.send !== false
18227
18468
  });
18228
18469
  if (pieces === null) return;
18229
- registry.noteSubmitted(terminalId, text2);
18470
+ registry.noteSubmitted(terminalId, fullText);
18471
+ const startAfterMs = submitStartDelayMs(
18472
+ registry.launchedAtOf(terminalId),
18473
+ Date.now(),
18474
+ input.readyAfterLaunchMs ?? 0
18475
+ );
18230
18476
  const outcome = await lane.writePieces(
18231
18477
  pieces,
18232
18478
  input.pieceGapMs,
18233
18479
  pieceWriter(socket, terminalId),
18234
- approvalGuard
18480
+ approvalGuard,
18481
+ { startAfterMs }
18235
18482
  );
18236
18483
  if (outcome === "interrupted") interrupted();
18237
18484
  if (outcome === "blocked") {
@@ -18239,7 +18486,7 @@ function attachTerminalSocket(options) {
18239
18486
  sendError(
18240
18487
  socket,
18241
18488
  "submit-failed",
18242
- waitingNow ? `El mensaje no se termino de mandar: ${submitting?.label ?? "la CLI"} empezo a esperar una respuesta mientras se escribia. Contestala antes de mandar otro mensaje.` : `El mensaje no se termino de mandar: ${submitting?.label ?? "la CLI"} abrio una herramienta mientras se escribia y puede estar pidiendo una aprobacion. Revisa la solapa CLI.`
18489
+ waitingNow ? submitting === null ? serverText("submitStoppedWaitingNoLabel") : serverText("submitStoppedWaiting", { label: submitting.label }) : submitting === null ? serverText("submitStoppedToolNoLabel") : serverText("submitStoppedTool", { label: submitting.label })
18243
18490
  );
18244
18491
  }
18245
18492
  },
@@ -18263,7 +18510,7 @@ function attachTerminalSocket(options) {
18263
18510
  sendError(
18264
18511
  socket,
18265
18512
  "agent-unsupported",
18266
- "Esta CLI no recibe respuestas desde la conversacion. Contestala en la solapa CLI."
18513
+ serverText("answersUnsupported")
18267
18514
  );
18268
18515
  break;
18269
18516
  }
@@ -18271,36 +18518,36 @@ function attachTerminalSocket(options) {
18271
18518
  if (channel !== void 0) {
18272
18519
  const descriptor = registry.get(message.terminalId);
18273
18520
  if (descriptor === null) {
18274
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
18521
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18275
18522
  break;
18276
18523
  }
18277
18524
  const { terminalId, toolUseId, selections } = message;
18278
18525
  void (async () => {
18279
18526
  try {
18280
- const failure = answerFailureMessage(
18527
+ const failure = answerFailureText(
18281
18528
  await channel.answer({ cwd: descriptor.cwd, sessionId: descriptor.sessionId }, toolUseId, selections)
18282
18529
  );
18283
18530
  if (failure !== null) sendError(socket, "answer-failed", failure);
18284
18531
  } catch (error) {
18285
18532
  const detail = error instanceof Error ? error.message : String(error);
18286
- debugLog("socket", `respuesta por API en ${terminalId.slice(0, 8)}: ${error instanceof Error ? error.name : "error"}`);
18533
+ debugLog("socket", `API answer in ${terminalId.slice(0, 8)}: ${error instanceof Error ? error.name : "error"}`);
18287
18534
  sendError(
18288
18535
  socket,
18289
18536
  "answer-failed",
18290
- "No se pudo mandar la respuesta: contestala en la solapa CLI.",
18537
+ serverText("answerSendFailed"),
18291
18538
  detail
18292
18539
  );
18293
18540
  }
18294
18541
  })();
18295
18542
  break;
18296
18543
  }
18297
- const interruptedAnswer = () => sendError(socket, "answer-failed", "La respuesta no se termino de mandar: la corto la interrupcion.");
18544
+ const interruptedAnswer = () => sendError(socket, "answer-failed", serverText("answerInterrupted"));
18298
18545
  void writeQueue.enqueue(
18299
18546
  message.terminalId,
18300
18547
  async (lane) => {
18301
18548
  const pending = conversations.getPendingQuestion(message.terminalId);
18302
18549
  if (pending === null || pending.toolUseId !== message.toolUseId) {
18303
- sendError(socket, "answer-failed", ANSWER_NOT_PENDING_MESSAGE);
18550
+ sendError(socket, "answer-failed", ANSWER_NOT_PENDING_TEXT);
18304
18551
  return;
18305
18552
  }
18306
18553
  const keys = buildAnswerKeys(
@@ -18311,7 +18558,7 @@ function attachTerminalSocket(options) {
18311
18558
  message.selections
18312
18559
  );
18313
18560
  if (keys === null) {
18314
- sendError(socket, "answer-failed", ANSWER_INVALID_MESSAGE);
18561
+ sendError(socket, "answer-failed", ANSWER_INVALID_TEXT);
18315
18562
  return;
18316
18563
  }
18317
18564
  const outcome = await lane.writePieces(
@@ -18340,12 +18587,12 @@ function attachTerminalSocket(options) {
18340
18587
  */
18341
18588
  case "agent.mode": {
18342
18589
  if (registry.get(message.terminalId) === null) {
18343
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
18590
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18344
18591
  break;
18345
18592
  }
18346
18593
  const cycle = adapterOf(message.terminalId)?.capabilities.permissionCycle ?? null;
18347
18594
  if (cycle === null) {
18348
- sendError(socket, "agent-unsupported", "Esta CLI no tiene modos de permiso que cambiar desde aca.");
18595
+ sendError(socket, "agent-unsupported", serverText("modesUnsupported"));
18349
18596
  break;
18350
18597
  }
18351
18598
  const current = conversations.getPermissionMode(message.terminalId) ?? cycle.launchMode;
@@ -18357,7 +18604,7 @@ function attachTerminalSocket(options) {
18357
18604
  });
18358
18605
  if (plan.kind === "none") break;
18359
18606
  if (plan.kind === "refused") {
18360
- sendError(socket, "mode-failed", plan.message);
18607
+ sendError(socket, "mode-failed", plan.text);
18361
18608
  break;
18362
18609
  }
18363
18610
  const { keys } = plan;
@@ -18391,7 +18638,7 @@ function attachTerminalSocket(options) {
18391
18638
  sendError(
18392
18639
  socket,
18393
18640
  "archive-failed",
18394
- "Las sesiones con una pesta\xF1a abierta no se archivan. Cerrala primero."
18641
+ serverText("archiveOpenTab")
18395
18642
  );
18396
18643
  }
18397
18644
  break;
@@ -18426,7 +18673,7 @@ function attachTerminalSocket(options) {
18426
18673
  sendError(
18427
18674
  socket,
18428
18675
  "agent-unsupported",
18429
- "Este servidor no sabe lanzar esa CLI.",
18676
+ serverText("agentUnknown"),
18430
18677
  message.unsupportedAgent,
18431
18678
  message.requestId
18432
18679
  );
@@ -18448,12 +18695,12 @@ function attachTerminalSocket(options) {
18448
18695
  });
18449
18696
  } catch (error) {
18450
18697
  if (error instanceof TerminalOpenError) {
18451
- sendError(socket, error.code, error.message, error.detail, message.requestId);
18698
+ sendError(socket, error.code, error.text, error.detail, message.requestId);
18452
18699
  } else {
18453
18700
  sendError(
18454
18701
  socket,
18455
18702
  "internal",
18456
- "No se pudo abrir la pestana.",
18703
+ serverText("tabOpenFailed"),
18457
18704
  error instanceof Error ? error.message : String(error),
18458
18705
  message.requestId
18459
18706
  );
@@ -18471,16 +18718,16 @@ function attachTerminalSocket(options) {
18471
18718
  try {
18472
18719
  const descriptor = await registry.wake(message.terminalId);
18473
18720
  if (descriptor === null && !existed) {
18474
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
18721
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18475
18722
  }
18476
18723
  } catch (error) {
18477
18724
  if (error instanceof TerminalOpenError) {
18478
- sendError(socket, error.code, error.message, error.detail);
18725
+ sendError(socket, error.code, error.text, error.detail);
18479
18726
  } else {
18480
18727
  sendError(
18481
18728
  socket,
18482
18729
  "internal",
18483
- "No se pudo abrir la CLI de la pesta\xF1a.",
18730
+ serverText("cliOpenFailed"),
18484
18731
  error instanceof Error ? error.message : String(error)
18485
18732
  );
18486
18733
  }
@@ -18490,7 +18737,7 @@ function attachTerminalSocket(options) {
18490
18737
  }
18491
18738
  case "terminal.close":
18492
18739
  if (!dropTab(message.terminalId)) {
18493
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
18740
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18494
18741
  } else {
18495
18742
  subscriptions.delete(message.terminalId);
18496
18743
  gitSubscriptions.delete(message.terminalId);
@@ -18500,7 +18747,7 @@ function attachTerminalSocket(options) {
18500
18747
  case "terminal.attach": {
18501
18748
  const attached = registry.attach(message.terminalId, listener);
18502
18749
  if (attached === null) {
18503
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
18750
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18504
18751
  break;
18505
18752
  }
18506
18753
  send(socket, {
@@ -18517,7 +18764,7 @@ function attachTerminalSocket(options) {
18517
18764
  break;
18518
18765
  case "terminal.rename":
18519
18766
  if (!registry.rename(message.terminalId, message.label)) {
18520
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
18767
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18521
18768
  }
18522
18769
  break;
18523
18770
  case "tabs.reorder":
@@ -18547,7 +18794,7 @@ function attachTerminalSocket(options) {
18547
18794
  }
18548
18795
  const snapshot = await conversations.subscribe(message.terminalId);
18549
18796
  if (snapshot === null) {
18550
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
18797
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18551
18798
  return;
18552
18799
  }
18553
18800
  subscriptions.add(message.terminalId);
@@ -18581,7 +18828,7 @@ function attachTerminalSocket(options) {
18581
18828
  void (async () => {
18582
18829
  const plan = await conversations.readPlan(message.terminalId, message.fileName).catch(() => null);
18583
18830
  if (plan === null) {
18584
- sendError(socket, "read-failed", "No se pudo leer el plan.");
18831
+ sendError(socket, "read-failed", serverText("planReadFailed"));
18585
18832
  return;
18586
18833
  }
18587
18834
  send(socket, { type: "plans.content", terminalId: message.terminalId, plan });
@@ -18618,7 +18865,7 @@ function attachTerminalSocket(options) {
18618
18865
  message.limit
18619
18866
  );
18620
18867
  if (page === null) {
18621
- sendError(socket, "unknown-terminal", "Esa conversacion ya no se esta siguiendo.");
18868
+ sendError(socket, "unknown-terminal", serverText("conversationNotFollowed"));
18622
18869
  break;
18623
18870
  }
18624
18871
  send(socket, {
@@ -18637,7 +18884,7 @@ function attachTerminalSocket(options) {
18637
18884
  }
18638
18885
  const status = await repos.subscribe(message.terminalId);
18639
18886
  if (status === null) {
18640
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
18887
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18641
18888
  return;
18642
18889
  }
18643
18890
  gitSubscriptions.add(message.terminalId);
@@ -18733,7 +18980,7 @@ function attachTerminalSocket(options) {
18733
18980
  case "memory.read":
18734
18981
  void memory.read(message.terminalId, message.name).then((note) => {
18735
18982
  if (note === null) {
18736
- sendError(socket, "memory-failed", "Esa nota ya no existe.", void 0, message.requestId);
18983
+ sendError(socket, "memory-failed", serverText("memoryNoteGone"), void 0, message.requestId);
18737
18984
  return;
18738
18985
  }
18739
18986
  send(
@@ -18749,7 +18996,7 @@ function attachTerminalSocket(options) {
18749
18996
  void (async () => {
18750
18997
  const status = repos.getStatus(message.terminalId);
18751
18998
  if (status === null || status.state !== "ready") {
18752
- sendError(socket, "read-failed", "Todavia no se conoce el estado del repositorio.");
18999
+ sendError(socket, "read-failed", serverText("gitStatusUnknown"));
18753
19000
  return;
18754
19001
  }
18755
19002
  try {
@@ -18763,7 +19010,7 @@ function attachTerminalSocket(options) {
18763
19010
  });
18764
19011
  send(socket, { type: "git.diff", terminalId: message.terminalId, diff });
18765
19012
  } catch (error) {
18766
- sendPathError(error, "No se pudo leer el diff.");
19013
+ sendPathError(error, serverText("diffReadFailed"));
18767
19014
  }
18768
19015
  })();
18769
19016
  break;
@@ -18771,7 +19018,7 @@ function attachTerminalSocket(options) {
18771
19018
  void (async () => {
18772
19019
  const cwd = cwdOf(message.terminalId);
18773
19020
  if (cwd === null) {
18774
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
19021
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18775
19022
  return;
18776
19023
  }
18777
19024
  try {
@@ -18780,7 +19027,7 @@ function attachTerminalSocket(options) {
18780
19027
  });
18781
19028
  send(socket, { type: "files.listing", terminalId: message.terminalId, listing });
18782
19029
  } catch (error) {
18783
- sendPathError(error, "No se pudo leer el directorio.");
19030
+ sendPathError(error, serverText("dirReadFailed"));
18784
19031
  }
18785
19032
  })();
18786
19033
  break;
@@ -18794,7 +19041,7 @@ function attachTerminalSocket(options) {
18794
19041
  void (async () => {
18795
19042
  const cwd = cwdOf(message.terminalId);
18796
19043
  if (cwd === null) {
18797
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
19044
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18798
19045
  return;
18799
19046
  }
18800
19047
  try {
@@ -18803,7 +19050,7 @@ function attachTerminalSocket(options) {
18803
19050
  });
18804
19051
  send(socket, { type: "files.results", terminalId: message.terminalId, result });
18805
19052
  } catch (error) {
18806
- sendPathError(error, "No se pudo buscar en el directorio.");
19053
+ sendPathError(error, serverText("dirSearchFailed"));
18807
19054
  }
18808
19055
  })();
18809
19056
  break;
@@ -18811,14 +19058,14 @@ function attachTerminalSocket(options) {
18811
19058
  void (async () => {
18812
19059
  const cwd = cwdOf(message.terminalId);
18813
19060
  if (cwd === null) {
18814
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
19061
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18815
19062
  return;
18816
19063
  }
18817
19064
  try {
18818
19065
  const preview = await readPreview(cwd, message.path);
18819
19066
  send(socket, { type: "files.preview", terminalId: message.terminalId, preview });
18820
19067
  } catch (error) {
18821
- sendPathError(error, "No se pudo leer el archivo.");
19068
+ sendPathError(error, serverText("fileReadFailed"));
18822
19069
  }
18823
19070
  })();
18824
19071
  break;
@@ -18826,13 +19073,13 @@ function attachTerminalSocket(options) {
18826
19073
  void (async () => {
18827
19074
  const cwd = cwdOf(message.terminalId);
18828
19075
  if (cwd === null) {
18829
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
19076
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18830
19077
  return;
18831
19078
  }
18832
19079
  try {
18833
19080
  revealPath(await resolveInside(cwd, message.path, { mustExist: true }));
18834
19081
  } catch (error) {
18835
- sendPathError(error, "No se pudo abrir la ruta.");
19082
+ sendPathError(error, serverText("pathOpenFailed"));
18836
19083
  }
18837
19084
  })();
18838
19085
  break;
@@ -18918,7 +19165,7 @@ function attachTerminalSocket(options) {
18918
19165
  sendError(
18919
19166
  socket,
18920
19167
  "picker-failed",
18921
- error instanceof DirectoryPickerError ? error.message : "No se pudo listar esa carpeta.",
19168
+ error instanceof DirectoryPickerError ? error.text : serverText("pickerListFailed"),
18922
19169
  error instanceof Error ? error.message : String(error)
18923
19170
  );
18924
19171
  }
@@ -18958,20 +19205,20 @@ function attachTerminalSocket(options) {
18958
19205
  void (async () => {
18959
19206
  const descriptor = registry.get(message.terminalId);
18960
19207
  if (descriptor === null) {
18961
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
19208
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18962
19209
  return;
18963
19210
  }
18964
19211
  if (descriptor.kind !== "agent" || descriptor.sessionId.length === 0) {
18965
- sendError(socket, "submit-failed", "Esa pestana no tiene un agente al que mandarle la nota.");
19212
+ sendError(socket, "submit-failed", serverText("noteNoAgent"));
18966
19213
  return;
18967
19214
  }
18968
19215
  const note = await notes.readForSubmit(message.noteId);
18969
19216
  if (note === null) {
18970
- sendError(socket, "submit-failed", "La nota ya no existe.");
19217
+ sendError(socket, "submit-failed", serverText("noteGone"));
18971
19218
  return;
18972
19219
  }
18973
19220
  if (note.text.trim().length === 0 && note.images.length === 0) {
18974
- sendError(socket, "submit-failed", "La nota esta vacia.");
19221
+ sendError(socket, "submit-failed", serverText("noteEmpty"));
18975
19222
  return;
18976
19223
  }
18977
19224
  const outcome = await deliverWhenReady(deliverDeps, {
@@ -18988,26 +19235,26 @@ function attachTerminalSocket(options) {
18988
19235
  case "refused":
18989
19236
  return;
18990
19237
  case "gone":
18991
- sendError(socket, "unknown-terminal", "La terminal ya no existe.");
19238
+ sendError(socket, "unknown-terminal", serverText("terminalGone"));
18992
19239
  return;
18993
19240
  case "no-agent":
18994
19241
  case "no-session":
18995
- sendError(socket, "submit-failed", "Esa pestana no tiene un agente al que mandarle la nota.");
19242
+ sendError(socket, "submit-failed", serverText("noteNoAgent"));
18996
19243
  return;
18997
19244
  case "no-ready-signal":
18998
- sendError(socket, "agent-unsupported", "Esta CLI no avisa cuando esta lista; la nota no se mando.");
19245
+ sendError(socket, "agent-unsupported", serverText("noteNoReadySignal"));
18999
19246
  return;
19000
19247
  case "no-images":
19001
- sendError(socket, "agent-unsupported", "Esta CLI no recibe imagenes; la nota no se mando.");
19248
+ sendError(socket, "agent-unsupported", serverText("noteImagesUnsupported"));
19002
19249
  return;
19003
19250
  case "not-ready":
19004
- sendError(socket, "submit-failed", "La CLI de esa pestana no llego a arrancar; la nota no se mando.");
19251
+ sendError(socket, "submit-failed", serverText("noteCliNotStarted"));
19005
19252
  return;
19006
19253
  case "image-failed":
19007
- sendError(socket, "submit-failed", "No se pudo adjuntar una imagen de la nota.", outcome.detail);
19254
+ sendError(socket, "submit-failed", serverText("noteImageFailed"), outcome.detail);
19008
19255
  return;
19009
19256
  case "interrupted":
19010
- sendError(socket, "submit-failed", "La nota no se termino de mandar: la corto la interrupcion.");
19257
+ sendError(socket, "submit-failed", serverText("noteInterrupted"));
19011
19258
  return;
19012
19259
  }
19013
19260
  })();
@@ -19031,7 +19278,7 @@ function attachTerminalSocket(options) {
19031
19278
  sendError(
19032
19279
  socket,
19033
19280
  "agent-unsupported",
19034
- "Este servidor no sabe lanzar esa CLI.",
19281
+ serverText("agentUnknown"),
19035
19282
  message.unsupportedAgent,
19036
19283
  message.requestId
19037
19284
  );
@@ -19041,19 +19288,24 @@ function attachTerminalSocket(options) {
19041
19288
  void (async () => {
19042
19289
  let outcome;
19043
19290
  try {
19044
- outcome = await continueSession(continueDeps, { agent: message.agent, sessionId: message.sessionId, target });
19291
+ outcome = await continueSession(continueDeps, {
19292
+ agent: message.agent,
19293
+ sessionId: message.sessionId,
19294
+ target,
19295
+ ...message.label !== void 0 ? { label: message.label } : {}
19296
+ });
19045
19297
  } catch (error) {
19046
19298
  sendError(
19047
19299
  socket,
19048
19300
  "continue-failed",
19049
- "No se pudo armar la continuaci\xF3n.",
19301
+ serverText("continueFailed"),
19050
19302
  error instanceof Error ? error.message : String(error),
19051
19303
  requestId
19052
19304
  );
19053
19305
  return;
19054
19306
  }
19055
19307
  if (!outcome.ok) {
19056
- sendError(socket, outcome.code, outcome.message, outcome.detail, requestId);
19308
+ sendError(socket, outcome.code, outcome.text, outcome.detail, requestId);
19057
19309
  return;
19058
19310
  }
19059
19311
  const { terminalId } = outcome.descriptor;
@@ -19073,7 +19325,7 @@ function attachTerminalSocket(options) {
19073
19325
  });
19074
19326
  const reason2 = prefillReasonFor(delivered);
19075
19327
  if (reason2 !== null) {
19076
- debugLog("continuar", `no se mando sola a ${terminalId.slice(0, 8)}: ${delivered.kind}`);
19328
+ debugLog("continue", `not sent on its own to ${terminalId.slice(0, 8)}: ${delivered.kind}`);
19077
19329
  send(socket, { type: "composer.prefill", terminalId, text: outcome.message, reason: reason2 });
19078
19330
  }
19079
19331
  })();
@@ -19104,7 +19356,7 @@ function attachTerminalSocket(options) {
19104
19356
  sendError(
19105
19357
  socket,
19106
19358
  "search-failed",
19107
- error instanceof VaultError ? error.message : "No se pudo buscar en la copia propia.",
19359
+ error instanceof VaultError ? error.text : serverText("searchFailed"),
19108
19360
  detail,
19109
19361
  searchId
19110
19362
  );
@@ -19392,7 +19644,7 @@ var VaultCatalog = class {
19392
19644
  try {
19393
19645
  listener();
19394
19646
  } catch (error) {
19395
- console.warn("[copia] un oyente del catalogo lanzo:", error);
19647
+ console.warn("[vault] a catalog listener threw:", error);
19396
19648
  }
19397
19649
  }
19398
19650
  }
@@ -19411,15 +19663,18 @@ function resolveDefaultCwd() {
19411
19663
  if (isPackaged) return process.cwd();
19412
19664
  return repoRoot;
19413
19665
  }
19666
+ var ORIGIN_REFUSED_TEXT = "Origin not allowed.\nOrigen no permitido.";
19667
+ var TOKEN_MISSING_TEXT = "The session token is missing. Open the URL the server printed at startup.\nFalta el token de sesi\xF3n. Abre la URL que imprimi\xF3 el servidor al arrancar.";
19668
+ var NOT_FOUND_TEXT = "Not found.\nNo encontrado.";
19414
19669
  function createAuthMiddleware(port, token) {
19415
19670
  return (request, response, next) => {
19416
19671
  if (!hasValidHost(request, port) || !hasValidOrigin(request, port)) {
19417
- response.status(403).type("text/plain").send("Origen no permitido.");
19672
+ response.status(403).type("text/plain").send(ORIGIN_REFUSED_TEXT);
19418
19673
  return;
19419
19674
  }
19420
19675
  const match = matchToken(request, token);
19421
19676
  if (match === null) {
19422
- response.status(401).type("text/plain").send("Falta el token de sesion. Abri la URL que imprimio el servidor al arrancar.");
19677
+ response.status(401).type("text/plain").send(TOKEN_MISSING_TEXT);
19423
19678
  return;
19424
19679
  }
19425
19680
  if (match.source === "query") {
@@ -19443,14 +19698,14 @@ function mountBuiltUi(app) {
19443
19698
  const indexHtml = path46.join(webDist, "index.html");
19444
19699
  if (!existsSync4(indexHtml)) {
19445
19700
  throw new Error(
19446
- `No hay interfaz compilada en ${webDist}.
19447
- Corre "pnpm build" antes de "pnpm start", o usa "pnpm dev".`
19701
+ `There's no compiled interface in ${webDist}.
19702
+ Run "pnpm build" before "pnpm start", or use "pnpm dev".`
19448
19703
  );
19449
19704
  }
19450
19705
  app.use(express.static(webDist, { index: false, maxAge: 0 }));
19451
19706
  app.get(/.*/, (request, response) => {
19452
19707
  if (LOOKS_LIKE_FILE.test(request.path)) {
19453
- response.status(404).type("text/plain").send("No encontrado.");
19708
+ response.status(404).type("text/plain").send(NOT_FOUND_TEXT);
19454
19709
  return;
19455
19710
  }
19456
19711
  response.sendFile(indexHtml);
@@ -19465,8 +19720,8 @@ ${line}`);
19465
19720
  console.log(" Agent Workbench");
19466
19721
  console.log(line);
19467
19722
  console.log(` URL ${url}`);
19468
- console.log(` Modo ${isProduction ? "produccion (interfaz compilada)" : "desarrollo"}`);
19469
- console.log(` Directorio ${cwd}`);
19723
+ console.log(` Mode ${isProduction ? "production (compiled interface)" : "development"}`);
19724
+ console.log(` Directory ${cwd}`);
19470
19725
  const startupAgents = agentList.map((info) => ({
19471
19726
  id: info.id,
19472
19727
  label: info.label,
@@ -19478,12 +19733,12 @@ ${line}`);
19478
19733
  statusLine: info.statusLine?.state ?? null
19479
19734
  }));
19480
19735
  for (const agentLine of startupAgentLines(startupAgents)) console.log(agentLine);
19481
- console.log(` Consola ${shell === null ? "no encontrada" : shell.file}`);
19736
+ console.log(` Console ${shell === null ? "not found" : shell.file}`);
19482
19737
  if (agentList.some((info) => info.environmentNotice === "child-session-marker")) {
19483
19738
  console.log("");
19484
- console.log(" Nota: este proceso heredo CLAUDE_CODE_CHILD_SESSION, que apaga el");
19485
- console.log(" guardado del historial. Se quita del entorno de las pestanas para");
19486
- console.log(" que el historial y la vista de conversacion funcionen igual.");
19739
+ console.log(" Note: this process inherited CLAUDE_CODE_CHILD_SESSION, which turns off");
19740
+ console.log(" saving the history. It is removed from the tabs' environment so the");
19741
+ console.log(" history and the conversation view work the same.");
19487
19742
  }
19488
19743
  console.log(`${line}
19489
19744
  `);
@@ -19494,7 +19749,7 @@ async function main() {
19494
19749
  const agents = createAgentRegistry();
19495
19750
  const [, shell] = await Promise.all([agents.locateAll(), locateShell()]);
19496
19751
  const movedFrom = await migrateLegacyConfigDir();
19497
- if (movedFrom !== null) console.log(`Configuracion movida de ${movedFrom} a ${appConfigDir()}`);
19752
+ if (movedFrom !== null) console.log(`Configuration moved from ${movedFrom} to ${appConfigDir()}`);
19498
19753
  await agents.prepareAll();
19499
19754
  const store = new WorkspaceStore();
19500
19755
  const archived = new ArchivedSessions();
@@ -19527,7 +19782,7 @@ async function main() {
19527
19782
  });
19528
19783
  const address = httpServer.address();
19529
19784
  if (address === null || typeof address === "string") {
19530
- throw new Error("No se pudo determinar el puerto asignado.");
19785
+ throw new Error("Couldn't determine the assigned port.");
19531
19786
  }
19532
19787
  const { port } = address;
19533
19788
  app.use(createAuthMiddleware(port, token));
@@ -19556,7 +19811,7 @@ async function main() {
19556
19811
  void store.load().then((state) => {
19557
19812
  if (state.tabs.length === 0 && state.foreignTabs.length === 0) return;
19558
19813
  if (agents.anyAvailable()) {
19559
- console.log(`Restaurando ${state.tabs.length} pestana(s) del arranque anterior...`);
19814
+ console.log(`Restoring ${state.tabs.length} tab(s) from the previous run...`);
19560
19815
  }
19561
19816
  return registry.restore(state);
19562
19817
  });
@@ -19568,7 +19823,7 @@ async function main() {
19568
19823
  const shutdown = () => {
19569
19824
  if (shuttingDown) return;
19570
19825
  shuttingDown = true;
19571
- console.log("\nCerrando Agent Workbench...");
19826
+ console.log("\nClosing Agent Workbench...");
19572
19827
  stopWatching();
19573
19828
  detachSocket();
19574
19829
  vault.dispose();
@@ -19587,7 +19842,7 @@ async function main() {
19587
19842
  }
19588
19843
  main().catch((error) => {
19589
19844
  const detail = error instanceof Error ? error.stack ?? error.message : typeof error === "object" && error !== null ? JSON.stringify(error, Object.getOwnPropertyNames(error)) : String(error);
19590
- console.error(`Agent Workbench no pudo arrancar:
19845
+ console.error(`Agent Workbench couldn't start:
19591
19846
  ${detail}`);
19592
19847
  process.exit(1);
19593
19848
  });