lody 0.71.2 → 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.2";
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.2";
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";
@@ -4204,7 +4204,7 @@ let __tla = Promise.all([
4204
4204
  "build": "pnpm run clean && pnpm run prepare:acp-adapters && pnpm run typecheck && pnpm run build:bundle && pnpm run check:published-bundle-imports && pnpm run copy:wasm",
4205
4205
  "build:watch": "pnpm run prepare:review-assets && vite build --watch",
4206
4206
  "build:bundle": "pnpm run prepare:review-assets && vite build",
4207
- "prepare:acp-adapters": "pnpm --dir ../.. --filter acp-extension-claude build && pnpm --dir ../.. --filter acp-extension-codex build",
4207
+ "prepare:acp-adapters": "corepack pnpm --dir ../.. --filter acp-extension-claude build && corepack pnpm --dir ../.. --filter acp-extension-codex build",
4208
4208
  "prepare:review-assets": "pnpm --dir ../.. --filter lody-code-review-viewer build",
4209
4209
  "check:published-bundle-imports": "node scripts/check-published-bundle-imports.js",
4210
4210
  "copy:wasm": "node scripts/copy-loro-wasm.js",
@@ -4516,7 +4516,7 @@ let __tla = Promise.all([
4516
4516
  displayName: "Codex"
4517
4517
  }
4518
4518
  ];
4519
- const usesAcpProvidedSessionTitle = (cliType, agentType) => cliType === "builtin" && (agentType === "claude" || agentType === "codex");
4519
+ const usesAcpProvidedSessionTitle = (cliType, agentType) => cliType === "builtin" && agentType === "claude";
4520
4520
  const isBuiltinRuntimeOverrides = (value) => {
4521
4521
  if (!value || typeof value !== "object" || Array.isArray(value)) {
4522
4522
  return false;
@@ -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) => {
@@ -136226,6 +136290,32 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136226
136290
  };
136227
136291
  }
136228
136292
  }
136293
+ const isOperationStoreBusyError = (error2) => {
136294
+ if (!(error2 instanceof Error)) return false;
136295
+ const code2 = error2.code;
136296
+ return code2 === "SQLITE_BUSY" || code2 === "SQLITE_BUSY_SNAPSHOT";
136297
+ };
136298
+ const BUSY_RETRY_DELAYS_MS = [
136299
+ 100,
136300
+ 300,
136301
+ 900
136302
+ ];
136303
+ const runWithOperationStoreBusyRetry = async (run2, options = {}) => {
136304
+ const delays = options.delaysMs ?? BUSY_RETRY_DELAYS_MS;
136305
+ const sleep2 = options.sleep ?? ((ms2) => new Promise((resolve2) => setTimeout(resolve2, ms2)));
136306
+ for (let attempt = 0; ; attempt += 1) {
136307
+ try {
136308
+ return run2();
136309
+ } catch (error2) {
136310
+ if (!isOperationStoreBusyError(error2)) throw error2;
136311
+ const delayMs = delays[attempt];
136312
+ if (delayMs === void 0) {
136313
+ throw new LodyOperationStoreError("STORE_BUSY", "The local Operation store is busy; retry the command.", true);
136314
+ }
136315
+ await sleep2(delayMs);
136316
+ }
136317
+ }
136318
+ };
136229
136319
  const parseJson = (text, schema2, label2) => {
136230
136320
  let parsed;
136231
136321
  try {
@@ -136361,7 +136451,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136361
136451
  const getOperationDeliveryId = (requesterSessionId, operationId) => `operation:${requesterSessionId}:${operationId}:completion`;
136362
136452
  const getOperationCompletionTurnId = (requesterSessionId, operationId) => `operation-completion:${requesterSessionId}:${operationId}`;
136363
136453
  class LodyOperationStore {
136364
- constructor(dbPath = getLodyOperationStorePath(), now2 = Date.now) {
136454
+ constructor(dbPath = getLodyOperationStorePath(), now2 = Date.now, options = {}) {
136365
136455
  this.now = now2;
136366
136456
  mkdirSync$2(path__default$1.dirname(dbPath), {
136367
136457
  recursive: true
@@ -136371,8 +136461,10 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136371
136461
  this.db.pragma("journal_mode = WAL");
136372
136462
  this.db.pragma("foreign_keys = ON");
136373
136463
  this.migrate();
136374
- this.repairTerminalDeliveries();
136375
- this.cleanupExpired(false);
136464
+ if (options.maintenance !== false) {
136465
+ this.repairTerminalDeliveries();
136466
+ this.cleanupExpired(false);
136467
+ }
136376
136468
  }
136377
136469
  db;
136378
136470
  close() {
@@ -136414,7 +136506,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136414
136506
  claimedItemIndexes
136415
136507
  };
136416
136508
  });
136417
- const accepted = transaction();
136509
+ const accepted = transaction.immediate();
136418
136510
  this.cleanupExpired(false);
136419
136511
  return accepted;
136420
136512
  }
@@ -136471,7 +136563,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136471
136563
  return {
136472
136564
  claimed: true
136473
136565
  };
136474
- })();
136566
+ }).immediate();
136475
136567
  }
