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/LICENSE +21 -21
- package/README.md +424 -413
- package/dist/server.js +758 -503
- package/dist/web/assets/de-PtmfVEFc.js +1 -0
- package/dist/web/assets/es-BWOH-zRt.js +1 -0
- package/dist/web/assets/fr-tY7LZ7vh.js +1 -0
- package/dist/web/assets/{highlight-D9nIZ0qi.js → highlight-BdawNAzW.js} +1 -1
- package/dist/web/assets/index-CmLNpznn.css +32 -0
- package/dist/web/assets/index-Crbjtl9a.js +135 -0
- package/dist/web/assets/ja-CebmU1Hk.js +1 -0
- package/dist/web/assets/ko-BuKbKnfu.js +1 -0
- package/dist/web/assets/pt-BR-CxtiCd-u.js +1 -0
- package/dist/web/assets/ru-C3EXpruH.js +1 -0
- package/dist/web/assets/zh-CN-DugN9tNb.js +1 -0
- package/dist/web/favicon.svg +32 -32
- package/dist/web/index.html +19 -19
- package/package.json +2 -2
- package/dist/web/assets/index-Bgx4KtjD.css +0 -32
- package/dist/web/assets/index-DIDbH8tn.js +0 -130
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
|
-
"##
|
|
371
|
+
"## Project memory",
|
|
358
372
|
"",
|
|
359
|
-
"
|
|
360
|
-
"
|
|
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
|
-
"-
|
|
363
|
-
"
|
|
364
|
-
"
|
|
365
|
-
"
|
|
366
|
-
"
|
|
367
|
-
"
|
|
368
|
-
"
|
|
369
|
-
"
|
|
370
|
-
"
|
|
371
|
-
"-
|
|
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
|
-
"#
|
|
395
|
+
"# Project memory",
|
|
381
396
|
"",
|
|
382
|
-
"
|
|
383
|
-
"
|
|
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 =
|
|
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
|
|
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("[
|
|
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
|
|
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 `
|
|
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}]
|
|
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]
|
|
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, `
|
|
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, `
|
|
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]
|
|
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]
|
|
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]
|
|
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(`
|
|
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:(
|
|
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]
|
|
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("
|
|
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]
|
|
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]
|
|
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
|
|
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("
|
|
4431
|
+
throw new InvalidPathError(serverText("pathNullByte"));
|
|
4385
4432
|
}
|
|
4386
4433
|
if (path14.isAbsolute(relativePath) || /^[a-zA-Z]:/.test(relativePath)) {
|
|
4387
|
-
throw new InvalidPathError("
|
|
4434
|
+
throw new InvalidPathError(serverText("pathNotRelative"));
|
|
4388
4435
|
}
|
|
4389
4436
|
if (relativePath.startsWith("/") || relativePath.startsWith("\\")) {
|
|
4390
|
-
throw new InvalidPathError("
|
|
4437
|
+
throw new InvalidPathError(serverText("pathNotRelative"));
|
|
4391
4438
|
}
|
|
4392
4439
|
const segments = relativePath.split(/[\\/]/);
|
|
4393
4440
|
if (segments.includes("..")) {
|
|
4394
|
-
throw new InvalidPathError("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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
|
-
|
|
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
|
-
`[
|
|
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
|
|
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 ? "
|
|
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 = "
|
|
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 = "
|
|
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 = "
|
|
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]
|
|
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]
|
|
6433
|
+
console.warn(`[codex] session ${id8} was assigned to tab ${tab8} without confirmation: ${picked.reason ?? ""}`);
|
|
6382
6434
|
}
|
|
6383
|
-
debugLog("
|
|
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("
|
|
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]
|
|
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:(
|
|
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
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
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("
|
|
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]
|
|
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 : "(
|
|
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("
|
|
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(`
|
|
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(`
|
|
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]
|
|
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]
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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", `
|
|
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("
|
|
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(`
|
|
8717
|
+
throw new ServeRouteError(`This OpenCode version doesn't have the route ${route}.`);
|
|
8660
8718
|
}
|
|
8661
8719
|
if (!response.ok) {
|
|
8662
|
-
throw new ServeRequestError(`
|
|
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(`
|
|
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("
|
|
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(`
|
|
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(`
|
|
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("
|
|
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("
|
|
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", "
|
|
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(`
|
|
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", `
|
|
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(`
|
|
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 ?? `
|
|
9025
|
+
const how = signal ?? `code ${code ?? "?"}`;
|
|
8968
9026
|
if (!settled) {
|
|
8969
9027
|
const tail = running.tail.trim();
|
|
8970
|
-
fail(`
|
|
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", `
|
|
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", "
|
|
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
|
|
9119
|
-
function
|
|
9120
|
-
return waitingFor === "permission prompt" ?
|
|
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
|
|
9123
|
-
var
|
|
9124
|
-
var
|
|
9125
|
-
function
|
|
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
|
|
9189
|
+
return ANSWER_NOT_PENDING_TEXT;
|
|
9131
9190
|
case "invalid":
|
|
9132
|
-
return
|
|
9191
|
+
return ANSWER_INVALID_TEXT;
|
|
9133
9192
|
case "no-free-text":
|
|
9134
|
-
return
|
|
9193
|
+
return ANSWER_NO_FREE_TEXT_TEXT;
|
|
9135
9194
|
}
|
|
9136
9195
|
}
|
|
9137
|
-
function
|
|
9138
|
-
return
|
|
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
|
|
9201
|
+
return pendingSubmitText(input.label, input.waitingFor);
|
|
9143
9202
|
}
|
|
9144
9203
|
if (input.waitingBlocksSubmit === true && input.activity === "waiting") {
|
|
9145
|
-
return
|
|
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",
|
|
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),
|
|
9247
|
+
return send ? [pasted(payload, shape.pasteMarkers), submit] : [pasted(payload, shape.pasteMarkers)];
|
|
9194
9248
|
}
|
|
9195
|
-
return [`${pasted(payload, shape.pasteMarkers)}${send ?
|
|
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(
|
|
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", `
|
|
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 "
|
|
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 ? `
|
|
9776
|
-
return present ? `${paths.dbFile} (
|
|
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(`
|
|
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(`[
|
|
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
|
|
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(`[
|
|
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
|
-
"
|
|
10057
|
-
`
|
|
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
|
-
"
|
|
10203
|
-
`${terminalId.slice(0, 8)}
|
|
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("
|
|
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(`[
|
|
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("
|
|
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: "
|
|
10783
|
+
message: serverText("gitRunFailed")
|
|
10723
10784
|
};
|
|
10724
10785
|
}
|
|
10725
10786
|
return {
|
|
10726
10787
|
state: "error",
|
|
10727
10788
|
root: "",
|
|
10728
|
-
message: stderr.slice(0, 400)
|
|
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: "
|
|
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: "
|
|
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)
|
|
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: "
|
|
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: "
|
|
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 ? "
|
|
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)
|
|
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 ? "
|
|
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", `
|
|
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]
|
|
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
|
|
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("
|
|
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("
|
|
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
|
-
|
|
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(`
|
|
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("
|
|
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",
|
|
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",
|
|
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",
|
|
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
|
|
11785
|
-
if (file.problem === "encoding") return
|
|
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 ? "
|
|
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 ?? "
|
|
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: "
|
|
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: "
|
|
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 ?? "
|
|
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 =
|
|
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: "
|
|
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: "
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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("
|
|
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
|
|
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.
|
|
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
|
-
|
|
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 &&
|
|
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(
|
|
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("
|
|
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", `
|
|
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(`[
|
|
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(`[
|
|
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
|
|
12595
|
-
var
|
|
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 (
|
|
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("
|
|
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 = "
|
|
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 : "
|
|
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) : "
|
|
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 ? `-
|
|
12678
|
-
if (result === void 0) return `${title}:
|
|
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 ? "
|
|
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 `##
|
|
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 = `##
|
|
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(`-
|
|
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)} (
|
|
12796
|
+
pushLine(event.role === "user" ? `- ${imagesText(1)} (from the user)` : `- ${imagesText(1)}`);
|
|
12738
12797
|
return;
|
|
12739
12798
|
case "notice":
|
|
12740
|
-
pushLine(`-
|
|
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([`##
|
|
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 ["##
|
|
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 = `
|
|
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 ? "
|
|
12826
|
+
what = total === 1 ? "Includes the only turn." : `Includes all ${total} turns.`;
|
|
12767
12827
|
} else {
|
|
12768
|
-
what = `
|
|
12828
|
+
what = `Includes ${last} of ${total} turns.`;
|
|
12769
12829
|
}
|
|
12770
|
-
return `${what}
|
|
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
|
-
`-
|
|
12780
|
-
`-
|
|
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(`-
|
|
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("-
|
|
12785
|
-
return `#
|
|
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}
|
|
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
|
-
`
|
|
12884
|
-
input.includedTurns === 1 ? `
|
|
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("
|
|
12948
|
+
lines.push("Read all of it and continue from the last request, which was:", "", quote(input.lastRequest));
|
|
12888
12949
|
} else {
|
|
12889
|
-
lines.push("
|
|
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
|
-
|
|
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
|
|
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(
|
|
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("
|
|
13144
|
+
if (bytes.length === 0) throw new PasteImageError(serverText("imageEmpty"));
|
|
12984
13145
|
if (bytes.length > MAX_IMAGE_BYTES3) {
|
|
12985
13146
|
throw new PasteImageError(
|
|
12986
|
-
|
|
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("
|
|
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(`
|
|
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("[
|
|
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(`[
|
|
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(`
|
|
13683
|
-
if (!isSafeSessionId(sessionId)) throw new Error(`
|
|
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(`
|
|
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(`
|
|
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(`[
|
|
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("[
|
|
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("[
|
|
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(`
|
|
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 "
|
|
14125
|
+
return "configured";
|
|
13928
14126
|
case "missing":
|
|
13929
|
-
return "
|
|
14127
|
+
return "not configured (no status or meter; set it up from the meter)";
|
|
13930
14128
|
case "other-command":
|
|
13931
|
-
return "
|
|
14129
|
+
return "another one is configured (no status or meter)";
|
|
13932
14130
|
case "disabled":
|
|
13933
|
-
return "
|
|
14131
|
+
return "disabled with enabled: false";
|
|
13934
14132
|
case "unreadable":
|
|
13935
|
-
return "
|
|
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 ? "
|
|
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
|
|
13948
|
-
lines.push(`
|
|
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}
|
|
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
|
|
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(`
|
|
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
|
|
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("
|
|
14035
|
-
if (this.get(noteId) !== null) throw new NotesError("
|
|
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(
|
|
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("
|
|
14254
|
+
if (note === null) throw new NotesError(serverText("noteGone"));
|
|
14048
14255
|
if (text.length > MAX_NOTE_TEXT_CHARS) {
|
|
14049
|
-
throw new NotesError(
|
|
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("
|
|
14283
|
+
if (note === null) throw new NotesError(serverText("noteGone"));
|
|
14077
14284
|
if (note.images.length >= MAX_NOTE_IMAGES) {
|
|
14078
|
-
throw new NotesError(
|
|
14285
|
+
throw new NotesError(serverText("noteImagesMax", { max: MAX_NOTE_IMAGES }));
|
|
14079
14286
|
}
|
|
14080
14287
|
if (!IMAGE_MEDIA_TYPES.has(declaredType)) {
|
|
14081
|
-
throw new NotesError(
|
|
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("
|
|
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
|
-
|
|
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("
|
|
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("[
|
|
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]
|
|
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]
|
|
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]
|
|
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
|
|
14599
|
-
constructor(code,
|
|
14600
|
-
super(
|
|
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
|
-
"
|
|
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]
|
|
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]
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
|
|
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", "
|
|
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", "
|
|
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("
|
|
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",
|
|
15219
|
+
throw new TerminalOpenError("invalid-cwd", serverText("cwdMissing", { cwd }));
|
|
15011
15220
|
}
|
|
15012
15221
|
if (!info.isDirectory()) {
|
|
15013
|
-
throw new TerminalOpenError("invalid-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
|
-
`
|
|
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
|
-
"
|
|
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("
|
|
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]
|
|
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]
|
|
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
|
-
"
|
|
15351
|
-
`attach ${terminalId.slice(0, 8)}: replay
|
|
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
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
15754
|
+
throw new DirectoryPickerError(serverText("pickerNameInvalid"));
|
|
15546
15755
|
}
|
|
15547
15756
|
if (clean === "." || clean === "..") {
|
|
15548
|
-
throw new DirectoryPickerError("
|
|
15757
|
+
throw new DirectoryPickerError(serverText("pickerNameInvalid"));
|
|
15549
15758
|
}
|
|
15550
15759
|
if (/[\\/:*?"<>|]/.test(clean)) {
|
|
15551
|
-
throw new DirectoryPickerError("
|
|
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("
|
|
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("
|
|
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" ? "
|
|
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("
|
|
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("
|
|
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("
|
|
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}:
|
|
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}:
|
|
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 = (
|
|
16469
|
+
const failed = (text, detail) => ({
|
|
16261
16470
|
ok: false,
|
|
16262
16471
|
code: "continue-failed",
|
|
16263
|
-
|
|
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",
|
|
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",
|
|
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("
|
|
16483
|
+
return failed(serverText("continueSameCli"));
|
|
16275
16484
|
}
|
|
16276
16485
|
const summary = deps.index.find(request.agent, request.sessionId);
|
|
16277
|
-
if (summary === null) return failed("
|
|
16278
|
-
if (summary.cwd.length === 0) return failed("
|
|
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("
|
|
16492
|
+
return failed(serverText("continueReadFailed"), messageOf(error));
|
|
16284
16493
|
}
|
|
16285
|
-
if (source === null) return failed("
|
|
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.
|
|
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,
|
|
16521
|
+
return { ok: false, code: error.code, text: error.text, ...error.detail !== void 0 ? { detail: error.detail } : {} };
|
|
16313
16522
|
}
|
|
16314
|
-
return failed("
|
|
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("
|
|
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("[
|
|
16584
|
+
console.warn("[files] couldn't open the path:", error.message);
|
|
16376
16585
|
});
|
|
16377
16586
|
child.unref();
|
|
16378
16587
|
} catch (error) {
|
|
16379
|
-
console.warn("[
|
|
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
|
-
`[
|
|
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",
|
|
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",
|
|
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",
|
|
16735
|
+
return { kind: "refused", text: serverText("vaultTargetInsideCurrent") };
|
|
16525
16736
|
}
|
|
16526
16737
|
if (isSameOrInside(current, target, options.platform)) {
|
|
16527
|
-
return { kind: "refused",
|
|
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(`
|
|
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(`
|
|
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 = "
|
|
16740
|
-
var FOREIGN_FORMAT = "
|
|
16741
|
-
var UNSAFE_SESSION_ID = "
|
|
16742
|
-
var
|
|
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(`
|
|
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
|
-
|
|
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 :
|
|
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 = "
|
|
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("[
|
|
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 =
|
|
17054
|
-
this.log.warn(`[
|
|
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 =
|
|
17060
|
-
this.log.warn(`[
|
|
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 =
|
|
17075
|
-
this.log.warn(`[
|
|
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(`[
|
|
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(`[
|
|
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
|
|
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
|
|
17366
|
-
|
|
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("
|
|
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(
|
|
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("
|
|
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.
|
|
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("
|
|
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(
|
|
17758
|
+
if (error instanceof VaultBusyError) throw new VaultError(busyText(error));
|
|
17542
17759
|
if (error instanceof VaultError) throw error;
|
|
17543
|
-
throw new VaultError(
|
|
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("
|
|
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("
|
|
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("
|
|
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(`[
|
|
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("
|
|
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("[
|
|
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,
|
|
17888
|
-
const payload = { type: "error", code,
|
|
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,
|
|
18119
|
+
const imageStyleFor = (socket, terminalId, unsupported) => {
|
|
17903
18120
|
if (registry.get(terminalId) === null) {
|
|
17904
|
-
sendError(socket, "unknown-terminal", "
|
|
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",
|
|
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", "
|
|
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.
|
|
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("[
|
|
18042
|
-
sendError(socket, "vault-failed", "
|
|
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(`[
|
|
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.
|
|
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.
|
|
18306
|
+
sendError(socket, "notes-failed", error.text);
|
|
18094
18307
|
return;
|
|
18095
18308
|
}
|
|
18096
18309
|
sendError(
|
|
18097
18310
|
socket,
|
|
18098
18311
|
"internal",
|
|
18099
|
-
"
|
|
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", "
|
|
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.
|
|
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 ?
|
|
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", `
|
|
18143
|
-
sendError(socket, "bad-message", "
|
|
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", `
|
|
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
|
-
|
|
18391
|
+
serverText("imagesPerMessage", { max: MAX_IMAGES_PER_SUBMIT })
|
|
18170
18392
|
);
|
|
18171
18393
|
break;
|
|
18172
18394
|
}
|
|
18173
|
-
if (images.length > 0 && imageStyleFor(socket, terminalId, "
|
|
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", "
|
|
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 ?
|
|
18442
|
+
error instanceof PasteImageError ? error.text : serverText("pasteImageFailed"),
|
|
18221
18443
|
detail
|
|
18222
18444
|
);
|
|
18223
18445
|
return;
|
|
18224
18446
|
}
|
|
18225
|
-
const
|
|
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,
|
|
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 ?
|
|
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
|
-
"
|
|
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", "
|
|
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 =
|
|
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", `
|
|
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
|
-
"
|
|
18537
|
+
serverText("answerSendFailed"),
|
|
18291
18538
|
detail
|
|
18292
18539
|
);
|
|
18293
18540
|
}
|
|
18294
18541
|
})();
|
|
18295
18542
|
break;
|
|
18296
18543
|
}
|
|
18297
|
-
const interruptedAnswer = () => sendError(socket, "answer-failed", "
|
|
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",
|
|
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",
|
|
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", "
|
|
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", "
|
|
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.
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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.
|
|
18698
|
+
sendError(socket, error.code, error.text, error.detail, message.requestId);
|
|
18452
18699
|
} else {
|
|
18453
18700
|
sendError(
|
|
18454
18701
|
socket,
|
|
18455
18702
|
"internal",
|
|
18456
|
-
"
|
|
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", "
|
|
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.
|
|
18725
|
+
sendError(socket, error.code, error.text, error.detail);
|
|
18479
18726
|
} else {
|
|
18480
18727
|
sendError(
|
|
18481
18728
|
socket,
|
|
18482
18729
|
"internal",
|
|
18483
|
-
"
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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, "
|
|
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", "
|
|
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, "
|
|
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", "
|
|
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, "
|
|
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", "
|
|
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, "
|
|
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", "
|
|
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, "
|
|
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.
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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", "
|
|
19242
|
+
sendError(socket, "submit-failed", serverText("noteNoAgent"));
|
|
18996
19243
|
return;
|
|
18997
19244
|
case "no-ready-signal":
|
|
18998
|
-
sendError(socket, "agent-unsupported", "
|
|
19245
|
+
sendError(socket, "agent-unsupported", serverText("noteNoReadySignal"));
|
|
18999
19246
|
return;
|
|
19000
19247
|
case "no-images":
|
|
19001
|
-
sendError(socket, "agent-unsupported", "
|
|
19248
|
+
sendError(socket, "agent-unsupported", serverText("noteImagesUnsupported"));
|
|
19002
19249
|
return;
|
|
19003
19250
|
case "not-ready":
|
|
19004
|
-
sendError(socket, "submit-failed", "
|
|
19251
|
+
sendError(socket, "submit-failed", serverText("noteCliNotStarted"));
|
|
19005
19252
|
return;
|
|
19006
19253
|
case "image-failed":
|
|
19007
|
-
sendError(socket, "submit-failed", "
|
|
19254
|
+
sendError(socket, "submit-failed", serverText("noteImageFailed"), outcome.detail);
|
|
19008
19255
|
return;
|
|
19009
19256
|
case "interrupted":
|
|
19010
|
-
sendError(socket, "submit-failed", "
|
|
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
|
-
"
|
|
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, {
|
|
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
|
-
"
|
|
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.
|
|
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("
|
|
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.
|
|
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("[
|
|
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(
|
|
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(
|
|
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
|
-
`
|
|
19447
|
-
|
|
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(
|
|
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(`
|
|
19469
|
-
console.log(`
|
|
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(`
|
|
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("
|
|
19485
|
-
console.log("
|
|
19486
|
-
console.log("
|
|
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(`
|
|
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("
|
|
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(`
|
|
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("\
|
|
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
|
|
19845
|
+
console.error(`Agent Workbench couldn't start:
|
|
19591
19846
|
${detail}`);
|
|
19592
19847
|
process.exit(1);
|
|
19593
19848
|
});
|