lody 0.71.1 → 0.71.3

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.1";
6
+ const reviewViewerVersion = "0.71.3";
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"];
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.1";
4193
+ const version$7 = "0.71.3";
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;
@@ -136226,6 +136226,32 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136226
136226
  };
136227
136227
  }
136228
136228
  }
136229
+ const isOperationStoreBusyError = (error2) => {
136230
+ if (!(error2 instanceof Error)) return false;
136231
+ const code2 = error2.code;
136232
+ return code2 === "SQLITE_BUSY" || code2 === "SQLITE_BUSY_SNAPSHOT";
136233
+ };
136234
+ const BUSY_RETRY_DELAYS_MS = [
136235
+ 100,
136236
+ 300,
136237
+ 900
136238
+ ];
136239
+ const runWithOperationStoreBusyRetry = async (run2, options = {}) => {
136240
+ const delays = options.delaysMs ?? BUSY_RETRY_DELAYS_MS;
136241
+ const sleep2 = options.sleep ?? ((ms2) => new Promise((resolve2) => setTimeout(resolve2, ms2)));
136242
+ for (let attempt = 0; ; attempt += 1) {
136243
+ try {
136244
+ return run2();
136245
+ } catch (error2) {
136246
+ if (!isOperationStoreBusyError(error2)) throw error2;
136247
+ const delayMs = delays[attempt];
136248
+ if (delayMs === void 0) {
136249
+ throw new LodyOperationStoreError("STORE_BUSY", "The local Operation store is busy; retry the command.", true);
136250
+ }
136251
+ await sleep2(delayMs);
136252
+ }
136253
+ }
136254
+ };
136229
136255
  const parseJson = (text, schema2, label2) => {
136230
136256
  let parsed;
136231
136257
  try {
@@ -136361,7 +136387,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136361
136387
  const getOperationDeliveryId = (requesterSessionId, operationId) => `operation:${requesterSessionId}:${operationId}:completion`;
136362
136388
  const getOperationCompletionTurnId = (requesterSessionId, operationId) => `operation-completion:${requesterSessionId}:${operationId}`;
136363
136389
  class LodyOperationStore {
136364
- constructor(dbPath = getLodyOperationStorePath(), now2 = Date.now) {
136390
+ constructor(dbPath = getLodyOperationStorePath(), now2 = Date.now, options = {}) {
136365
136391
  this.now = now2;
136366
136392
  mkdirSync$2(path__default$1.dirname(dbPath), {
136367
136393
  recursive: true
@@ -136371,8 +136397,10 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136371
136397
  this.db.pragma("journal_mode = WAL");
136372
136398
  this.db.pragma("foreign_keys = ON");
136373
136399
  this.migrate();
136374
- this.repairTerminalDeliveries();
136375
- this.cleanupExpired(false);
136400
+ if (options.maintenance !== false) {
136401
+ this.repairTerminalDeliveries();
136402
+ this.cleanupExpired(false);
136403
+ }
136376
136404
  }
136377
136405
  db;
136378
136406
  close() {
@@ -136414,7 +136442,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136414
136442
  claimedItemIndexes
136415
136443
  };
136416
136444
  });
136417
- const accepted = transaction();
136445
+ const accepted = transaction.immediate();
136418
136446
  this.cleanupExpired(false);
136419
136447
  return accepted;
136420
136448
  }
@@ -136471,7 +136499,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136471
136499
  return {
136472
136500
  claimed: true
136473
136501
  };
136474
- })();
136502
+ }).immediate();
136475
136503
  }
136476
136504
  markItemInputDurable(requesterSessionId, operationId, itemIndex, claimToken) {
136477
136505
  return this.db.transaction(() => {
@@ -136492,7 +136520,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136492
136520
  this.db.prepare(`DELETE FROM operation_item_materializations
136493
136521
  WHERE requester_session_id = ? AND operation_id = ? AND item_index = ?`).run(requesterSessionId, operationId, itemIndex);
136494
136522
  return this.get(requesterSessionId, operationId);
136495
- })();
136523
+ }).immediate();
136496
136524
  }
136497
136525
  finish(requesterSessionId, operationId, completion, finishedAt = new Date(this.now()).toISOString()) {
136498
136526
  const bounded = this.assertCompletionBound(completion);
@@ -136518,7 +136546,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136518
136546
  }
136519
136547
  return updated;
136520
136548
  });