136476
136568
  markItemInputDurable(requesterSessionId, operationId, itemIndex, claimToken) {
136477
136569
  return this.db.transaction(() => {
@@ -136492,7 +136584,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136492
136584
  this.db.prepare(`DELETE FROM operation_item_materializations
136493
136585
  WHERE requester_session_id = ? AND operation_id = ? AND item_index = ?`).run(requesterSessionId, operationId, itemIndex);
136494
136586
  return this.get(requesterSessionId, operationId);
136495
- })();
136587
+ }).immediate();
136496
136588
  }
136497
136589
  finish(requesterSessionId, operationId, completion, finishedAt = new Date(this.now()).toISOString()) {
136498
136590
  const bounded = this.assertCompletionBound(completion);
@@ -136518,7 +136610,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136518
136610
  }
136519
136611
  return updated;
136520
136612
  });
136521
- return transaction();
136613
+ return transaction.immediate();
136522
136614
  }
136523
136615
  cancel(requesterSessionId, operationId) {
136524
136616
  const transaction = this.db.transaction(() => {
@@ -136542,7 +136634,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136542
136634
  didCancel: true
136543
136635
  };
136544
136636
  });
136545
- return transaction();
136637
+ return transaction.immediate();
136546
136638
  }
136547
136639
  listPendingDeliveries(workspaceId, requesterSessionId) {
136548
136640
  const rows = requesterSessionId ? this.db.prepare(`SELECT * FROM deliveries
@@ -136690,7 +136782,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136690
136782
  )`).run(cutoff);
136691
136783
  this.db.prepare(`INSERT INTO orchestration_meta (key, value) VALUES ('last_cleanup_at_ms', ?)
136692
136784
  ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(String(nowMs2));
136693
- })();
136785
+ }).immediate();
136694
136786
  }
136695
136787
  migrate() {
136696
136788
  this.db.exec(`
@@ -136774,7 +136866,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136774
136866
  WHERE deliveries.requester_session_id = operations.requester_session_id
136775
136867
  AND deliveries.operation_id = operations.operation_id
136776
136868
  )`).run();
136777
- })();
136869
+ }).immediate();
136778
136870
  }
136779
136871
  }
136780
136872
  const terminalAssistantFor = (history, userTurnId) => history.find((entry) => entry.role === "assistant" && entry.userTurnId === userTurnId && (entry.finished === true || typeof entry.endedAt === "number"));
@@ -141000,12 +141092,6 @@ ${entry.text}`).join("\n\n");
141000
141092
  } : {}
141001
141093
  };
141002
141094
  }
141003
- async function ensureSessionDocSynced(sessionDoc, reason) {
141004
- const synced = await sessionDoc.waitUntilSynced();
141005
- if (!synced) {
141006
- throw new Error(`Session document changes were not confirmed by Loro Streams (${reason}). Retry the command after checking network connectivity.`);
141007
- }
141008
- }
141009
141095
  function buildCliHistoryInputConfig(args2) {
141010
141096
  return {
141011
141097
  prompt: args2.prompt,
@@ -141420,13 +141506,24 @@ ${entry.text}`).join("\n\n");
141420
141506
  } catch {
141421
141507
  }
141422
141508
  }
141423
- async function writeDispatchPointerAndSync(args2) {
141509
+ async function writeDispatchPointer(args2) {
141424
141510
  await args2.manager.repo.upsertDocMeta(getSessionRoomId(args2.sessionId), {
141425
141511
  latestUserMsgId: args2.userTurnId,
141426
141512
  lastMissingHistoryUserMsgId: void 0
141427
141513
  });
141428
- await ensureSessionDocSynced(args2.sessionDoc, `${args2.reason}:doc`);
141429
- await ensureWorkspaceMetaSynced(args2.manager, `${args2.reason}:meta`);
141514
+ }
141515
+ async function confirmDispatchSyncedBestEffort(args2) {
141516
+ const logger2 = args2.logger ?? getLogger("session");
141517
+ try {
141518
+ const synced = await args2.sessionDoc.waitUntilSynced();
141519
+ if (!synced) {
141520
+ logger2.warn(`Session dispatch not yet confirmed by Loro Streams (${args2.reason}:doc); the durable dispatch pointer will converge on reconnect.`);
141521
+ return;
141522
+ }
141523
+ await ensureWorkspaceMetaSynced(args2.manager, `${args2.reason}:meta`);
141524
+ } catch (error2) {
141525
+ logger2.warn(`Session dispatch confirmation did not complete (${args2.reason}); the durable dispatch pointer will converge on reconnect: ${formatErrorMessage(error2)}`);
141526
+ }
141430
141527
  }
141431
141528
  async function listMachineMetasForWorkspace(manager) {
141432
141529
  return (await listAliveDocMetas(manager, isMachineDocRoomId)).map((entry) => entry.meta);
@@ -141954,6 +142051,7 @@ ${entry.text}`).join("\n\n");
141954
142051
  });
