lody 0.71.3 → 0.71.4

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.
@@ -3,7 +3,7 @@ import { readFile, mkdir, writeFile, rename } from "node:fs/promises";
3
3
  import os__default from "node:os";
4
4
  import path__default from "node:path";
5
5
  import process from "node:process";
6
- const reviewViewerVersion = "0.71.3";
6
+ const reviewViewerVersion = "0.71.4";
7
7
  const reviewViewerSha256 = "e395b367a7644fe83bc050d0c646810914a2844824fd2b5d11d24ae4f55b6893";
8
8
  const reviewViewerFileName = "standalone.html";
9
9
  const DEFAULT_CDN_BASES = ["https://cdn.jsdelivr.net/npm", "https://unpkg.com"];
@@ -34404,7 +34404,12 @@ ${content.text}
34404
34404
  sessions
34405
34405
  };
34406
34406
  }
34407
+ static TITLE_POLL_INTERVAL_MS = 2e3;
34407
34408
  async maybeUpdateSessionTitle(sessionId, session) {
34409
+ if (session.titleLookupInFlight) {
34410
+ return;
34411
+ }
34412
+ session.titleLookupInFlight = true;
34408
34413
  let info;
34409
34414
  try {
34410
34415
  info = await mGe(sessionId, {
@@ -34413,8 +34418,10 @@ ${content.text}
34413
34418
  } catch (error) {
34414
34419
  this.logger.error(`Session ${sessionId}: failed to read session info: ${error}`);
34415
34420
  return;
34421
+ } finally {
34422
+ session.titleLookupInFlight = false;
34416
34423
  }
34417
- const rawTitle = info?.customTitle ?? info?.summary;
34424
+ const rawTitle = info?.customTitle;
34418
34425
  if (!rawTitle) {
34419
34426
  return;
34420
34427
  }
@@ -34423,6 +34430,7 @@ ${content.text}
34423
34430
  return;
34424
34431
  }
34425
34432
  session.lastTitle = title;
34433
+ this.stopSessionTitlePolling(session);
34426
34434
  await this.client.sessionUpdate({
34427
34435
  sessionId,
34428
34436
  update: {
@@ -34432,6 +34440,22 @@ ${content.text}
34432
34440
  }
34433
34441
  });
34434
34442
  }
34443
+ startSessionTitlePolling(sessionId, session) {
34444
+ if (session.lastTitle || session.titlePollTimer) {
34445
+ return;
34446
+ }
34447
+ void this.maybeUpdateSessionTitle(sessionId, session);
34448
+ session.titlePollTimer = setInterval(() => {
34449
+ void this.maybeUpdateSessionTitle(sessionId, session);
34450
+ }, ClaudeAcpAgent.TITLE_POLL_INTERVAL_MS);
34451
+ session.titlePollTimer.unref();
34452
+ }
34453
+ stopSessionTitlePolling(session) {
34454
+ if (session.titlePollTimer) {
34455
+ clearInterval(session.titlePollTimer);
34456
+ session.titlePollTimer = void 0;
34457
+ }
34458
+ }
34435
34459
  async authenticate(_params) {
34436
34460
  if (_params.methodId === "gateway" || _params.methodId === "gateway-bedrock") {
34437
34461
  this.gatewayAuthRequest = _params;
@@ -34971,7 +34995,9 @@ Compacting failed${reason}`
34971
34995
  }
34972
34996
  case "session_state_changed": {
34973
34997
  session.lastSessionState = message.state;
34974
- if (message.state === "idle") {
34998
+ if (message.state === "running") {
34999
+ this.startSessionTitlePolling(params.sessionId, session);
35000
+ } else if (message.state === "idle") {
34975
35001
  if (session.cancelled && session.activeTurn && !session.activeTurn.settled) {
34976
35002
  settleActive({
34977
35003
  stopReason: "cancelled",
@@ -34989,6 +35015,7 @@ Compacting failed${reason}`
34989
35015
  this.logger.error(`Session ${params.sessionId}: SDK went idle without emitting a result for the active turn; failing the in-flight prompt (issue #825)`);
34990
35016
  failActive(RequestError.internalError(errorKindData("no_result"), TURN_NO_RESULT_MESSAGE));
34991
35017
  }
35018
+ this.stopSessionTitlePolling(session);
34992
35019
  await this.maybeUpdateSessionTitle(params.sessionId, session);
34993
35020
  }
34994
35021
  break;
@@ -35715,6 +35742,7 @@ ${message.api_refusal_explanation}` : "";
35715
35742
  }
35716
35743
  session.queryClosed = true;
35717
35744
  session.consumer = void 0;
35745
+ this.stopSessionTitlePolling(session);
35718
35746
  session.settingsManager.dispose();
35719
35747
  session.input.end();
35720
35748
  session.query.close();
package/dist/index.js CHANGED
@@ -4190,7 +4190,7 @@ let __tla = Promise.all([
4190
4190
  }
4191
4191
  }
4192
4192
  const name$1 = "lody";
4193
- const version$7 = "0.71.3";
4193
+ const version$7 = "0.71.4";
4194
4194
  const description$1 = "Lody Agent CLI tool for managing remote command execution";
4195
4195
  const type$2 = "module";
4196
4196
  const main$4 = "dist/index.js";
@@ -103186,8 +103186,13 @@ ${value}`;
103186
103186
  return proxyFromEnvExports.getProxyForUrl(targetUrl);
103187
103187
  };