136521
- return transaction();
136549
+ return transaction.immediate();
136522
136550
  }
136523
136551
  cancel(requesterSessionId, operationId) {
136524
136552
  const transaction = this.db.transaction(() => {
@@ -136542,7 +136570,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136542
136570
  didCancel: true
136543
136571
  };
136544
136572
  });
136545
- return transaction();
136573
+ return transaction.immediate();
136546
136574
  }
136547
136575
  listPendingDeliveries(workspaceId, requesterSessionId) {
136548
136576
  const rows = requesterSessionId ? this.db.prepare(`SELECT * FROM deliveries
@@ -136690,7 +136718,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136690
136718
  )`).run(cutoff);
136691
136719
  this.db.prepare(`INSERT INTO orchestration_meta (key, value) VALUES ('last_cleanup_at_ms', ?)
136692
136720
  ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(String(nowMs2));
136693
- })();
136721
+ }).immediate();
136694
136722
  }
136695
136723
  migrate() {
136696
136724
  this.db.exec(`
@@ -136774,7 +136802,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136774
136802
  WHERE deliveries.requester_session_id = operations.requester_session_id
136775
136803
  AND deliveries.operation_id = operations.operation_id
136776
136804
  )`).run();
136777
- })();
136805
+ }).immediate();
136778
136806
  }
136779
136807
  }
136780
136808
  const terminalAssistantFor = (history, userTurnId) => history.find((entry) => entry.role === "assistant" && entry.userTurnId === userTurnId && (entry.finished === true || typeof entry.endedAt === "number"));
@@ -136800,6 +136828,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136800
136828
  deliveryChains = /* @__PURE__ */ new Map();
136801
136829
  operationAbortControllers = /* @__PURE__ */ new Map();
136802
136830
  metaWatch = null;
136831
+ store = null;
136803
136832
  storeWatch = null;
136804
136833
  storeWakeTimer = null;
136805
136834
  started = false;
@@ -136815,15 +136844,15 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136815
136844
  "doc-metadata"
136816
136845
  ]
136817
136846
  });
136818
- this.withStore(() => void 0);
136847
+ this.store = this.storeFactory();
136819
136848
  const storePath = this.options.storePath ?? getLodyOperationStorePath(this.options.machineId);
136820
136849
  const storeBasename = path__default$1.basename(storePath);
136821
136850
  this.storeWatch = watch(path__default$1.dirname(storePath), (_event, filename) => {
136822
136851
  if (!filename || !filename.toString().startsWith(storeBasename)) return;
136823
- this.abortTerminalCoordinators();
136824
- if (this.storeWakeTimer) clearTimeout(this.storeWakeTimer);
136852
+ if (this.storeWakeTimer) return;
136825
136853
  this.storeWakeTimer = setTimeout(() => {
136826
136854
  this.storeWakeTimer = null;
136855
+ this.abortTerminalCoordinators();
136827
136856
  this.wake("operation-store");
136828
136857
  }, 10);
136829
136858
  this.storeWakeTimer.unref?.();
@@ -136838,6 +136867,8 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136838
136867
  this.storeWatch = null;
136839
136868
  if (this.storeWakeTimer) clearTimeout(this.storeWakeTimer);
136840
136869
  this.storeWakeTimer = null;
136870
+ this.store?.close();
136871
+ this.store = null;
136841
136872
  for (const subscription of this.targetSubscriptions.values()) {
136842
136873
  subscription.unsubscribe();
136843
136874
  }
@@ -136877,6 +136908,7 @@ The postId is ${normalizedFeedbackPostId}. Use the feedback-progress-reporter sk
136877
136908
  ]);
136878
136909
  }
136879
136910
  withStore(fn) {
136911
+ if (this.store) return fn(this.store);
136880
136912
  const store = this.storeFactory();
136881
136913
  try {
136882
136914
  return fn(store);
@@ -140996,12 +141028,6 @@ ${entry.text}`).join("\n\n");
140996
141028
  } : {}
140997
141029
  };
140998
141030
  }