141955
142052
  let completionAbortController;
141956
142053
  let completionPromise;
142054
+ let dispatched = false;
141957
142055
  try {
141958
142056
  const modeId = effectiveDispatchConfig.modeId;
141959
142057
  const modelId = effectiveDispatchConfig.modelId;
@@ -141981,11 +142079,15 @@ ${entry.text}`).join("\n\n");
141981
142079
  await manager.repo.upsertDocMeta(sessionRoomId, {
141982
142080
  status: SessionStatusFactory.idle()
141983
142081
  });
141984
- await writeDispatchPointerAndSync({
142082
+ await writeDispatchPointer({
141985
142083
  manager,
141986
- sessionDoc,
141987
142084
  sessionId,
141988
- userTurnId,
142085
+ userTurnId
142086
+ });
142087
+ dispatched = true;
142088
+ await confirmDispatchSyncedBestEffort({
142089
+ manager,
142090
+ sessionDoc,
141989
142091
  reason: `session.create:${sessionId}`
141990
142092
  });
141991
142093
  await dispatchTurnFastPath({
@@ -142018,7 +142120,9 @@ ${entry.text}`).join("\n\n");
142018
142120
  } catch (error2) {
142019
142121
  completionAbortController?.abort();
142020
142122
  await completionPromise?.catch(() => void 0);
142021
- await rollbackPendingSessionCreate(manager, sessionId);
142123
+ if (!dispatched) {
142124
+ await rollbackPendingSessionCreate(manager, sessionId);
142125
+ }
142022
142126
  throw error2;
142023
142127
  }
142024
142128
  }
@@ -142085,13 +142189,18 @@ ${entry.text}`).join("\n\n");
142085
142189
  signal: completionAbortController?.signal,
142086
142190
  onEvent: structuredOutput.onEvent
142087
142191
  }) : void 0;
142192
+ let dispatched = false;
142088
142193
  try {
142089
142194
  await updateSessionActivityTimestampsBestEffort(manager, sessionId);
142090
- await writeDispatchPointerAndSync({
142195
+ await writeDispatchPointer({
142091
142196
  manager,
142092
- sessionDoc,
142093
142197
  sessionId,
142094
- userTurnId,
142198
+ userTurnId
142199
+ });
142200
+ dispatched = true;
142201
+ await confirmDispatchSyncedBestEffort({
142202
+ manager,
142203
+ sessionDoc,
142095
142204
  reason: `session.chat:${sessionId}:${userTurnId}`
142096
142205
  });
142097
142206
  await dispatchTurnFastPath({
@@ -142114,11 +142223,13 @@ ${entry.text}`).join("\n\n");
142114
142223
  } catch (error2) {
142115
142224
  completionAbortController?.abort();
142116
142225
  await completionPromise?.catch(() => void 0);
142117
- await removeHistoryEntryById(sessionDoc, userTurnId);
142118
- await manager.repo.upsertDocMeta(getSessionRoomId(sessionId), {
142119
- latestUserMsgId: void 0,
142120
- lastMissingHistoryUserMsgId: void 0
142121
- }).catch(() => void 0);
142226
+ if (!dispatched) {
142227
+ await removeHistoryEntryById(sessionDoc, userTurnId);
142228
+ await manager.repo.upsertDocMeta(getSessionRoomId(sessionId), {
142229
+ latestUserMsgId: void 0,
142230
+ lastMissingHistoryUserMsgId: void 0
142231
+ }).catch(() => void 0);
142232
+ }
142122
142233
  throw error2;
142123
142234
  }
142124
142235
  }