103188
103188
  function resolveProxyUrl(targetUrl) {
103189
- const proxyUrl = getProxyForUrlWithWsFallback(targetUrl);
103189
+ const rawProxyUrl = getProxyForUrlWithWsFallback(targetUrl);
103190
+ if (!rawProxyUrl) {
103191
+ return {};
103192
+ }
103193
+ const proxyUrl = normalizeProxyUrl(rawProxyUrl);
103190
103194
  if (!proxyUrl) {
103195
+ warnUnsupportedProxyUrl(rawProxyUrl);
103191
103196
  return {};
103192
103197
  }
103193
103198
  return {
@@ -103195,6 +103200,33 @@ ${value}`;
103195
103200
  proxyUrlRedacted: redactProxyUrl(proxyUrl)
103196
103201
  };
103197
103202
  }
103203
+ const SCHEME_PREFIX_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
103204
+ const warnedProxyUrls = /* @__PURE__ */ new Set();
103205
+ function normalizeProxyUrl(rawProxyUrl) {
103206
+ const trimmed2 = rawProxyUrl.trim();
103207
+ if (!trimmed2) {
103208
+ return void 0;
103209
+ }
103210
+ const candidate = SCHEME_PREFIX_RE.test(trimmed2) ? trimmed2 : `http://${trimmed2}`;
103211
+ let parsed;
103212
+ try {
103213
+ parsed = new URL(candidate);
103214
+ } catch {
103215
+ return void 0;
103216
+ }
103217
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
103218
+ return void 0;
103219
+ }
103220
+ return candidate;
103221
+ }
103222
+ function warnUnsupportedProxyUrl(rawProxyUrl) {
103223
+ const redacted = redactProxyUrl(rawProxyUrl);
103224
+ if (warnedProxyUrls.has(redacted)) {
103225
+ return;
103226
+ }
103227
+ warnedProxyUrls.add(redacted);
103228
+ getLogger("proxy").warn(`[proxy] ignoring unsupported proxy URL ${redacted}: only http:// and https:// proxies are supported (e.g. socks proxies are not); using a direct connection instead`);
103229
+ }
103198
103230
  const DEFAULT_CONNECT_TIMEOUT_MS = 15e3;
103199
103231
  const DEFAULT_MAX_CONCURRENT_STREAMS = 100;