140999
- async function ensureSessionDocSynced(sessionDoc, reason) {
141000
- const synced = await sessionDoc.waitUntilSynced();
141001
- if (!synced) {
141002
- throw new Error(`Session document changes were not confirmed by Loro Streams (${reason}). Retry the command after checking network connectivity.`);
141003
- }
141004
- }
141005
141031
  function buildCliHistoryInputConfig(args2) {
141006
141032
  return {
141007
141033
  prompt: args2.prompt,
@@ -141416,13 +141442,24 @@ ${entry.text}`).join("\n\n");
141416
141442
  } catch {
141417
141443
  }
141418
141444
  }
141419
- async function writeDispatchPointerAndSync(args2) {
141445
+ async function writeDispatchPointer(args2) {
141420
141446
  await args2.manager.repo.upsertDocMeta(getSessionRoomId(args2.sessionId), {
141421
141447
  latestUserMsgId: args2.userTurnId,
141422
141448
  lastMissingHistoryUserMsgId: void 0
141423
141449
  });
141424
- await ensureSessionDocSynced(args2.sessionDoc, `${args2.reason}:doc`);
141425
- await ensureWorkspaceMetaSynced(args2.manager, `${args2.reason}:meta`);
141450
+ }
141451
+ async function confirmDispatchSyncedBestEffort(args2) {
141452
+ const logger2 = args2.logger ?? getLogger("session");
141453
+ try {
141454
+ const synced = await args2.sessionDoc.waitUntilSynced();
141455
+ if (!synced) {
141456
+ logger2.warn(`Session dispatch not yet confirmed by Loro Streams (${args2.reason}:doc); the durable dispatch pointer will converge on reconnect.`);
141457
+ return;
141458
+ }
141459
+ await ensureWorkspaceMetaSynced(args2.manager, `${args2.reason}:meta`);
141460
+ } catch (error2) {
141461
+ logger2.warn(`Session dispatch confirmation did not complete (${args2.reason}); the durable dispatch pointer will converge on reconnect: ${formatErrorMessage(error2)}`);
141462
+ }
141426
141463
  }
141427
141464
  async function listMachineMetasForWorkspace(manager) {
141428
141465
  return (await listAliveDocMetas(manager, isMachineDocRoomId)).map((entry) => entry.meta);
@@ -141950,6 +141987,7 @@ ${entry.text}`).join("\n\n");
141950
141987
  });
141951
141988
  let completionAbortController;
141952
141989
  let completionPromise;
141990
+ let dispatched = false;
141953
141991
  try {
141954
141992
  const modeId = effectiveDispatchConfig.modeId;
141955
141993
  const modelId = effectiveDispatchConfig.modelId;
@@ -141977,11 +142015,15 @@ ${entry.text}`).join("\n\n");
141977
142015
  await manager.repo.upsertDocMeta(sessionRoomId, {
141978
142016
  status: SessionStatusFactory.idle()
141979
142017
  });
141980
- await writeDispatchPointerAndSync({
142018
+ await writeDispatchPointer({
141981
142019
  manager,
141982
- sessionDoc,
141983
142020
  sessionId,
141984
- userTurnId,
142021
+ userTurnId
142022
+ });
142023
+ dispatched = true;
142024
+ await confirmDispatchSyncedBestEffort({
142025
+ manager,
142026
+ sessionDoc,
141985
142027
  reason: `session.create:${sessionId}`
141986
142028
  });
141987
142029
  await dispatchTurnFastPath({
@@ -142014,7 +142056,9 @@ ${entry.text}`).join("\n\n");
142014
142056
  } catch (error2) {
142015
142057
  completionAbortController?.abort();
142016
142058
  await completionPromise?.catch(() => void 0);
142017
- await rollbackPendingSessionCreate(manager, sessionId);
142059
+ if (!dispatched) {
142060
+ await rollbackPendingSessionCreate(manager, sessionId);
142061
+ }
142018
142062
  throw error2;
142019
142063
  }
142020
142064
  }