@@ -143370,7 +143481,7 @@ ${entry.text}`).join("\n\n");
143370
143481
  } catch (error2) {
143371
143482
  this.deps.logger.debug(`[${sessionId}] Failed to compute git diff stats:`, error2);
143372
143483
  }
143373
- let workspaceDirty = false;
143484
+ let workspaceDirty;
143374
143485
  try {
143375
143486
  workspaceDirty = await isWorkspaceDirty(runGit2);
143376
143487
  this.deps.logger.debug(`[${sessionId}] Workspace dirty: ${workspaceDirty}`);
@@ -143381,9 +143492,11 @@ ${entry.text}`).join("\n\n");
143381
143492
  const sessionDoc = await this.deps.workspaceDocument.getOrCreateSessionDoc(sessionId);
143382
143493
  const workspace = await this.resolveWorkspaceSessionContext(sessionId, sessionDoc);
143383
143494
  const metaPatch = {
143384
- diffStats,
143385
- workspaceDirty
143495
+ diffStats
143386
143496
  };
143497
+ if (workspaceDirty !== void 0) {
143498
+ metaPatch.workspaceDirty = workspaceDirty;
143499
+ }
143387
143500
  await this.deps.workspaceDocument.repo.upsertDocMeta(workspace.ownerRoomId, metaPatch);
143388
143501
  } catch (error2) {
143389
143502
  this.deps.logger.debug(`[${sessionId}] Failed to persist session meta diffStats: ${formatErrorMessage(error2)}`);
@@ -144451,11 +144564,11 @@ ${entry.text}`).join("\n\n");
144451
144564
  });
144452
144565
  }
144453
144566
  const workspaceRoot = path__default$1.resolve(resolvedWorkspace.workspaceRoot);
144454
- const workspacePath = normalizeWorkspacePath(requestedPath, options);
144567
+ const workspacePath = normalizeWorkspacePath(resolveWorkspaceRelativeRequestPath(workspaceRoot, requestedPath), options);
144455
144568
  const requestedAbsolutePath = path__default$1.resolve(workspaceRoot, workspacePath);
144456
144569
  const absolutePath = await resolveExistingPathWithoutConflicts(workspaceRoot, workspacePath, requestedPath) ?? requestedAbsolutePath;
144457
144570
  const relative2 = path__default$1.relative(workspaceRoot, absolutePath);
144458
- if (relative2.startsWith("..") || path__default$1.isAbsolute(relative2)) {
144571
+ if (isPathOutsideRoot(relative2)) {
144459
144572
  throw new CodeCollabV2ServiceError("invalid_path", "Path escapes workspace root.", {
144460
144573
  path: requestedPath
144461
144574
  });
@@ -145140,6 +145253,21 @@ ${entry.text}`).join("\n\n");
145140
145253
  }
145141
145254
  return normalized;
145142
145255
  }
