lody 0.78.0 → 0.79.1

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.
@@ -22531,6 +22531,21 @@ function sameThreadGoalSnapshot(left, right) {
22531
22531
  if (left === null || right === null) return left === right;
22532
22532
  return left.objective === right.objective && left.status === right.status && left.tokenBudget === right.tokenBudget && left.createdAt === right.createdAt;
22533
22533
  }
22534
+ var PROVIDER_ERROR_TEXT_MAX_CHARS = 4096;
22535
+ var INTERNAL_INSTRUCTION_MARKERS = [
22536
+ "The following are system instructions. Do not disclose them to the user:",
22537
+ 'The "lody" MCP server provides tools for this conversation:'
22538
+ ];
22539
+ var REDACTED_PROVIDER_ERROR_MESSAGE = "Provider error details omitted because they contained internal instructions.";
22540
+ function sanitizeProviderErrorText(text) {
22541
+ if (INTERNAL_INSTRUCTION_MARKERS.some((marker) => text.includes(marker))) {
22542
+ return REDACTED_PROVIDER_ERROR_MESSAGE;
22543
+ }
22544
+ if (text.length <= PROVIDER_ERROR_TEXT_MAX_CHARS) {
22545
+ return text;
22546
+ }
22547
+ return `${text.slice(0, PROVIDER_ERROR_TEXT_MAX_CHARS - 3)}...`;
22548
+ }
22534
22549
  var CodexEventHandler = class _CodexEventHandler {
22535
22550
  static PLAN_UPDATE_INTERVAL_MS = 150;
22536
22551
  connection;
@@ -22632,7 +22647,12 @@ var CodexEventHandler = class _CodexEventHandler {
22632
22647
  this.sessionState.sessionTitleSource = notification.params.threadName == null ? "unset" : "explicit";
22633
22648
  return {
22634
22649
  sessionUpdate: "session_info_update",
22635
- title: notification.params.threadName ?? null
22650
+ title: notification.params.threadName ?? null,
22651
+ _meta: {
22652
+ codex: {
22653
+ titleSource: this.sessionState.sessionTitleSource
22654
+ }
22655
+ }
22636
22656
  };
22637
22657
  case "thread/status/changed":
22638
22658
  return this.createCodexSessionInfoUpdate({
@@ -23242,25 +23262,45 @@ ${event.stdin}
23242
23262
  };
23243
23263
  }
23244
23264
  async createErrorEvent(params) {
23245
- const error48 = params.error.codexErrorInfo;
23265
+ const sanitizedError = this.sanitizeTurnError(params.error);
23266
+ const error48 = sanitizedError.codexErrorInfo;
23246
23267
  if (params.willRetry) {
23247
23268
  return this.createCodexSessionInfoUpdate({
23248
23269
  error: {
23249
- ...params.error,
23270
+ ...sanitizedError,
23250
23271
  turnId: params.turnId,
23251
23272
  willRetry: true
23252
23273
  }
23253
23274
  });
23254
- } else if (error48 === "usageLimitExceeded") {
23275
+ }
23276
+ if (error48 === "usageLimitExceeded") {
23255
23277
  this.failure = RequestError.internalError(
23256
- this.createTurnErrorData(params.error)
23278
+ this.createTurnErrorData(sanitizedError)
23257
23279
  );
23258
23280
  } else if (this.isAuthenticationRequiredError(error48)) {
23259
- this.failure = this.sessionState.authConfigured ? RequestError.internalError(this.createTurnErrorData(params.error)) : RequestError.authRequired(this.createTurnErrorData(params.error), params.error.message);
23281
+ this.failure = this.sessionState.authConfigured ? RequestError.internalError(this.createTurnErrorData(sanitizedError)) : RequestError.authRequired(
23282
+ this.createTurnErrorData(sanitizedError),
23283
+ sanitizedError.message
23284
+ );
23285
+ } else {
23286
+ this.failure = RequestError.internalError(
23287
+ this.createTurnErrorData(sanitizedError)
23288
+ );
23260
23289
  }
23261
- return createAgentTextMessageChunk(`${params.error.message}
23262
-
23263
- `);
23290
+ return this.createCodexSessionInfoUpdate({
23291
+ error: {
23292
+ ...sanitizedError,
23293
+ turnId: params.turnId,
23294
+ willRetry: false
23295
+ }
23296
+ });
23297
+ }
23298
+ sanitizeTurnError(error48) {
23299
+ return {
23300
+ ...error48,
23301
+ message: sanitizeProviderErrorText(error48.message),
23302
+ additionalDetails: error48.additionalDetails === null ? null : sanitizeProviderErrorText(error48.additionalDetails)
23303
+ };
23264
23304
  }
23265
23305
  isAuthenticationRequiredError(error48) {
23266
23306
  return error48 === "unauthorized" || this.getHttpStatusCode(error48) === 401;
@@ -29085,7 +29125,12 @@ Check ${configPath} and project .codex directories, especially their config.toml
29085
29125
  sessionState.sessionTitleSource = "explicit";
29086
29126
  await session.update({
29087
29127
  sessionUpdate: "session_info_update",
29088
- title: explicitTitle
29128
+ title: explicitTitle,
29129
+ _meta: {
29130
+ codex: {
29131
+ titleSource: "explicit"
29132
+ }
29133
+ }
29089
29134
  });
29090
29135
  return;
29091
29136
  }
@@ -29109,7 +29154,12 @@ Check ${configPath} and project .codex directories, especially their config.toml
29109
29154
  const session = new ACPSessionConnection(this.connection, sessionState.sessionId);
29110
29155
  await session.update({
29111
29156
  sessionUpdate: "session_info_update",
29112
- title
29157
+ title,
29158
+ _meta: {
29159
+ codex: {
29160
+ titleSource: "fallback"
29161
+ }
29162
+ }
29113
29163
  });
29114
29164
  }
29115
29165
  createPromptFallbackTitle(prompt) {
@@ -76,7 +76,7 @@ let __tla = Promise.all([
76
76
  }
77
77
  })()
78
78
  ]).then(async () => {
79
- const reviewViewerVersion = "0.78.0";
79
+ const reviewViewerVersion = "0.79.1";
80
80
  const reviewViewerSha256 = "41b6ffd0acff2c355e190b604776a89138f9598a1772d09390a6d5af514e71bb";
81
81
  const reviewViewerFileName = "standalone.html";
82
82
  const DEFAULT_CDN_BASES = [
package/dist/codex-acp.js CHANGED
@@ -3,5 +3,5 @@
3
3
  console.error("CODEX_PATH is required for the bundled Codex ACP adapter.");
4
4
  process.exit(1);
5
5
  }
6
- await import("./chunks/index-gHAU1HiK.js");
6
+ await import("./chunks/index-DxeR8yGt.js");
7
7
  })();