@@ -142081,13 +142125,18 @@ ${entry.text}`).join("\n\n");
142081
142125
  signal: completionAbortController?.signal,
142082
142126
  onEvent: structuredOutput.onEvent
142083
142127
  }) : void 0;
142128
+ let dispatched = false;
142084
142129
  try {
142085
142130
  await updateSessionActivityTimestampsBestEffort(manager, sessionId);
142086
- await writeDispatchPointerAndSync({
142131
+ await writeDispatchPointer({
142087
142132
  manager,
142088
- sessionDoc,
142089
142133
  sessionId,
142090
- userTurnId,
142134
+ userTurnId
142135
+ });
142136
+ dispatched = true;
142137
+ await confirmDispatchSyncedBestEffort({
142138
+ manager,
142139
+ sessionDoc,
142091
142140
  reason: `session.chat:${sessionId}:${userTurnId}`
142092
142141
  });
142093
142142
  await dispatchTurnFastPath({
@@ -142110,11 +142159,13 @@ ${entry.text}`).join("\n\n");
142110
142159
  } catch (error2) {
142111
142160
  completionAbortController?.abort();
142112
142161
  await completionPromise?.catch(() => void 0);
142113
- await removeHistoryEntryById(sessionDoc, userTurnId);
142114
- await manager.repo.upsertDocMeta(getSessionRoomId(sessionId), {
142115
- latestUserMsgId: void 0,
142116
- lastMissingHistoryUserMsgId: void 0
142117
- }).catch(() => void 0);
142162
+ if (!dispatched) {
142163
+ await removeHistoryEntryById(sessionDoc, userTurnId);
142164
+ await manager.repo.upsertDocMeta(getSessionRoomId(sessionId), {
142165
+ latestUserMsgId: void 0,
142166
+ lastMissingHistoryUserMsgId: void 0
142167
+ }).catch(() => void 0);
142168
+ }
142118
142169
  throw error2;
142119
142170
  }
142120
142171
  }
@@ -143470,7 +143521,7 @@ ${entry.text}`).join("\n\n");
143470
143521
  this.deps.logger.debug(`[${sessionId}] auto-commit-push: turn cancelled during ${stage}; skipping remaining work`);
143471
143522
  return true;
143472
143523
  };
143473
- if (!resolveProjectGitHubRepo(project)) {
143524
+ if (!resolveProjectGitHubRepo(project) || project?.kind === "local" && project.useWorktree !== true) {
143474
143525
  return;
143475
143526
  }
143476
143527
  const workspace = await this.resolveWorkspaceSessionContext(sessionId, sessionDoc);
@@ -144447,11 +144498,11 @@ ${entry.text}`).join("\n\n");
144447
144498
  });
144448
144499
  }
144449
144500
  const workspaceRoot = path__default$1.resolve(resolvedWorkspace.workspaceRoot);
144450
- const workspacePath = normalizeWorkspacePath(requestedPath, options);
144501
+ const workspacePath = normalizeWorkspacePath(resolveWorkspaceRelativeRequestPath(workspaceRoot, requestedPath), options);
144451
144502
  const requestedAbsolutePath = path__default$1.resolve(workspaceRoot, workspacePath);
144452
144503
  const absolutePath = await resolveExistingPathWithoutConflicts(workspaceRoot, workspacePath, requestedPath) ?? requestedAbsolutePath;
144453
144504
  const relative2 = path__default$1.relative(workspaceRoot, absolutePath);
144454
- if (relative2.startsWith("..") || path__default$1.isAbsolute(relative2)) {
144505
+ if (isPathOutsideRoot(relative2)) {
144455
144506
  throw new CodeCollabV2ServiceError("invalid_path", "Path escapes workspace root.", {
144456
144507
  path: requestedPath
144457
144508
  });
@@ -145136,6 +145187,21 @@ ${entry.text}`).join("\n\n");
145136
145187
  }
145137
145188
  return normalized;
145138
145189
  }
145190
+ function resolveWorkspaceRelativeRequestPath(workspaceRoot, input2) {
145191
+ if (!path__default$1.isAbsolute(input2)) {
145192
+ return input2;
145193
+ }
145194
+ const relative2 = path__default$1.relative(workspaceRoot, path__default$1.resolve(input2));
145195
+ if (isPathOutsideRoot(relative2)) {
145196
+ throw new CodeCollabV2ServiceError("invalid_path", "Path escapes workspace root.", {
145197
+ path: input2
145198
+ });
145199
+ }
145200
+ return relative2.split(path__default$1.sep).join("/");
145201
+ }
145202
+ function isPathOutsideRoot(relativePath) {
145203
+ return relativePath === ".." || relativePath.startsWith(`..${path__default$1.sep}`) || path__default$1.isAbsolute(relativePath);
145204
+ }
145139
145205
  async function stableReadFile(resolved, maxBytes) {
145140
145206
  let lastTransientError;
145141
145207
  for (let attempt = 0; attempt < 3; attempt += 1) {
@@ -145252,7 +145318,7 @@ ${entry.text}`).join("\n\n");
145252
145318
  throw mapNodeReadError(error2, resolved.workspacePath);
145253
145319
  });