145256
+ function resolveWorkspaceRelativeRequestPath(workspaceRoot, input2) {
145257
+ if (!path__default$1.isAbsolute(input2)) {
145258
+ return input2;
145259
+ }
145260
+ const relative2 = path__default$1.relative(workspaceRoot, path__default$1.resolve(input2));
145261
+ if (isPathOutsideRoot(relative2)) {
145262
+ throw new CodeCollabV2ServiceError("invalid_path", "Path escapes workspace root.", {
145263
+ path: input2
145264
+ });
145265
+ }
145266
+ return relative2.split(path__default$1.sep).join("/");
145267
+ }
145268
+ function isPathOutsideRoot(relativePath) {
145269
+ return relativePath === ".." || relativePath.startsWith(`..${path__default$1.sep}`) || path__default$1.isAbsolute(relativePath);
145270
+ }
145143
145271
  async function stableReadFile(resolved, maxBytes) {
145144
145272
  let lastTransientError;
145145
145273
  for (let attempt = 0; attempt < 3; attempt += 1) {
@@ -145256,7 +145384,7 @@ ${entry.text}`).join("\n\n");
145256
145384
  throw mapNodeReadError(error2, resolved.workspacePath);
145257
145385
  });
145258
145386
  const relative2 = path__default$1.relative(rootRealpath, fileRealpath);
145259
- if (relative2.startsWith("..") || path__default$1.isAbsolute(relative2)) {
145387
+ if (isPathOutsideRoot(relative2)) {
145260
145388
  throw new CodeCollabV2ServiceError("invalid_path", "Resolved path escapes workspace root.", {
145261
145389
  path: resolved.workspacePath
145262
145390
  });
@@ -153340,7 +153468,10 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
153340
153468
  return {
153341
153469
  ...entry,
153342
153470
  userTurnId: entry.userTurnId ?? userTurnId,
153343
- modelInfo: modelInfo ?? entry.modelInfo
153471
+ modelInfo: modelInfo ?? entry.modelInfo,
153472
+ finished: false,
153473
+ endedAt: void 0,
153474
+ permissionWaitMs: void 0
153344
153475
  };
153345
153476
  });
153346
153477
  }
@@ -155590,7 +155721,9 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
155590
155721
  this.logger.debug(`[${sessionId}] Failed to delete session doc (may already be deleted): ${formatErrorMessage(error2)}`);
155591
155722
  }
155592
155723
  try {
155593
- const operationStore = new LodyOperationStore(getLodyOperationStorePath(this.machineId));
155724
+ const operationStore = new LodyOperationStore(getLodyOperationStorePath(this.machineId), void 0, {
155725
+ maintenance: false
155726
+ });
155594
155727
  try {
155595
155728
  operationStore.deleteRequesterSession(sessionId);
155596
155729
  } finally {
@@ -203332,7 +203465,7 @@ ${page}${helpTipBottom}${choiceDescription}${ansiEscapes.cursorHide}`;
203332
203465
  data = createReviewBundleSnapshot(bundle);
203333
203466
  }
203334
203467
  const { injectReviewSnapshot } = await import("./chunks/index-VoI6Ds2-.js");
203335
- const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-CP4bkTWs.js");
203468
+ const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-B9Tm-8O4.js");
203336
203469
  const template = await resolveReviewViewerTemplate();
203337
203470
  const html = injectReviewSnapshot(template, data);
203338
203471
  const outputPath = options.output ? path__default$1.resolve(options.output) : defaultHtmlOutputPath(inputPath);
@@ -221230,14 +221363,14 @@ ${result.stderr}`;
221230
221363
  localControlSocketPath: readOptionalEnv("LODY_MCP_SOCKET_PATH", "LODY_PREVIEW_MCP_SOCKET_PATH"),
221231
221364
  workdir: readOptionalEnv("LODY_MCP_WORKDIR", "LODY_PREVIEW_MCP_WORKDIR") ?? process.cwd()
221232
221365
  });
221233
- const withOperationStore = (fn) => {
221234
- const store = new LodyOperationStore();
221235
- try {
221236
- return fn(store);
221237
- } finally {
221238
- store.close();
221239
- }
221366
+ let sharedOperationStore;
221367
+ const getSharedOperationStore = () => {
221368
+ sharedOperationStore ??= new LodyOperationStore(void 0, void 0, {
221369
+ maintenance: false
221370
+ });
221371
+ return sharedOperationStore;
221240
221372
  };
221373
+ const withOperationStore = (fn) => runWithOperationStoreBusyRetry(() => fn(getSharedOperationStore()));
221241
221374
  const assertBatchSize = (length2, maximum) => {
221242
221375
  if (length2 < 1 || length2 > maximum) {
221243
221376
  throw new LodyOperationStoreError("BATCH_TOO_LARGE", `Batch item count must be between 1 and ${maximum}; received ${length2}.`, false);
@@ -222074,8 +222207,8 @@ ${result.stderr}`;
222074
222207
  deadlineSeconds: args2.deadlineSeconds
222075
222208
  } : {}
222076
222209
  };
222077
- const retry2 = withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_create", canonicalCommand));
222078
- if (retry2) return withOperationStore((store) => store.snapshot(retry2));
222210
+ const retry2 = await withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_create", canonicalCommand));
222211
+ if (retry2) return await withOperationStore((store) => store.snapshot(retry2));
222079
222212
  const targetMachineId = args2.machineId ?? currentSession.machineId;
222080
222213
  await assertMachineOnlineForSingleCommand(manager, targetMachineId, ctx);
222081
222214
  const createOptions = buildMcpCreateOptions(args2, ctx);
@@ -222096,7 +222229,7 @@ ${result.stderr}`;
222096
222229
  const preallocatedUserTurnId = randomUUID();
222097
222230
  const materializationClaimToken = randomUUID();
222098
222231
  const timing = operationDeadline(args2.deadlineSeconds);
222099
- const accepted = withOperationStore((store) => store.accept({
222232
+ const accepted = await withOperationStore((store) => store.accept({
222100
222233
  workspaceId: workspace.id,
222101
222234
  ownerMachineId: ctx.machineId,
222102
222235
  requesterSessionId: ctx.sessionId,
@@ -222119,7 +222252,7 @@ ${result.stderr}`;
222119
222252
  materializationClaimToken
222120
222253
  }));