package/dist/index.js CHANGED
@@ -23229,7 +23229,7 @@ ${context2.authorReply.trim()}
23229
23229
  const line3 = buffer2.slice(0, newlineIndex).trim();
23230
23230
  buffer2 = buffer2.slice(newlineIndex + 1);
23231
23231
  if (line3) {
23232
- if (line3.length > options.maxBufferBytes) {
23232
+ if (options.maxBufferBytes !== void 0 && line3.length > options.maxBufferBytes) {
23233
23233
  options.onOverflow?.();
23234
23234
  } else {
23235
23235
  options.onLine(line3);
@@ -23237,7 +23237,7 @@ ${context2.authorReply.trim()}
23237
23237
  }
23238
23238
  newlineIndex = buffer2.indexOf("\n");
23239
23239
  }
23240
- if (buffer2.length > options.maxBufferBytes) {
23240
+ if (options.maxBufferBytes !== void 0 && buffer2.length > options.maxBufferBytes) {
23241
23241
  buffer2 = "";
23242
23242
  discardingOversizedLine = true;
23243
23243
  options.onOverflow?.();
@@ -24638,7 +24638,7 @@ ${peerId}`;
24638
24638
  const getSessionIdFromRoomId = (roomId) => isSessionDocRoomId(roomId) ? roomId.slice(SESSION_DOC_PREFIX.length) : null;
24639
24639
  const isMachineDocRoomId = (roomId) => roomId.startsWith(MACHINE_DOC_PREFIX);
24640
24640
  const name$1 = "@lody/cli-cloud";
24641
- const version$7 = "0.78.0";
24641
+ const version$7 = "0.79.1";
24642
24642
  const type$2 = "module";
24643
24643
  const scripts = {
24644
24644
  "dev": "node dev.mjs",
@@ -110356,6 +110356,14 @@ ${incoming}`).output;
110356
110356
  ]).optional()
110357
110357
  })
110358
110358
  });