145254
145320
  const relative2 = path__default$1.relative(rootRealpath, fileRealpath);
145255
- if (relative2.startsWith("..") || path__default$1.isAbsolute(relative2)) {
145321
+ if (isPathOutsideRoot(relative2)) {
145256
145322
  throw new CodeCollabV2ServiceError("invalid_path", "Resolved path escapes workspace root.", {
145257
145323
  path: resolved.workspacePath
145258
145324
  });
@@ -155586,7 +155652,9 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
155586
155652
  this.logger.debug(`[${sessionId}] Failed to delete session doc (may already be deleted): ${formatErrorMessage(error2)}`);
155587
155653
  }
155588
155654
  try {
155589
- const operationStore = new LodyOperationStore(getLodyOperationStorePath(this.machineId));
155655
+ const operationStore = new LodyOperationStore(getLodyOperationStorePath(this.machineId), void 0, {
155656
+ maintenance: false
155657
+ });
155590
155658
  try {
155591
155659
  operationStore.deleteRequesterSession(sessionId);
155592
155660
  } finally {
@@ -203328,7 +203396,7 @@ ${page}${helpTipBottom}${choiceDescription}${ansiEscapes.cursorHide}`;
203328
203396
  data = createReviewBundleSnapshot(bundle);
203329
203397
  }
203330
203398
  const { injectReviewSnapshot } = await import("./chunks/index-VoI6Ds2-.js");
203331
- const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-By7iC3mo.js");
203399
+ const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-S-XYMgL_.js");
203332
203400
  const template = await resolveReviewViewerTemplate();
203333
203401
  const html = injectReviewSnapshot(template, data);
203334
203402
  const outputPath = options.output ? path__default$1.resolve(options.output) : defaultHtmlOutputPath(inputPath);
@@ -221226,14 +221294,14 @@ ${result.stderr}`;
221226
221294
  localControlSocketPath: readOptionalEnv("LODY_MCP_SOCKET_PATH", "LODY_PREVIEW_MCP_SOCKET_PATH"),
221227
221295
  workdir: readOptionalEnv("LODY_MCP_WORKDIR", "LODY_PREVIEW_MCP_WORKDIR") ?? process.cwd()
221228
221296
  });
221229
- const withOperationStore = (fn) => {
221230
- const store = new LodyOperationStore();
221231
- try {
221232
- return fn(store);
221233
- } finally {
221234
- store.close();
221235
- }
221297
+ let sharedOperationStore;
221298
+ const getSharedOperationStore = () => {
221299
+ sharedOperationStore ??= new LodyOperationStore(void 0, void 0, {
221300
+ maintenance: false
221301
+ });
221302
+ return sharedOperationStore;
221236
221303
  };
221304
+ const withOperationStore = (fn) => runWithOperationStoreBusyRetry(() => fn(getSharedOperationStore()));
221237
221305
  const assertBatchSize = (length2, maximum) => {
221238
221306
  if (length2 < 1 || length2 > maximum) {
221239
221307
  throw new LodyOperationStoreError("BATCH_TOO_LARGE", `Batch item count must be between 1 and ${maximum}; received ${length2}.`, false);
@@ -222070,8 +222138,8 @@ ${result.stderr}`;
222070
222138
  deadlineSeconds: args2.deadlineSeconds
222071
222139
  } : {}
222072
222140
  };
222073
- const retry2 = withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_create", canonicalCommand));
222074
- if (retry2) return withOperationStore((store) => store.snapshot(retry2));
222141
+ const retry2 = await withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_create", canonicalCommand));
222142
+ if (retry2) return await withOperationStore((store) => store.snapshot(retry2));
222075
222143
  const targetMachineId = args2.machineId ?? currentSession.machineId;
222076
222144
  await assertMachineOnlineForSingleCommand(manager, targetMachineId, ctx);
222077
222145
  const createOptions = buildMcpCreateOptions(args2, ctx);
@@ -222092,7 +222160,7 @@ ${result.stderr}`;
222092
222160
  const preallocatedUserTurnId = randomUUID();
222093
222161
  const materializationClaimToken = randomUUID();
222094
222162
  const timing = operationDeadline(args2.deadlineSeconds);
222095
- const accepted = withOperationStore((store) => store.accept({
222163
+ const accepted = await withOperationStore((store) => store.accept({
222096
222164
  workspaceId: workspace.id,
222097
222165
  ownerMachineId: ctx.machineId,
222098
222166
  requesterSessionId: ctx.sessionId,
@@ -222115,7 +222183,7 @@ ${result.stderr}`;
222115
222183
  materializationClaimToken
222116
222184
  }));
222117
222185
  if (accepted.operation.state === "finished") {
222118
- return withOperationStore((store) => store.snapshot(accepted.operation));
222186
+ return await withOperationStore((store) => store.snapshot(accepted.operation));
222119
222187
  }
222120
222188
  const pendingItem = accepted.operation.items[0];
222121
222189
  if (!pendingItem || pendingItem.status !== "active") {
@@ -222129,7 +222197,7 @@ ${result.stderr}`;
222129
222197
  if (result.sessionId !== pendingItem.target.sessionId || result.userTurnId !== pendingItem.target.userTurnId) {
222130
222198
  throw new Error("Create result did not preserve preallocated target ids.");
222131
222199
  }
222132
- withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, 0, materializationClaimToken));
222200
+ await withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, 0, materializationClaimToken));
222133
222201
  }
222134
222202
  return snapshotOperation(ctx.sessionId, args2.operationId);
222135
222203
  });
@@ -222154,8 +222222,8 @@ ${result.stderr}`;
222154
222222
  deadlineSeconds: args2.deadlineSeconds
222155
222223
  } : {}
222156
222224
  };
222157
- const retry2 = withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_chat", canonicalCommand));
222158
- if (retry2) return withOperationStore((store) => store.snapshot(retry2));
222225
+ const retry2 = await withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_chat", canonicalCommand));
222226
+ if (retry2) return await withOperationStore((store) => store.snapshot(retry2));
222159
222227
  const targetSession = await readCurrentSessionMeta(manager, args2.sessionId);
222160
222228
  if (!targetSession) {
222161
222229
  throw new LodyOperationStoreError("SESSION_NOT_FOUND", `Target Session not found: ${args2.sessionId}`, false);
@@ -222177,7 +222245,7 @@ ${result.stderr}`;
222177
222245
  const preallocatedUserTurnId = randomUUID();
222178
222246
  const materializationClaimToken = randomUUID();
222179
222247
  const timing = operationDeadline(args2.deadlineSeconds);
222180
- const accepted = withOperationStore((store) => store.accept({
222248
+ const accepted = await withOperationStore((store) => store.accept({
222181
222249
  workspaceId: workspace.id,
222182
222250
  ownerMachineId: ctx.machineId,
222183
222251
  requesterSessionId: ctx.sessionId,
@@ -222200,7 +222268,7 @@ ${result.stderr}`;
222200
222268
  materializationClaimToken
222201
222269
  }));
222202
222270
  if (accepted.operation.state === "finished") {
222203
- return withOperationStore((store) => store.snapshot(accepted.operation));
222271
+ return await withOperationStore((store) => store.snapshot(accepted.operation));
222204
222272
  }
222205
222273
  const pendingItem = accepted.operation.items[0];
222206
222274
  if (!pendingItem || pendingItem.status !== "active") {
@@ -222214,7 +222282,7 @@ ${result.stderr}`;
222214
222282
  if (result.userTurnId !== pendingItem.target.userTurnId) {
222215
222283
  throw new Error("Chat result did not preserve the preallocated target turn id.");
222216
222284
  }
222217
- withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, 0, materializationClaimToken));
222285
+ await withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, 0, materializationClaimToken));
222218
222286
  }
222219
222287
  return snapshotOperation(ctx.sessionId, args2.operationId);
222220
222288
  });
@@ -222257,11 +222325,11 @@ ${result.stderr}`;
222257
222325
  } : {},
222258
222326
  error: makeLodyError(code2, message, retryable)
222259
222327
  });
222260
- const finishOperationWhenEveryItemIsTerminal = (requesterSessionId, operationId, items2) => {
222328
+ const finishOperationWhenEveryItemIsTerminal = async (requesterSessionId, operationId, items2) => {
222261
222329
  if (items2.some((item) => item.status === "active")) {
222262
222330
  return;
222263
222331
  }
222264
- withOperationStore((store) => store.finish(requesterSessionId, operationId, {
222332
+ await withOperationStore((store) => store.finish(requesterSessionId, operationId, {
222265
222333
  type: "result",
222266
222334
  value: {
222267
222335
  items: items2
@@ -222289,8 +222357,8 @@ ${result.stderr}`;
222289
222357
  deadlineSeconds: args2.deadlineSeconds
222290
222358
  } : {}
222291
222359
  };