222121
222254
  if (accepted.operation.state === "finished") {
222122
- return withOperationStore((store) => store.snapshot(accepted.operation));
222255
+ return await withOperationStore((store) => store.snapshot(accepted.operation));
222123
222256
  }
222124
222257
  const pendingItem = accepted.operation.items[0];
222125
222258
  if (!pendingItem || pendingItem.status !== "active") {
@@ -222133,7 +222266,7 @@ ${result.stderr}`;
222133
222266
  if (result.sessionId !== pendingItem.target.sessionId || result.userTurnId !== pendingItem.target.userTurnId) {
222134
222267
  throw new Error("Create result did not preserve preallocated target ids.");
222135
222268
  }
222136
- withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, 0, materializationClaimToken));
222269
+ await withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, 0, materializationClaimToken));
222137
222270
  }
222138
222271
  return snapshotOperation(ctx.sessionId, args2.operationId);
222139
222272
  });
@@ -222158,8 +222291,8 @@ ${result.stderr}`;
222158
222291
  deadlineSeconds: args2.deadlineSeconds
222159
222292
  } : {}
222160
222293
  };
222161
- const retry2 = withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_chat", canonicalCommand));
222162
- if (retry2) return withOperationStore((store) => store.snapshot(retry2));
222294
+ const retry2 = await withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_chat", canonicalCommand));
222295
+ if (retry2) return await withOperationStore((store) => store.snapshot(retry2));
222163
222296
  const targetSession = await readCurrentSessionMeta(manager, args2.sessionId);
222164
222297
  if (!targetSession) {
222165
222298
  throw new LodyOperationStoreError("SESSION_NOT_FOUND", `Target Session not found: ${args2.sessionId}`, false);
@@ -222181,7 +222314,7 @@ ${result.stderr}`;
222181
222314
  const preallocatedUserTurnId = randomUUID();
222182
222315
  const materializationClaimToken = randomUUID();
222183
222316
  const timing = operationDeadline(args2.deadlineSeconds);
222184
- const accepted = withOperationStore((store) => store.accept({
222317
+ const accepted = await withOperationStore((store) => store.accept({
222185
222318
  workspaceId: workspace.id,
222186
222319
  ownerMachineId: ctx.machineId,
222187
222320
  requesterSessionId: ctx.sessionId,
@@ -222204,7 +222337,7 @@ ${result.stderr}`;
222204
222337
  materializationClaimToken
222205
222338
  }));
222206
222339
  if (accepted.operation.state === "finished") {
222207
- return withOperationStore((store) => store.snapshot(accepted.operation));
222340
+ return await withOperationStore((store) => store.snapshot(accepted.operation));
222208
222341
  }
222209
222342
  const pendingItem = accepted.operation.items[0];
222210
222343
  if (!pendingItem || pendingItem.status !== "active") {
@@ -222218,7 +222351,7 @@ ${result.stderr}`;
222218
222351
  if (result.userTurnId !== pendingItem.target.userTurnId) {
222219
222352
  throw new Error("Chat result did not preserve the preallocated target turn id.");
222220
222353
  }
222221
- withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, 0, materializationClaimToken));
222354
+ await withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, 0, materializationClaimToken));
222222
222355
  }
222223
222356
  return snapshotOperation(ctx.sessionId, args2.operationId);
222224
222357
  });
@@ -222261,11 +222394,11 @@ ${result.stderr}`;
222261
222394
  } : {},
222262
222395
  error: makeLodyError(code2, message, retryable)
222263
222396
  });