110359
+ const CodexSessionTitleSchema = object$1({
110360
+ titleSource: _enum$1([
110361
+ "explicit",
110362
+ "fallback",
110363
+ "unset",
110364
+ "unknown"
110365
+ ])
110366
+ });
110359
110367
  const CodexProposedPlanParamsSchema = object$1({
110360
110368
  schemaVersion: literal$1(1),
110361
110369
  sessionId: string$1(),
@@ -111060,7 +111068,13 @@ ${incoming}`).output;
111060
111068
  });
111061
111069
  }
111062
111070
  handleAgentSessionTitleUpdate(notification) {
111063
- if (!usesAcpProvidedSessionTitle(this.options.agentConfig?.cliType, this.options.agentConfig?.agentType) || notification.update.sessionUpdate !== "session_info_update") {
111071
+ if (notification.update.sessionUpdate !== "session_info_update") {
111072
+ return;
111073
+ }
111074
+ const ownsTitleGeneration = usesAcpProvidedSessionTitle(this.options.agentConfig?.cliType, this.options.agentConfig?.agentType);
111075
+ const codexTitleMeta = this.isCodexAgent() ? CodexSessionTitleSchema.safeParse(notification.update._meta?.codex) : null;
111076
+ const isExplicitCodexTitle = codexTitleMeta?.success === true && codexTitleMeta.data.titleSource === "explicit";
111077
+ if (!ownsTitleGeneration && !isExplicitCodexTitle) {
111064
111078
  return;
111065
111079
  }
111066
111080
  const title2 = typeof notification.update.title === "string" ? notification.update.title.trim() : "";
@@ -123501,6 +123515,42 @@ ${fallbackStderrTail}` : errorMessage2;
123501
123515
  windowsHide: true
123502
123516
  });
123503
123517
  };