222292
- const retry2 = withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_create_many", canonicalCommand));
222293
- if (retry2) return withOperationStore((store) => store.snapshot(retry2));
222360
+ const retry2 = await withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_create_many", canonicalCommand));
222361
+ if (retry2) return await withOperationStore((store) => store.snapshot(retry2));
222294
222362
  const invoking = await resolveInvokingTurnContext(manager, requester);
222295
222363
  const initialItems = await mapWithConcurrency(expanded, 5, async (item) => {
222296
222364
  const label2 = item.label;
@@ -222337,7 +222405,7 @@ ${result.stderr}`;
222337
222405
  });
222338
222406
  const materializationClaimToken = randomUUID();
222339
222407
  const timing = operationDeadline(args2.deadlineSeconds);
222340
- const accepted = withOperationStore((store) => store.accept({
222408
+ const accepted = await withOperationStore((store) => store.accept({
222341
222409
  workspaceId: workspace.id,
222342
222410
  ownerMachineId: ctx.machineId,
222343
222411
  requesterSessionId: ctx.sessionId,
@@ -222358,7 +222426,7 @@ ${result.stderr}`;
222358
222426
  materializationClaimToken
222359
222427
  }));
222360
222428
  if (accepted.operation.state === "finished") {
222361
- return withOperationStore((store) => store.snapshot(accepted.operation));
222429
+ return await withOperationStore((store) => store.snapshot(accepted.operation));
222362
222430
  }