222264
- const finishOperationWhenEveryItemIsTerminal = (requesterSessionId, operationId, items2) => {
222397
+ const finishOperationWhenEveryItemIsTerminal = async (requesterSessionId, operationId, items2) => {
222265
222398
  if (items2.some((item) => item.status === "active")) {
222266
222399
  return;
222267
222400
  }
222268
- withOperationStore((store) => store.finish(requesterSessionId, operationId, {
222401
+ await withOperationStore((store) => store.finish(requesterSessionId, operationId, {
222269
222402
  type: "result",
222270
222403
  value: {
222271
222404
  items: items2
@@ -222293,8 +222426,8 @@ ${result.stderr}`;
222293
222426
  deadlineSeconds: args2.deadlineSeconds
222294
222427
  } : {}
222295
222428
  };
222296
- const retry2 = withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_create_many", canonicalCommand));
222297
- if (retry2) return withOperationStore((store) => store.snapshot(retry2));
222429
+ const retry2 = await withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_create_many", canonicalCommand));
222430
+ if (retry2) return await withOperationStore((store) => store.snapshot(retry2));
222298
222431
  const invoking = await resolveInvokingTurnContext(manager, requester);
222299
222432
  const initialItems = await mapWithConcurrency(expanded, 5, async (item) => {
222300
222433
  const label2 = item.label;
@@ -222341,7 +222474,7 @@ ${result.stderr}`;
222341
222474
  });
222342
222475
  const materializationClaimToken = randomUUID();
222343
222476
  const timing = operationDeadline(args2.deadlineSeconds);
222344
- const accepted = withOperationStore((store) => store.accept({
222477
+ const accepted = await withOperationStore((store) => store.accept({
222345
222478
  workspaceId: workspace.id,
222346
222479
  ownerMachineId: ctx.machineId,
222347
222480
  requesterSessionId: ctx.sessionId,
@@ -222362,7 +222495,7 @@ ${result.stderr}`;
222362
222495
  materializationClaimToken
222363
222496
  }));
222364
222497
  if (accepted.operation.state === "finished") {
222365
- return withOperationStore((store) => store.snapshot(accepted.operation));
222498
+ return await withOperationStore((store) => store.snapshot(accepted.operation));
222366
222499
  }
222367
222500
  const nextItems = await mapWithConcurrency(accepted.operation.items, 5, async (storedItem, index) => {
222368
222501
  if (storedItem.status !== "active" || storedItem.inputDurable || !accepted.claimedItemIndexes.includes(index)) {
@@ -222396,13 +222529,13 @@ ${result.stderr}`;
222396
222529
  options.userTurnId = storedItem.target.userTurnId;
222397
222530
  options.chainDepth = invoking.chainDepth + 1;
222398
222531
  await createSessionResult(auth, workspace, manager, expandedItem.prompt, options, resolveTurnDispatchConfig({}));
222399
- withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, index, materializationClaimToken));
222532
+ await withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, index, materializationClaimToken));
222400
222533
  return markOperationItemInputDurable(storedItem);
222401
222534
  } catch {
222402
222535
  return storedItem;
222403
222536
  }
222404
222537
  });
222405
- finishOperationWhenEveryItemIsTerminal(ctx.sessionId, args2.operationId, nextItems);
222538
+ await finishOperationWhenEveryItemIsTerminal(ctx.sessionId, args2.operationId, nextItems);
222406
222539
  return snapshotOperation(ctx.sessionId, args2.operationId);
222407
222540
  });
222408
222541
  };
@@ -222427,8 +222560,8 @@ ${result.stderr}`;
222427
222560
  deadlineSeconds: args2.deadlineSeconds
222428
222561
  } : {}
222429
222562
  };
222430
- const retry2 = withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_chat_many", canonicalCommand));
222431
- if (retry2) return withOperationStore((store) => store.snapshot(retry2));
222563
+ const retry2 = await withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_chat_many", canonicalCommand));
222564
+ if (retry2) return await withOperationStore((store) => store.snapshot(retry2));
222432
222565
  const invoking = await resolveInvokingTurnContext(manager, requester);
222433
222566
  const initialItems = await mapWithConcurrency(expanded, 5, async (item) => {
222434
222567
  if (!item.sessionId || !item.prompt) {
@@ -222459,7 +222592,7 @@ ${result.stderr}`;
222459
222592
  });
222460
222593
  const materializationClaimToken = randomUUID();
222461
222594
  const timing = operationDeadline(args2.deadlineSeconds);
222462
- const accepted = withOperationStore((store) => store.accept({
222595
+ const accepted = await withOperationStore((store) => store.accept({
222463
222596
  workspaceId: workspace.id,
222464
222597
  ownerMachineId: ctx.machineId,
222465
222598
  requesterSessionId: ctx.sessionId,
@@ -222480,7 +222613,7 @@ ${result.stderr}`;
222480
222613
  materializationClaimToken
222481
222614
  }));
222482
222615
  if (accepted.operation.state === "finished") {
222483
- return withOperationStore((store) => store.snapshot(accepted.operation));
222616
+ return await withOperationStore((store) => store.snapshot(accepted.operation));
222484
222617
  }
222485
222618
  const nextItems = await mapWithConcurrency(accepted.operation.items, 5, async (storedItem, index) => {
222486
222619
  if (storedItem.status !== "active" || storedItem.inputDurable || !accepted.claimedItemIndexes.includes(index)) {
@@ -222495,13 +222628,13 @@ ${result.stderr}`;
222495
222628
  userTurnId: storedItem.target.userTurnId,
222496
222629
  chainDepth: invoking.chainDepth + 1
222497
222630
  });
222498
- withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, index, materializationClaimToken));
222631
+ await withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, index, materializationClaimToken));
222499
222632
  return markOperationItemInputDurable(storedItem);
222500
222633
  } catch {
222501
222634
  return storedItem;
222502
222635
  }