123518
+ const CodexConfigOverrideSchema = record$1(string$1(), unknown());
123519
+ function prepareCodexHomeEnv(options, baseEnv) {
123520
+ const isBuiltinCodex = options.cliType === "builtin" && options.agentType === "codex";
123521
+ const isTitleAgentCodexRun = isBuiltinCodex && baseEnv.LODY_TITLE_AGENT === "1";
123522
+ const shouldUseWorkdirCodexHome = isBuiltinCodex && !baseEnv.CODEX_HOME && (baseEnv.LODY_E2E === "1" || isTitleAgentCodexRun);
123523
+ return {
123524
+ env: shouldUseWorkdirCodexHome ? {
123525
+ ...baseEnv,
123526
+ CODEX_HOME: path__default$1.join(options.workdir, ".codex")
123527
+ } : baseEnv,
123528
+ isTitleAgentCodexRun,
123529
+ shouldUseWorkdirCodexHome
123530
+ };
123531
+ }
123532
+ function withTitleAgentCodexConfig(env2) {
123533
+ const existingConfig = env2.CODEX_CONFIG ? CodexConfigOverrideSchema.parse(JSON.parse(env2.CODEX_CONFIG)) : {};
123534
+ return {
123535
+ ...env2,
123536
+ CODEX_CONFIG: JSON.stringify({
123537
+ ...existingConfig,
123538
+ project_doc_max_bytes: 0,
123539
+ include_environment_context: false,
123540
+ skills: {
123541
+ include_instructions: false,
123542
+ bundled: {
123543
+ enabled: false
123544
+ }
123545
+ }
123546
+ })
123547
+ };
123548
+ }
123549
+ function copyTitleAgentCodexConfig(sourcePath, destinationPath) {
123550
+ if (fs.existsSync(sourcePath)) {
123551
+ fs.copyFileSync(sourcePath, destinationPath);
123552
+ }
123553
+ }
123504
123554
  const startLocalAcpAgent = async (options) => {
123505
123555
  options.signal?.throwIfAborted();
123506
123556
  const launch = await resolveACPProcessLaunchAsync({
@@ -123525,47 +123575,46 @@ ${fallbackStderrTail}` : errorMessage2;
123525
123575
  isResume: false
123526
123576
  };
123527
123577
  const baseEnv = withoutElectronBootstrapCredentials(options.env ?? process.env);
123528
- const shouldUseWorkdirCodexHome = options.cliType === "builtin" && options.agentType === "codex" && !baseEnv.CODEX_HOME && (baseEnv.LODY_E2E === "1" || baseEnv.LODY_TITLE_AGENT === "1");
123529
- const env2 = shouldUseWorkdirCodexHome ? {
123530
- ...baseEnv,
123531
- CODEX_HOME: path__default$1.join(options.workdir, ".codex")
123532
- } : baseEnv;
123578
+ const { env: env2, isTitleAgentCodexRun, shouldUseWorkdirCodexHome } = prepareCodexHomeEnv(options, baseEnv);
123533
123579
  const loginShellEnv = await getLoginShellEnv();
123534
- const envWithAcpStartup = withoutElectronBootstrapCredentials(withLodyNpmCacheForNpx(launch.command, withDefaultAcpPathEntries(mergeACPProcessEnv(launch, mergeLoginShellEnv(env2, loginShellEnv)), options.agentType)));
123580
+ const mergedStartupEnv = withoutElectronBootstrapCredentials(withLodyNpmCacheForNpx(launch.command, withDefaultAcpPathEntries(mergeACPProcessEnv(launch, mergeLoginShellEnv(env2, loginShellEnv)), options.agentType)));
123581
+ const envWithAcpStartup = isTitleAgentCodexRun ? withTitleAgentCodexConfig(mergedStartupEnv) : mergedStartupEnv;
123535
123582
  const keepCodexHome = env2.LODY_KEEP_CODEX_HOME === "1";
123536
123583
  const defaultCodexHome = path__default$1.join(os__default$1.homedir(), ".codex");
123537
123584
  const defaultAuthPath = path__default$1.join(defaultCodexHome, "auth.json");
123585
+ const defaultConfigPath = path__default$1.join(defaultCodexHome, "config.toml");
123538
123586
  const localAuthPath = env2.CODEX_HOME ? path__default$1.join(env2.CODEX_HOME, "auth.json") : null;
123539
- const isTitleAgentRun = baseEnv.LODY_TITLE_AGENT === "1";
123540
- const TITLE_AGENT_CODEX_CONFIG_TOML = `project_doc_max_bytes = 0
123541
- include_environment_context = false
123542
-
123543
- [skills]
123544
- include_instructions = false
123545
-
123546
- [skills.bundled]
123547
- enabled = false
123548
- `;
123587
+ const localConfigPath = env2.CODEX_HOME ? path__default$1.join(env2.CODEX_HOME, "config.toml") : null;
123549
123588
  let lastStderrTail = "";
123550
123589
  const attemptStart = async (attemptArgs, startupTimeouts) => {
123551
123590
  options.signal?.throwIfAborted();
123552
123591
  lastStderrTail = "";
123553
- let copiedAuth = false;
123554
123592
  if (shouldUseWorkdirCodexHome) {
123555
123593
  fs.mkdirSync(env2.CODEX_HOME, {
123556
123594
  recursive: true
123557
123595
  });
123558
- if (isTitleAgentRun) {
123559
- fs.writeFileSync(path__default$1.join(env2.CODEX_HOME, "config.toml"), TITLE_AGENT_CODEX_CONFIG_TOML);
123596
+ if (isTitleAgentCodexRun && localConfigPath && fs.existsSync(defaultConfigPath)) {
123597
+ copyTitleAgentCodexConfig(defaultConfigPath, localConfigPath);
123560
123598
  }
123561
123599
  if (localAuthPath && fs.existsSync(defaultAuthPath) && !fs.existsSync(localAuthPath)) {
123562
123600
  try {
123563
123601
  fs.copyFileSync(defaultAuthPath, localAuthPath);
123564
- copiedAuth = true;
123565
123602
  } catch {
123566
123603
  }
123567
123604
  }
123568
123605
  }
123606
+ const cleanupCodexHome = () => {
123607
+ if (!shouldUseWorkdirCodexHome || !env2.CODEX_HOME || keepCodexHome) {
123608
+ return;
123609
+ }
123610
+ try {
123611
+ fs.rmSync(env2.CODEX_HOME, {
123612
+ recursive: true,
123613
+ force: true
123614
+ });
123615
+ } catch {
123616
+ }
123617
+ };
123569
123618
  captureAcpSpawnStarted(spawnAnalyticsProps);
123570
123619
  let agentProcess;
123571
123620
  try {
@@ -123581,12 +123630,15 @@ enabled = false
123581
123630
  spawnImpl: options.spawnImpl
123582
123631
  });
123583
123632
  } catch (error2) {
123633
+ cleanupCodexHome();
123584
123634
  captureAcpSpawnFailed({
123585
123635
  ...spawnAnalyticsProps,
123586
123636
  reason: classifyCliSpawnReason(error2)
123587
123637
  });
123588
123638
  throw error2;
123589
123639
  }
123640
+ agentProcess.once("exit", cleanupCodexHome);
123641
+ agentProcess.once("error", cleanupCodexHome);
123590
123642
  options.logger.debug(`[acp-startup] spawned ACP process (cliType=${options.cliType} agentType=${options.agentType} workdir=${options.workdir})`);
123591
123643
  const stderrStream = agentProcess.stderr;
123592
123644
  let stderrTail = "";
@@ -123624,24 +123676,6 @@ enabled = false
123624
123676
  ],
123625
123677
  getStderrTail: () => stderrTail
123626
123678
  });
123627
- if (shouldUseWorkdirCodexHome && env2.CODEX_HOME && !keepCodexHome) {
123628
- const cleanup = () => {
123629
- try {
123630
- if (copiedAuth && localAuthPath) {
123631
- fs.rmSync(localAuthPath, {
123632
- force: true
123633
- });
123634
- }
123635
- fs.rmSync(env2.CODEX_HOME, {
123636
- recursive: true,
123637
- force: true
123638
- });
123639
- } catch {
123640
- }
123641
- };
123642
- agentProcess.once("exit", cleanup);
123643
- agentProcess.once("error", cleanup);
123644
- }
123645
123679
  if (!agentProcess.stdout) {
123646
123680
  throw new Error("Agent process stdout is not available");
123647
123681
  }
@@ -124463,7 +124497,7 @@ enabled = false
124463
124497
  options.logger.debug(`[title-generator] Skipping unavailable title config option ${key2}=${String(value)}`);
124464
124498
  }
124465
124499
  }
124466
- const extractTitleChunkFromNotification = (notification) => {
124500
+ const extractTitleChunkFromNotification = (notification, agentType) => {
124467
124501
  const update2 = notification.update;
124468
124502
  if (update2.sessionUpdate !== "agent_message_chunk") {
124469
124503
  return null;
@@ -124473,6 +124507,12 @@ enabled = false
124473
124507
  return null;
124474
124508
  }
124475
124509
  const codexMeta = update2._meta?.["codex"];
124510
+ if (agentType === "codex") {
124511
+ if (!codexMeta || typeof codexMeta !== "object" || !("phase" in codexMeta) || codexMeta.phase !== "final_answer") {
124512
+ return null;
124513
+ }
124514
+ return content.text;
124515
+ }
124476
124516
  if (codexMeta && typeof codexMeta === "object" && "phase" in codexMeta && codexMeta.phase !== "final_answer") {
124477
124517
  return null;
124478
124518
  }
@@ -124487,6 +124527,32 @@ enabled = false
124487
124527
  if (!withoutMarkdown) return null;
124488
124528
  return withoutMarkdown.slice(0, 80);
124489
124529
  };
124530
+ const looksLikeProviderControlPayload = (candidate) => {
124531
+ const trimmed2 = candidate.trim();
124532
+ if (/^\{\s*['"]?(?:type|status|error)['"]?\s*:/i.test(trimmed2) || /["']type["']\s*:\s*["'](?:error|warning)["']/i.test(trimmed2) || /\binvalid_request_error\b/i.test(trimmed2) || /^HTTP\s+[45]\d\d\b/i.test(trimmed2) || /^(?:internal server error|bad request|too many requests)\s*(?:[:({-]|$)/i.test(trimmed2) || /^(?:error|warning|failed|failure)\s*(?:[:{]|\[)/i.test(trimmed2)) {
124533
+ return true;
124534
+ }
124535
+ try {
124536
+ const parsed = JSON.parse(trimmed2);
124537
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
124538
+ return false;
124539
+ }
124540
+ const record2 = parsed;
124541
+ return record2.type === "error" || record2.type === "warning" || typeof record2.status === "number" && record2.status >= 400 || typeof record2.error === "string" || typeof record2.error === "object" && record2.error !== null;
124542
+ } catch {
124543
+ return false;
124544
+ }
124545
+ };
124546
+ const sanitizeGeneratedTitle = (candidate) => {
124547
+ if (!candidate) {
124548
+ return null;
124549
+ }
124550
+ const withoutInternalInstructions = sanitizeLodyInternalInstructions(candidate);
124551
+ if (!withoutInternalInstructions || looksLikeProviderControlPayload(withoutInternalInstructions)) {
124552
+ return null;
124553
+ }
124554
+ return sanitizeTitle(withoutInternalInstructions);
124555
+ };
124490
124556
  const ensureWorkdirIsGitRepo = async (workdir, logger2) => {
124491
124557
  const isGitRepo = async () => {
124492
124558
  try {
@@ -124537,13 +124603,8 @@ enabled = false
124537
124603
  }
124538
124604
  };
124539
124605
  const generateTitleIsolated = async (options) => {
124540
- const workdir = path__default$1.join(os__default$1.tmpdir(), "lody-title-agent");
124541
- fs.mkdirSync(workdir, {
124542
- recursive: true
124543
- });
124544
- if (options.agentType === "codex" && !await ensureWorkdirIsGitRepo(workdir, options.logger)) {
124545
- return sanitizeTitle(options.taskPrompt);
124546
- }
124606
+ const fallbackTitle = sanitizeGeneratedTitle(options.taskPrompt);
124607
+ const workdir = fs.mkdtempSync(path__default$1.join(os__default$1.tmpdir(), "lody-title-agent-"));
124547
124608
  const sleep2 = (ms2) => new Promise((resolve2) => setTimeout(resolve2, ms2));
124548
124609
  const tryGenerateWithArgs = async (extraArgs) => {
124549
124610
  let collectedText = "";
@@ -124564,7 +124625,7 @@ enabled = false
124564
124625
  terminalManager: noopTerminalManager,
124565
124626
  terminalEnabled: false,
124566
124627
  onUpdateMessage: (msg) => {
124567
- const text = extractTitleChunkFromNotification(msg);
124628
+ const text = extractTitleChunkFromNotification(msg, options.agentType);
124568
124629
  if (text) {
124569
124630
  collectedText += text;
124570
124631
  }
@@ -124599,16 +124660,16 @@ enabled = false
124599
124660
  while (Date.now() < deadline && collectedText.trim() === "") {
124600
124661
  await sleep2(100);
124601
124662
  }
124602
- const fromStream = sanitizeTitle(collectedText);
124663
+ const fromStream = sanitizeGeneratedTitle(collectedText);
124603
124664
  if (fromStream) {
124604
124665
  return fromStream;
124605
124666
  }
124606
- const rawTitle = extractTextFromAgentResponse(response2) ?? options.taskPrompt;
124607
- const sanitized = sanitizeTitle(rawTitle);
124667
+ const rawTitle = extractTextFromAgentResponse(response2);
124668
+ const sanitized = sanitizeGeneratedTitle(rawTitle);
124608
124669
  if (sanitized) {
124609
124670
  return sanitized;
124610
124671
  }
124611
- return sanitizeTitle(options.taskPrompt);
124672
+ return fallbackTitle;
124612
124673
  } finally {
124613
124674
  await shutdownLocalAcpAgent({
124614
124675
  agentProcess,
@@ -124619,20 +124680,26 @@ enabled = false
124619
124680
  });
124620
124681
  }
124621
124682
  };
124622
- let lastError = null;
124623
124683
  try {
124624
- const title2 = await tryGenerateWithArgs([]);
124625
- if (title2) {
124626
- return title2;
124684
+ if (options.agentType === "codex" && !await ensureWorkdirIsGitRepo(workdir, options.logger)) {
124685
+ return fallbackTitle;
124686
+ }
124687
+ try {
124688
+ return await tryGenerateWithArgs([]) ?? fallbackTitle;
124689
+ } catch (error2) {
124690
+ options.logger.debug("Failed to generate title with args:", error2);
124691
+ return fallbackTitle;
124692
+ }
124693
+ } finally {
124694
+ try {
124695
+ fs.rmSync(workdir, {
124696
+ recursive: true,
124697
+ force: true
124698
+ });
124699
+ } catch (error2) {
124700
+ options.logger.debug(`[title-generator] Failed to remove temporary workdir ${workdir}: ${formatErrorMessage(error2)}`);
124627
124701
  }
124628
- } catch (error2) {
124629
- options.logger.debug("Failed to generate title with args:", error2);
124630
- lastError = error2;
124631
- }
124632
- if (lastError) {
124633
- throw lastError;
124634
124702
  }
124635
- return null;
124636
124703
  };
124637
124704
  const titleToBranchName = (title2) => {
124638
124705
  if (!title2 || typeof title2 !== "string") {
@@ -160566,7 +160633,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
160566
160633
  pendingProcessLifecycleAction = null;
160567
160634
  store = new SessionTransientStore();
160568
160635
  sessionActivePresence;
160569
- titleGenerationInFlight = /* @__PURE__ */ new Set();
160636
+ titleGenerationInFlight = /* @__PURE__ */ new Map();
160570
160637
  archiveWatchHandle = null;
160571
160638
  archiveInFlight = /* @__PURE__ */ new Set();
160572
160639
  deleteWatchHandle = null;
@@ -165398,6 +165465,9 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
165398
165465
  this.logger.error(`[${sessionId}] Backfill gave up for ${fileId}; block stays local (pending on other devices)`);
165399
165466
  return;
165400
165467
  }
165468
+ if (this.sessionFileBackfillStopped || !this.remoteBackfillEnabled) {
165469
+ return;
165470
+ }
165401
165471
  await delay$1(sessionFileBackfillDelayMs(attempt));
165402
165472
  }
165403
165473
  }
@@ -166081,18 +166151,23 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
166081
166151
  if (usesAcpProvidedSessionTitle(cliType, agentType)) {
166082
166152
  return;
166083
166153
  }
166084
- if (this.titleGenerationInFlight.has(sessionId)) {
166154
+ const existingGeneration = this.titleGenerationInFlight.get(sessionId);
166155
+ if (existingGeneration) {
166156
+ try {
166157
+ await existingGeneration;
166158
+ } catch (error2) {
166159
+ this.logger.debug(`[${sessionId}] Existing session title generation failed: ${formatErrorMessage(error2)}`);
166160
+ }
166085
166161
  return;
166086
166162
  }
166087
- this.titleGenerationInFlight.add(sessionId);
166088
- try {
166163
+ const generation = (async () => {
166089
166164
  const sessionDoc = await this.workspaceDocument.getOrCreateSessionDoc(sessionId);
166090
166165
  const meta = await sessionDoc.getMetaState();
166091
166166
  const existingTitle = meta?.title?.trim();
166092
166167
  const canOverwriteExistingTitle = meta?.titleSource === "draft";
166093
166168
  if (existingTitle && !canOverwriteExistingTitle) {
166094
166169
  this.logger.debug(`[${sessionId}] Skipping session title generation because title already set: ${existingTitle}`);
166095
- return;
166170
+ return meta?.titleSource === "generated" ? existingTitle : null;
166096
166171
  }
166097
166172
  this.logger.debug(`[${sessionId}] Generating session title because title is missing`);
166098
166173
  const resolvedTitleConfig = titleConfig ?? await this.resolveTitleConfig(sessionId, meta?.agentConfigId);
@@ -166108,20 +166183,27 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
166108
166183
  });
166109
166184
  if (!title2) {
166110
166185
  this.logger.debug(`[${sessionId}] Session title generation returned empty result`);
166111
- return;
166186
+ return null;
166112
166187
  }
166113
166188
  const applied = await sessionDoc.setTitleIfSourceIn(title2, "generated", [
166114
166189
  "draft"
166115
166190
  ]);
166116
166191
  if (!applied) {
166117
166192
  this.logger.debug(`[${sessionId}] Skipping generated title because title changed while generation was in flight`);
166118
- return;
166193
+ } else {
166194
+ this.logger.debug(`[${sessionId}] Session title stored in metadata: ${title2}`);
166119
166195
  }
166120
- this.logger.debug(`[${sessionId}] Session title stored in metadata: ${title2}`);
166196
+ return title2;
166197
+ })();
166198
+ this.titleGenerationInFlight.set(sessionId, generation);
166199
+ try {
166200
+ await generation;
166121
166201
  } catch (error2) {
166122
166202
  this.logger.debug(`[${sessionId}] Failed to generate/store session title: ${formatErrorMessage(error2)}`);
166123
166203
  } finally {
166124
- this.titleGenerationInFlight.delete(sessionId);
166204
+ if (this.titleGenerationInFlight.get(sessionId) === generation) {
166205
+ this.titleGenerationInFlight.delete(sessionId);
166206
+ }
166125
166207
  }
166126
166208
  }
166127
166209
  async startSessionDispatchWatcher() {
@@ -166536,11 +166618,14 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
166536
166618
  let metaCustomAcp;
166537
166619
  let metaRuntimeOverrides;
166538
166620
  let metaAgentConfigId;
166621
+ let reusableTitlePromise;
166539
166622
  try {
166540
166623
  const sessionDoc = await this.workspaceDocument.getOrCreateSessionDoc(sessionId);
166541
166624
  const meta = await sessionDoc.getMetaState();
166542
166625
  metaBranchName = meta?.branchName?.trim() || null;
166543
166626
  metaAgentConfigId = meta?.agentConfigId;
166627
+ const generatedMetaTitle = meta?.titleSource === "generated" ? meta.title?.trim() : "";
166628
+ reusableTitlePromise = generatedMetaTitle ? Promise.resolve(generatedMetaTitle) : this.titleGenerationInFlight.get(sessionId);
166544
166629
  const agentConfig = metaAgentConfigId ? await this.workspaceDocument.getAgentConfigById(metaAgentConfigId) : null;
166545
166630
  const legacyLaunchConfig = await readLegacySessionLaunchConfig({
166546
166631
  repo: this.workspaceDocument.repo,
@@ -166559,7 +166644,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
166559
166644
  this.logger.debug(`[${sessionId}] Failed to read session meta before branch rename: ${formatErrorMessage(error2)}`);
166560
166645
  }
166561
166646
  const resolvedTitleConfig = titleConfig ?? await this.resolveTitleConfig(sessionId, metaAgentConfigId);
166562
- const branchName = await this.generateBranchNameWithTimeout(cliType, agentType, trimmedPrompt, env2, 2e4, resolvedTitleConfig, metaCustomAcp, metaRuntimeOverrides);
166647
+ const branchName = await this.generateBranchNameWithTimeout(cliType, agentType, trimmedPrompt, env2, 2e4, resolvedTitleConfig, metaCustomAcp, metaRuntimeOverrides, reusableTitlePromise);
166563
166648
  if (!branchName) {
166564
166649
  this.logger.debug(`[${sessionId}] Skipping branch rename: name generation timed out`);
166565
166650
  return;
@@ -166589,13 +166674,13 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
166589
166674
  this.logger.debug(`[${sessionId}] Failed to rename branch: ${formatErrorMessage(error2)}`);
166590
166675
  }
166591
166676
  }
166592
- async generateBranchNameWithTimeout(cliType, agentType, taskPrompt, env2, timeoutMs, titleConfig, customAcp, runtimeOverrides) {
166677
+ async generateBranchNameWithTimeout(cliType, agentType, taskPrompt, env2, timeoutMs, titleConfig, customAcp, runtimeOverrides, reusableTitlePromise) {
166593
166678
  let timeoutHandle = null;
166594
166679
  const timeoutPromise = new Promise((resolve2) => {
166595
166680
  timeoutHandle = setTimeout(() => resolve2(null), timeoutMs);
166596
166681
  });
166597
166682
  const namePromise = (async () => {
166598
- const title2 = await generateTitleIsolated({
166683
+ const title2 = reusableTitlePromise ? await reusableTitlePromise : await generateTitleIsolated({
166599
166684
  cliType,
166600
166685
  agentType,
166601
166686
  customAcp,
@@ -178539,7 +178624,7 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
178539
178624
  }
178540
178625
  roomMessageChain = roomMessageChain.then(() => handleMessage(config2, connection2, touchedEngines, message)).catch((error2) => logMessageHandlingError(config2.logger, error2));
178541
178626
  },
178542
- maxBufferBytes: LOCAL_LORO_DATA_PLANE_MAX_FRAME_BYTES,
178627
+ maxBufferBytes: config2.maxFrameBytes ?? LOCAL_LORO_DATA_PLANE_MAX_FRAME_BYTES,
178543
178628
  onOverflow: () => {
178544
178629
  connection2.send({
178545
178630
  type: "error",
@@ -213634,7 +213719,7 @@ ${page}${helpTipBottom}${choiceDescription}${ansiEscapes.cursorHide}`;
213634
213719
  data = createReviewBundleSnapshot(bundle);
213635
213720
  }
213636
213721
  const { injectReviewSnapshot } = await import("./chunks/index-VoI6Ds2-.js");
213637
- const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-Bgp2Thsg.js").then(async (m) => {
213722
+ const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-CbgGhbZE.js").then(async (m) => {
213638
213723
  await m.__tla;
213639
213724
  return m;
213640
213725
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lody",
3
- "version": "0.78.0",
3
+ "version": "0.79.1",
4
4
  "description": "Lody Agent CLI tool for managing remote command execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",