222363
222431
  const nextItems = await mapWithConcurrency(accepted.operation.items, 5, async (storedItem, index) => {
222364
222432
  if (storedItem.status !== "active" || storedItem.inputDurable || !accepted.claimedItemIndexes.includes(index)) {
@@ -222392,13 +222460,13 @@ ${result.stderr}`;
222392
222460
  options.userTurnId = storedItem.target.userTurnId;
222393
222461
  options.chainDepth = invoking.chainDepth + 1;
222394
222462
  await createSessionResult(auth, workspace, manager, expandedItem.prompt, options, resolveTurnDispatchConfig({}));
222395
- withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, index, materializationClaimToken));
222463
+ await withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, index, materializationClaimToken));
222396
222464
  return markOperationItemInputDurable(storedItem);
222397
222465
  } catch {
222398
222466
  return storedItem;
222399
222467
  }
222400
222468
  });
222401
- finishOperationWhenEveryItemIsTerminal(ctx.sessionId, args2.operationId, nextItems);
222469
+ await finishOperationWhenEveryItemIsTerminal(ctx.sessionId, args2.operationId, nextItems);
222402
222470
  return snapshotOperation(ctx.sessionId, args2.operationId);
222403
222471
  });
222404
222472
  };
@@ -222423,8 +222491,8 @@ ${result.stderr}`;
222423
222491
  deadlineSeconds: args2.deadlineSeconds
222424
222492
  } : {}
222425
222493
  };
222426
- const retry2 = withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_chat_many", canonicalCommand));
222427
- if (retry2) return withOperationStore((store) => store.snapshot(retry2));
222494
+ const retry2 = await withOperationStore((store) => store.findMatchingRetry(ctx.sessionId, args2.operationId, "session_chat_many", canonicalCommand));
222495
+ if (retry2) return await withOperationStore((store) => store.snapshot(retry2));
222428
222496
  const invoking = await resolveInvokingTurnContext(manager, requester);
222429
222497
  const initialItems = await mapWithConcurrency(expanded, 5, async (item) => {
222430
222498
  if (!item.sessionId || !item.prompt) {
@@ -222455,7 +222523,7 @@ ${result.stderr}`;
222455
222523
  });
222456
222524
  const materializationClaimToken = randomUUID();
222457
222525
  const timing = operationDeadline(args2.deadlineSeconds);
222458
- const accepted = withOperationStore((store) => store.accept({
222526
+ const accepted = await withOperationStore((store) => store.accept({
222459
222527
  workspaceId: workspace.id,
222460
222528
  ownerMachineId: ctx.machineId,
222461
222529
  requesterSessionId: ctx.sessionId,
@@ -222476,7 +222544,7 @@ ${result.stderr}`;
222476
222544
  materializationClaimToken
222477
222545
  }));
222478
222546
  if (accepted.operation.state === "finished") {
222479
- return withOperationStore((store) => store.snapshot(accepted.operation));
222547
+ return await withOperationStore((store) => store.snapshot(accepted.operation));
222480
222548
  }
222481
222549
  const nextItems = await mapWithConcurrency(accepted.operation.items, 5, async (storedItem, index) => {
222482
222550
  if (storedItem.status !== "active" || storedItem.inputDurable || !accepted.claimedItemIndexes.includes(index)) {
@@ -222491,13 +222559,13 @@ ${result.stderr}`;
222491
222559
  userTurnId: storedItem.target.userTurnId,
222492
222560
  chainDepth: invoking.chainDepth + 1
222493
222561
  });
222494
- withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, index, materializationClaimToken));
222562
+ await withOperationStore((store) => store.markItemInputDurable(ctx.sessionId, args2.operationId, index, materializationClaimToken));
222495
222563
  return markOperationItemInputDurable(storedItem);
222496
222564
  } catch {
222497
222565
  return storedItem;
222498
222566
  }
222499
222567
  });
222500
- finishOperationWhenEveryItemIsTerminal(ctx.sessionId, args2.operationId, nextItems);
222568
+ await finishOperationWhenEveryItemIsTerminal(ctx.sessionId, args2.operationId, nextItems);
222501
222569
  return snapshotOperation(ctx.sessionId, args2.operationId);
222502
222570
  });
222503
222571
  };
@@ -222779,7 +222847,7 @@ ${lines2.join("\n")}` : ""}${suffix}`);
222779
222847
  }, async (args2) => {
222780
222848
  try {
222781
222849
  const ctx = getSessionContext();
222782
- return jsonTextResult(snapshotOperation(ctx.sessionId, args2.operationId));
222850
+ return jsonTextResult(await snapshotOperation(ctx.sessionId, args2.operationId));
222783
222851
  } catch (error2) {
222784
222852
  return mcpErrorResult(error2);
222785
222853
  }
@@ -222792,13 +222860,13 @@ ${lines2.join("\n")}` : ""}${suffix}`);
222792
222860
  try {
222793
222861
  const ctx = getSessionContext();
222794
222862
  const requesterSessionId = ctx.sessionId;
222795
- const before = withOperationStore((store) => store.get(requesterSessionId, args2.operationId));
222796
- const cancellation = withOperationStore((store) => store.cancel(requesterSessionId, args2.operationId));
222863
+ const before = await withOperationStore((store) => store.get(requesterSessionId, args2.operationId));
222864
+ const cancellation = await withOperationStore((store) => store.cancel(requesterSessionId, args2.operationId));
222797
222865
  if (cancellation.didCancel && before.state === "active") {
222798
222866
  const startedTargets = before.items.filter((item) => item.status === "active" && item.inputDurable);
222799
222867
  await Promise.allSettled(startedTargets.map((item) => item.status === "active" ? runLodyCliJson(buildOperationTargetCancelArgs(getMcpWorkspaceId(ctx), item)) : Promise.resolve()));
222800
222868
  }
222801
- return jsonTextResult(withOperationStore((store) => store.snapshot(cancellation.operation)));
222869
+ return jsonTextResult(await withOperationStore((store) => store.snapshot(cancellation.operation)));
222802
222870
  } catch (error2) {
222803
222871
  return mcpErrorResult(error2);
222804
222872
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lody",
3
- "version": "0.71.1",
3
+ "version": "0.71.3",
4
4
  "description": "Lody Agent CLI tool for managing remote command execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -75,15 +75,15 @@
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",
79
- "@lody/code-review-helper": "0.0.0",
80
- "@lody/loro-streams-rpc": "0.0.1",
81
- "@lody/shared": "0.0.1",
82
78
  "@lody/convex": "0.0.1",
79
+ "@lody/loro-streams-rpc": "0.0.1",
83
80
  "acp-extension-claude": "0.59.0",
84
- "acp-extension-codex": "1.2.1",
81
+ "@lody/code-review-helper": "0.0.0",
82
+ "@lody/cli-supervisor": "0.0.1",
83
+ "@lody/shared": "0.0.1",
85
84
  "acp-extension-core": "0.0.5",
86
- "lody-code-review-viewer": "0.71.1"
85
+ "lody-code-review-viewer": "0.71.3",
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",