103200
103232
  const DISABLED_TRANSPORT_VALUES = /* @__PURE__ */ new Set([
@@ -106445,6 +106477,7 @@ ${value}`;
106445
106477
  const JSON_RPC_VERSION$1 = "2.0";
106446
106478
  const LORO_STREAMS_RPC_VERSION = "1";
106447
106479
  const LORO_STREAMS_RPC_RETENTION_SECONDS = 86400;
106480
+ const DEFAULT_LIVE_IDLE_TIMEOUT_MS = 12e4;
106448
106481
  const LORO_STREAMS_RPC_ERROR_CODES = {
106449
106482
  rpcVersionMismatch: "rpc_version_mismatch",
106450
106483
  methodUnavailable: "method_unavailable",
@@ -106970,57 +107003,101 @@ ${value}`;
106970
107003
  },
106971
107004
  readJsonLive: async (streamId, state2, onBatch, liveOptions) => {
106972
107005
  const { client: streamClient, context: context2 } = getStreamClient(streamId, "live");
106973
- for await (const event of streamClient.live({
106974
- offset: state2.nextOffset ?? "now",
106975
- mode: options.liveMode ?? "sse",
106976
- signal: liveOptions?.signal
106977
- })) {
106978
- switch (event.type) {
106979
- case "data":
106980
- state2.nextOffset = event.nextOffset;
106981
- state2.cursor = event.cursor;
106982
- await onBatch({
106983
- messages: parseJsonLivePayload(event.payload, streamId, "live read"),
106984
- nextOffset: event.nextOffset,
106985
- cursor: event.cursor,
106986
- upToDate: false
106987
- });
106988
- break;
106989
- case "up_to_date": {
106990
- state2.nextOffset = event.nextOffset;
106991
- state2.cursor = event.cursor;
106992
- await onBatch({
106993
- messages: [],
106994
- nextOffset: event.nextOffset,
106995
- cursor: event.cursor,
106996
- upToDate: true
106997
- });
106998
- break;
106999
- }
107000
- case "eof": {
107001
- state2.nextOffset = event.nextOffset;
107002
- state2.cursor = void 0;
107003
- await onBatch({
107004
- messages: [],
107005
- nextOffset: event.nextOffset,
107006
- cursor: void 0,
107007
- upToDate: true
107008
- });
107009
- return;
107006
+ const watchdog = createLiveIdleWatchdog(options.liveIdleTimeoutMs ?? DEFAULT_LIVE_IDLE_TIMEOUT_MS, liveOptions?.signal);
107007
+ try {
107008
+ for await (const event of streamClient.live({
107009
+ offset: state2.nextOffset ?? "now",
107010
+ mode: options.liveMode ?? "sse",
107011
+ signal: watchdog.signal
107012
+ })) {
107013
+ watchdog.reset();
107014
+ switch (event.type) {
107015
+ case "data":
107016
+ state2.nextOffset = event.nextOffset;
107017
+ state2.cursor = event.cursor;
107018
+ await onBatch({
107019
+ messages: parseJsonLivePayload(event.payload, streamId, "live read"),
107020
+ nextOffset: event.nextOffset,
107021
+ cursor: event.cursor,
107022
+ upToDate: false
107023
+ });
107024
+ break;
107025
+ case "up_to_date": {
107026
+ state2.nextOffset = event.nextOffset;
107027
+ state2.cursor = event.cursor;
107028
+ await onBatch({
107029
+ messages: [],
107030
+ nextOffset: event.nextOffset,
107031
+ cursor: event.cursor,
107032
+ upToDate: true
107033
+ });
107034
+ break;
107035
+ }
107036
+ case "eof": {
107037
+ state2.nextOffset = event.nextOffset;
107038
+ state2.cursor = void 0;
107039
+ await onBatch({
107040
+ messages: [],
107041
+ nextOffset: event.nextOffset,
107042
+ cursor: void 0,
107043
+ upToDate: true
107044
+ });
107045
+ return;
107046
+ }
107047
+ case "reconnecting":
107048
+ case "reconnected":
107049
+ break;
107050
+ case "error":
107051
+ throw toLoroGatewayError("read live updates from", streamId, event.error, context2);
107010
107052
  }
107011
- case "reconnecting":
107012
- case "reconnected":
107013
- break;
107014
- case "error":
107015
- throw toLoroGatewayError("read live updates from", streamId, event.error, context2);
107016
107053
  }
107054
+ } finally {
107055
+ watchdog.dispose();
107017
107056
  }
107018
107057
  }
107019
107058
  };
107020
107059
  }
107060
+ function createLiveIdleWatchdog(timeoutMs, parentSignal) {
107061
+ const controller = new AbortController();
107062
+ const armed = Number.isFinite(timeoutMs) && timeoutMs > 0;
107063
+ let timer2;
107064
+ const onParentAbort = () => controller.abort();
107065
+ if (parentSignal) {
107066
+ if (parentSignal.aborted) {
107067
+ controller.abort();
107068
+ } else {
107069
+ parentSignal.addEventListener("abort", onParentAbort, {
107070
+ once: true
107071
+ });
107072
+ }
107073
+ }
107074
+ const reset = () => {
107075
+ if (!armed || controller.signal.aborted) {
107076
+ return;
107077
+ }
107078
+ if (timer2 !== void 0) {
107079
+ clearTimeout(timer2);
107080
+ }
107081
+ timer2 = setTimeout(() => controller.abort(), timeoutMs);
107082
+ timer2.unref?.();
107083
+ };
107084
+ const dispose = () => {
107085
+ if (timer2 !== void 0) {
107086
+ clearTimeout(timer2);
107087
+ timer2 = void 0;
107088
+ }
107089
+ parentSignal?.removeEventListener("abort", onParentAbort);
107090
+ };
107091
+ reset();
107092
+ return {
107093
+ signal: controller.signal,
107094
+ reset,
107095
+ dispose
107096
+ };
107097
+ }
107021
107098
  function selectJsonStreamOperationBaseUrl(baseUrl, streamId, operation, shardUrls) {
107022
107099
  const shardCandidates = operation === "append" ? shardUrls?.largePost ?? shardUrls?.other : operation === "live" ? shardUrls?.catchup ?? shardUrls?.bootstrap ?? shardUrls?.other : shardUrls?.other;
107023
- const shardBaseUrl = operation === "append" ? selectRandomShardUrl(shardCandidates) : selectStableShardUrl(shardCandidates, streamId);
107100
+ const shardBaseUrl = selectStableShardUrl(shardCandidates, streamId);
107024
107101
  if (!shardBaseUrl) return baseUrl;
107025
107102
  try {
107026
107103
  const selected = new URL(shardBaseUrl);
@@ -107041,16 +107118,6 @@ ${value}`;
107041
107118
  }
107042
107119
  return candidates[(hash2 >>> 0) % candidates.length];
107043
107120
  }
107044
- function selectRandomShardUrl(candidates) {
107045
- if (!candidates || candidates.length === 0) return void 0;
107046
- const crypto2 = globalThis.crypto;
107047
- if (crypto2?.getRandomValues) {
107048
- const value = new Uint32Array(1);
107049
- crypto2.getRandomValues(value);
107050
- return candidates[(value[0] ?? 0) % candidates.length];
107051
- }
107052
- return candidates[Math.floor(Math.random() * candidates.length)];
107053
- }
107054
107121
  const delay$4 = async (ms2) => {
107055
107122
  await new Promise((resolve2) => setTimeout(resolve2, ms2));
107056
107123
  };
@@ -130961,9 +131028,6 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
130961
131028
  return null;
130962
131029
  };
130963
131030
  const isWorkspaceDirty = async (runGit2) => {
130964
- if (!await isInsideGitWorktree$1(runGit2)) {
130965
- return false;
130966
- }
130967
131031
  try {
130968
131032
  const status = await runGit2([
130969
131033
  "status",
@@ -130971,7 +131035,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
130971
131035
  ]);
130972
131036
  return status.trim().length > 0;
130973
131037
  } catch {
130974
- return false;
131038
+ return void 0;
130975
131039
  }
130976
131040
  };
130977
131041
  const listUntrackedFiles = async (runGit2, filePaths) => {
@@ -143417,7 +143481,7 @@ ${entry.text}`).join("\n\n");
143417
143481
  } catch (error2) {
143418
143482
  this.deps.logger.debug(`[${sessionId}] Failed to compute git diff stats:`, error2);
143419
143483
  }
143420
- let workspaceDirty = false;
143484
+ let workspaceDirty;
143421
143485
  try {
143422
143486
  workspaceDirty = await isWorkspaceDirty(runGit2);
143423
143487
  this.deps.logger.debug(`[${sessionId}] Workspace dirty: ${workspaceDirty}`);
@@ -143428,9 +143492,11 @@ ${entry.text}`).join("\n\n");
143428
143492
  const sessionDoc = await this.deps.workspaceDocument.getOrCreateSessionDoc(sessionId);
143429
143493
  const workspace = await this.resolveWorkspaceSessionContext(sessionId, sessionDoc);
143430
143494
  const metaPatch = {
143431
- diffStats,
143432
- workspaceDirty
143495
+ diffStats
143433
143496
  };
143497
+ if (workspaceDirty !== void 0) {
143498
+ metaPatch.workspaceDirty = workspaceDirty;
143499
+ }
143434
143500
  await this.deps.workspaceDocument.repo.upsertDocMeta(workspace.ownerRoomId, metaPatch);
143435
143501
  } catch (error2) {
143436
143502
  this.deps.logger.debug(`[${sessionId}] Failed to persist session meta diffStats: ${formatErrorMessage(error2)}`);
@@ -153402,7 +153468,10 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
153402
153468
  return {
153403
153469
  ...entry,
153404
153470
  userTurnId: entry.userTurnId ?? userTurnId,
153405
- modelInfo: modelInfo ?? entry.modelInfo
153471
+ modelInfo: modelInfo ?? entry.modelInfo,
153472
+ finished: false,
153473
+ endedAt: void 0,
153474
+ permissionWaitMs: void 0
153406
153475
  };
153407
153476
  });
153408
153477
  }
@@ -203396,7 +203465,7 @@ ${page}${helpTipBottom}${choiceDescription}${ansiEscapes.cursorHide}`;
203396
203465
  data = createReviewBundleSnapshot(bundle);
203397
203466
  }
203398
203467
  const { injectReviewSnapshot } = await import("./chunks/index-VoI6Ds2-.js");
203399
- const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-S-XYMgL_.js");
203468
+ const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-B9Tm-8O4.js");
203400
203469
  const template = await resolveReviewViewerTemplate();
203401
203470
  const html = injectReviewSnapshot(template, data);
203402
203471
  const outputPath = options.output ? path__default$1.resolve(options.output) : defaultHtmlOutputPath(inputPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lody",
3
- "version": "0.71.3",
3
+ "version": "0.71.4",
4
4
  "description": "Lody Agent CLI tool for managing remote command execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -75,14 +75,14 @@
75
75
  "ws": "^8.18.3",
76
76
  "zod": "^4.1.5",
77
77
  "zstd-stream": "^1.1.0",
78
+ "@lody/cli-supervisor": "0.0.1",
78
79
  "@lody/convex": "0.0.1",
79
80
  "@lody/loro-streams-rpc": "0.0.1",
80
- "acp-extension-claude": "0.59.0",
81
- "@lody/code-review-helper": "0.0.0",
82
- "@lody/cli-supervisor": "0.0.1",
83
81
  "@lody/shared": "0.0.1",
82
+ "@lody/code-review-helper": "0.0.0",
83
+ "acp-extension-claude": "0.59.0",
84
+ "lody-code-review-viewer": "0.71.4",
84
85
  "acp-extension-core": "0.0.5",
85
- "lody-code-review-viewer": "0.71.3",
86
86
  "acp-extension-codex": "1.2.1"
87
87
  },
88
88
  "files": [