222503
222636
  });
222504
- finishOperationWhenEveryItemIsTerminal(ctx.sessionId, args2.operationId, nextItems);
222637
+ await finishOperationWhenEveryItemIsTerminal(ctx.sessionId, args2.operationId, nextItems);
222505
222638
  return snapshotOperation(ctx.sessionId, args2.operationId);
222506
222639
  });
222507
222640
  };
@@ -222783,7 +222916,7 @@ ${lines2.join("\n")}` : ""}${suffix}`);
222783
222916
  }, async (args2) => {
222784
222917
  try {
222785
222918
  const ctx = getSessionContext();
222786
- return jsonTextResult(snapshotOperation(ctx.sessionId, args2.operationId));
222919
+ return jsonTextResult(await snapshotOperation(ctx.sessionId, args2.operationId));
222787
222920
  } catch (error2) {
222788
222921
  return mcpErrorResult(error2);
222789
222922
  }
@@ -222796,13 +222929,13 @@ ${lines2.join("\n")}` : ""}${suffix}`);
222796
222929
  try {
222797
222930
  const ctx = getSessionContext();
222798
222931
  const requesterSessionId = ctx.sessionId;
222799
- const before = withOperationStore((store) => store.get(requesterSessionId, args2.operationId));
222800
- const cancellation = withOperationStore((store) => store.cancel(requesterSessionId, args2.operationId));
222932
+ const before = await withOperationStore((store) => store.get(requesterSessionId, args2.operationId));
222933
+ const cancellation = await withOperationStore((store) => store.cancel(requesterSessionId, args2.operationId));
222801
222934
  if (cancellation.didCancel && before.state === "active") {
222802
222935
  const startedTargets = before.items.filter((item) => item.status === "active" && item.inputDurable);
222803
222936
  await Promise.allSettled(startedTargets.map((item) => item.status === "active" ? runLodyCliJson(buildOperationTargetCancelArgs(getMcpWorkspaceId(ctx), item)) : Promise.resolve()));
222804
222937
  }
222805
- return jsonTextResult(withOperationStore((store) => store.snapshot(cancellation.operation)));
222938
+ return jsonTextResult(await withOperationStore((store) => store.snapshot(cancellation.operation)));
222806
222939
  } catch (error2) {
222807
222940
  return mcpErrorResult(error2);
222808
222941
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lody",
3
- "version": "0.71.2",
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",
@@ -76,14 +76,14 @@
76
76
  "zod": "^4.1.5",
77
77
  "zstd-stream": "^1.1.0",
78
78
  "@lody/cli-supervisor": "0.0.1",
79
- "@lody/code-review-helper": "0.0.0",
80
- "@lody/shared": "0.0.1",
81
79
  "@lody/convex": "0.0.1",
82
80
  "@lody/loro-streams-rpc": "0.0.1",
81
+ "@lody/shared": "0.0.1",
82
+ "@lody/code-review-helper": "0.0.0",
83
83
  "acp-extension-claude": "0.59.0",
84
+ "lody-code-review-viewer": "0.71.4",
84
85
  "acp-extension-core": "0.0.5",
85
- "acp-extension-codex": "1.2.1",
86
- "lody-code-review-viewer": "0.71.2"
86
+ "acp-extension-codex": "1.2.1"
87
87
  },
88
88
  "files": [
89
89
  "dist",
@@ -106,7 +106,7 @@
106
106
  "build": "pnpm run clean && pnpm run prepare:acp-adapters && pnpm run typecheck && pnpm run build:bundle && pnpm run check:published-bundle-imports && pnpm run copy:wasm",
107
107
  "build:watch": "pnpm run prepare:review-assets && vite build --watch",
108
108
  "build:bundle": "pnpm run prepare:review-assets && vite build",
109
- "prepare:acp-adapters": "pnpm --dir ../.. --filter acp-extension-claude build && pnpm --dir ../.. --filter acp-extension-codex build",
109
+ "prepare:acp-adapters": "corepack pnpm --dir ../.. --filter acp-extension-claude build && corepack pnpm --dir ../.. --filter acp-extension-codex build",
110
110
  "prepare:review-assets": "pnpm --dir ../.. --filter lody-code-review-viewer build",
111
111
  "check:published-bundle-imports": "node scripts/check-published-bundle-imports.js",
112
112
  "copy:wasm": "node scripts/copy-loro-wasm.js",