lody 0.46.2-next.1 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +244 -243
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -22780,7 +22780,7 @@ Event: ${getEventDescription(event)}`);
22780
22780
  const mergedOptions = {
22781
22781
  ...options,
22782
22782
  dsn: options.dsn ?? "https://080f9de535ff335a1a0440d0e385f796@o4510491299086336.ingest.us.sentry.io/4510559045681152",
22783
- environment: options.environment ?? "staging",
22783
+ environment: options.environment ?? "production",
22784
22784
  sendClientReports: options.sendClientReports ?? true,
22785
22785
  transport: options.transport ?? makeNodeTransport,
22786
22786
  stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),
@@ -36820,7 +36820,7 @@ Mongoose Error Code: ${error2.code}` : ""}`
36820
36820
  return client;
36821
36821
  }
36822
36822
  const name = "lody";
36823
- const version$4 = "0.46.2-next.1";
36823
+ const version$4 = "0.47.0";
36824
36824
  const description = "Lody Agent CLI tool for managing remote command execution";
36825
36825
  const type = "module";
36826
36826
  const main$3 = "dist/index.js";
@@ -37068,15 +37068,15 @@ Mongoose Error Code: ${error2.code}` : ""}`
37068
37068
  return "dev";
37069
37069
  }
37070
37070
  };
37071
- const getRuntimeEnv = () => normalizeRuntimeEnv("staging");
37071
+ const getRuntimeEnv = () => normalizeRuntimeEnv("production");
37072
37072
  const isDevEnv = () => getRuntimeEnv() === "dev";
37073
37073
  const runtimeEnv = getRuntimeEnv();
37074
- const environment$1 = "staging";
37074
+ const environment$1 = "production";
37075
37075
  const dsn = "https://080f9de535ff335a1a0440d0e385f796@o4510491299086336.ingest.us.sentry.io/4510559045681152";
37076
37076
  const postHogHost = process.env.LODY_POSTHOG_HOST ?? "https://us.i.posthog.com";
37077
37077
  const postHogKey = process.env.LODY_POSTHOG_KEY ?? "phc_LFS5i5WIwg4irAhrG5oJR04iYPhReVZ3DdFZOKqCkjG";
37078
- const tracesSampleRate = Number(process.env.SENTRY_TRACES_SAMPLE_RATE) || 1;
37079
- const profilesSampleRate = Number(process.env.SENTRY_PROFILES_SAMPLE_RATE) || 0.25;
37078
+ const tracesSampleRate = Number(process.env.SENTRY_TRACES_SAMPLE_RATE) || 0.2;
37079
+ const profilesSampleRate = Number(process.env.SENTRY_PROFILES_SAMPLE_RATE) || 0.1;
37080
37080
  const sentryEnabled = runtimeEnv !== "dev" && true;
37081
37081
  const postHogEnabled = runtimeEnv !== "dev" && process.env.LODY_POSTHOG_DISABLED !== "1";
37082
37082
  const release = `${name}@${version$4}`;
@@ -64608,16 +64608,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
64608
64608
  }
64609
64609
  return _v4(options, buf, offset2);
64610
64610
  }
64611
- let LODY_AUTH_URL = "https://impressive-guineapig-165.convex.cloud";
64611
+ let LODY_AUTH_URL = "https://nautical-curlew-181.convex.cloud";
64612
64612
  let LODY_AUTH_SITE_URL = "";
64613
- let LODY_SERVER_URL = "https://lody-server.lz-9c5.workers.dev";
64614
- let SITE_URL = "https://main.lody.pages.dev";
64613
+ let LODY_SERVER_URL = "https://api.lody.ai";
64614
+ let SITE_URL = "https://lody.ai";
64615
64615
  let SITE_APP_BASE_PATH = "";
64616
64616
  const loadEnv = () => {
64617
- LODY_AUTH_URL = "https://impressive-guineapig-165.convex.cloud";
64617
+ LODY_AUTH_URL = "https://nautical-curlew-181.convex.cloud";
64618
64618
  LODY_AUTH_SITE_URL = "";
64619
- LODY_SERVER_URL = "https://lody-server.lz-9c5.workers.dev";
64620
- SITE_URL = "https://main.lody.pages.dev";
64619
+ LODY_SERVER_URL = "https://api.lody.ai";
64620
+ SITE_URL = "https://lody.ai";
64621
64621
  SITE_APP_BASE_PATH = process.env["SITE_APP_BASE_PATH"] ?? "";
64622
64622
  };
64623
64623
  const MACHINE_ID_FILE_NAME = "machine-id";
@@ -82846,7 +82846,6 @@ ${tailedOutput}` : null;
82846
82846
  const LODY_PRESENCE_CHANNEL = "presence";
82847
82847
  const LODY_PRESENCE_TTL_MS = 6e4;
82848
82848
  const LODY_PRESENCE_HEARTBEAT_MS = 2e4;
82849
- const LODY_PRESENCE_STREAM_HEARTBEAT_MS = 15e3;
82850
82849
  const ActiveSessionStatusSchema = discriminatedUnion("type", [
82851
82850
  object({
82852
82851
  type: literal("running")
@@ -91165,7 +91164,8 @@ ${val.stack}`;
91165
91164
  const requestUrl = this.subresourceEndpoint("/bootstrap");
91166
91165
  const response = await this.http.fetchAuthorized(requestUrl, {
91167
91166
  method: "GET",
91168
- headers: this.mergeHeaders(input2.headers)
91167
+ headers: this.mergeHeaders(input2.headers),
91168
+ signal: input2.signal
91169
91169
  }, this.timeoutConfig.connectTimeoutMs, "connect");
91170
91170
  if (!response.ok) return {
91171
91171
  ok: false,
@@ -92711,8 +92711,10 @@ stream:${scope2.streamId}`;
92711
92711
  }
92712
92712
  };
92713
92713
  }
92714
- async bootstrap() {
92715
- const result = await this.client.bootstrap();
92714
+ async bootstrap(signal) {
92715
+ const result = await this.client.bootstrap({
92716
+ signal
92717
+ });
92716
92718
  if (!result.ok) throw this.toTransportFailure(result.result);
92717
92719
  return {
92718
92720
  nextOffset: result.result.nextOffset,
@@ -93097,7 +93099,6 @@ stream:${scope2.streamId}`;
93097
93099
  adaptor;
93098
93100
  client;
93099
93101
  reconnectConfig;
93100
- heartbeatMs;
93101
93102
  debug;
93102
93103
  joinState;
93103
93104
  constructor(options) {
@@ -93107,7 +93108,6 @@ stream:${scope2.streamId}`;
93107
93108
  ...DEFAULT_RECONNECT_CONFIG,
93108
93109
  ...options.reconnectConfig
93109
93110
  };
93110
- this.heartbeatMs = options.heartbeatMs ?? 15e3;
93111
93111
  this.debug = options.debug ?? false;
93112
93112
  this.client = new StreamsTransportClient({
93113
93113
  streamUrl: options.streamUrl,
@@ -93188,18 +93188,15 @@ stream:${scope2.streamId}`;
93188
93188
  };
93189
93189
  }
93190
93190
  async startJoin(state2) {
93191
+ state2.unsubscribeLocal = this.adaptor.subscribeLocalUpdates((update2) => {
93192
+ this.enqueueLocal(state2, normalizeEphemeralUpdate(update2));
93193
+ });
93191
93194
  state2.requestController = new AbortController();
93192
93195
  const firstOpen = this.waitForFirstOpen(state2);
93193
93196
  this.runReadLoop(state2);
93194
93197
  await firstOpen;
93195
93198
  if (state2.closed) return;
93196
- state2.unsubscribeLocal = this.adaptor.subscribeLocalUpdates((update2) => {
93197
- this.enqueueLocal(state2, normalizeEphemeralUpdate(update2));
93198
- });
93199
93199
  this.setWriteStatus(state2, "ok");
93200
- const fullState = normalizeEphemeralUpdate(await this.adaptor.encodeAll());
93201
- if (fullState != null) state2.pendingLocal.pushBack(fullState);
93202
- this.startHeartbeat(state2);
93203
93200
  await this.flushPendingLocal(state2);
93204
93201
  if (state2.status === "error" || state2.status === "disconnected") throw new Error(`ephemeral stream failed to join: ${state2.status}`);
93205
93202
  }
@@ -93257,7 +93254,6 @@ stream:${scope2.streamId}`;
93257
93254
  if (state2.closed) return;
93258
93255
  state2.closed = true;
93259
93256
  state2.unsubscribeLocal?.();
93260
- if (state2.heartbeatTimer != null) clearInterval(state2.heartbeatTimer);
93261
93257
  state2.requestController?.abort();
93262
93258
  state2.retrySleepController.abort();
93263
93259
  this.rejectFirstOpen(state2, closedSubscriptionError());
@@ -93285,7 +93281,6 @@ stream:${scope2.streamId}`;
93285
93281
  ]);
93286
93282
  this.setReadStatus(state2, "ok");
93287
93283
  this.resolveFirstOpen(state2);
93288
- if (state2.unsubscribeLocal != null) this.enqueueFullState(state2);
93289
93284
  }, async (update2) => {
93290
93285
  if (update2.byteLength > 0) await this.adaptor.applyRemoteUpdates([
93291
93286
  update2
@@ -93333,27 +93328,10 @@ stream:${scope2.streamId}`;
93333
93328
  }
93334
93329
  }
93335
93330
  }
93336
- startHeartbeat(state2) {
93337
- if (this.heartbeatMs === false || this.heartbeatMs <= 0) return;
93338
- state2.heartbeatTimer = setInterval(() => {
93339
- this.enqueueFullState(state2);
93340
- }, this.heartbeatMs);
93341
- }
93342
- async enqueueFullState(state2) {
93343
- try {
93344
- const update2 = await this.adaptor.encodeAll();
93345
- this.enqueueLocal(state2, normalizeEphemeralUpdate(update2));
93346
- } catch (error2) {
93347
- this.logError("ephemeral encodeAll failed", error2, {
93348
- streamUrl: this.streamUrl
93349
- });
93350
- this.setWriteStatus(state2, "error");
93351
- }
93352
- }
93353
93331
  enqueueLocal(state2, update2) {
93354
93332
  if (state2.closed || update2 == null || update2.byteLength === 0) return;
93355
93333
  state2.pendingLocal.pushBack(update2);
93356
- this.flushPendingLocal(state2);
93334
+ if (state2.writeStatus !== "connecting") this.flushPendingLocal(state2);
93357
93335
  }
93358
93336
  async flushPendingLocal(state2) {
93359
93337
  if (state2.flushingLocal || state2.closed) return;
@@ -94115,7 +94093,7 @@ stream:${scope2.streamId}`;
94115
94093
  async startJoin(state2) {
94116
94094
  try {
94117
94095
  state2.requestController ??= new AbortController();
94118
- const joined = await this.performInitialJoinSync(state2.liveMode);
94096
+ const joined = await this.performInitialJoinSync(state2.liveMode, state2.requestController?.signal);
94119
94097
  state2.cursor = joined.cursor;
94120
94098
  state2.streamCursor = joined.streamCursor;
94121
94099
  state2.liveMode = joined.liveMode;
@@ -94158,8 +94136,8 @@ stream:${scope2.streamId}`;
94158
94136
  }
94159
94137
  return cursor;
94160
94138
  }
94161
- async performInitialJoinSync(preferredLiveMode) {
94162
- const initial = await this.finalizeDirectLocalAppendIfNeeded(await this.resolveInitialRemoteState());
94139
+ async performInitialJoinSync(preferredLiveMode, signal) {
94140
+ const initial = await this.finalizeDirectLocalAppendIfNeeded(await this.resolveInitialRemoteState(signal));
94163
94141
  let cursor = initial.cursor;
94164
94142
  let localExportRefVersion = cursor.serverLowerBoundVersion;
94165
94143
  let streamCursor = initial.streamCursor;
@@ -94168,7 +94146,7 @@ stream:${scope2.streamId}`;
94168
94146
  localExportRefVersion = exportedLocal.nextRefVersion;
94169
94147
  if (exportedLocal.batch != null) {
94170
94148
  cursor = await this.appendLocalBatch(cursor, exportedLocal.batch);
94171
- const caughtUp = await this.catchup(cursor);
94149
+ const caughtUp = await this.catchup(cursor, void 0, void 0, void 0, signal);
94172
94150
  cursor = caughtUp.cursor;
94173
94151
  streamCursor = caughtUp.streamCursor;
94174
94152
  }
@@ -94187,31 +94165,37 @@ stream:${scope2.streamId}`;
94187
94165
  serverLowerBoundVersion: this.adapter.emptyVersion()
94188
94166
  });
94189
94167
  }
94190
- async resolveInitialRemoteState() {
94168
+ async resolveInitialRemoteState(signal) {
94191
94169
  const loaded = await this.remoteCursorStore.load(this.streamUrl);
94192
94170
  const cursor = loaded ?? this.createInitialCursor();
94193
94171
  try {
94194
- if (loaded == null) return await this.bootstrapState(cursor);
94172
+ if (loaded == null) return await this.bootstrapState(cursor, {
94173
+ signal
94174
+ });
94195
94175
  const caughtUp = await this.catchupOrGone(cursor, {
94196
- goneStrategy: "return-gone"
94176
+ goneStrategy: "return-gone",
94177
+ signal
94197
94178
  });
94198
- if ("kind" in caughtUp) return await this.recoverFromGoneWithLocalBootstrap(cursor);
94179
+ if ("kind" in caughtUp) return await this.recoverFromGoneWithLocalBootstrap(cursor, signal);
94199
94180
  return caughtUp;
94200
94181
  } catch (error2) {
94201
94182
  if (!isStreamNotFoundError(error2) || !this.createStreamIfMissing) throw error2;
94202
94183
  await this.ensureStreamExists();
94203
- return await this.bootstrapState(this.createInitialCursor());
94184
+ return await this.bootstrapState(this.createInitialCursor(), {
94185
+ signal
94186
+ });
94204
94187
  }
94205
94188
  }
94206
94189
  async bootstrapState(cursor, options = {}) {
94207
94190
  const cursorManager = options.cursorManager ?? this.cursorManager;
94208
- const result = await this.client.bootstrap();
94191
+ const result = await this.client.bootstrap(options.signal);
94209
94192
  let saved = await cursorManager.applyBootstrapPayload(cursor, result.snapshot, result.updates, result.nextOffset);
94210
94193
  let bootstrapCursor = result.streamCursor;
94211
94194
  if (!result.upToDate) {
94212
94195
  const caughtUp = await this.catchupOrGone(saved, {
94213
94196
  streamCursor: bootstrapCursor,
94214
- cursorManager
94197
+ cursorManager,
94198
+ signal: options.signal
94215
94199
  });
94216
94200
  if ("kind" in caughtUp) throw new Error("bootstrap catch-up unexpectedly returned gone");
94217
94201
  saved = caughtUp.cursor;
@@ -94222,11 +94206,12 @@ stream:${scope2.streamId}`;
94222
94206
  streamCursor: bootstrapCursor
94223
94207
  };
94224
94208
  }
94225
- async catchup(cursor, live, streamCursor, onRemoteVersionApplied) {
94209
+ async catchup(cursor, live, streamCursor, onRemoteVersionApplied, signal) {
94226
94210
  const result = await this.catchupOrGone(cursor, {
94227
94211
  live,
94228
94212
  streamCursor,
94229
- onRemoteVersionApplied
94213
+ onRemoteVersionApplied,
94214
+ signal
94230
94215
  });
94231
94216
  if ("kind" in result) throw new Error("catchup unexpectedly returned gone");
94232
94217
  return result;
@@ -94238,7 +94223,7 @@ stream:${scope2.streamId}`;
94238
94223
  const cursorManager = options.cursorManager ?? this.cursorManager;
94239
94224
  while (true) {
94240
94225
  const requestOffset = current2.nextOffset;
94241
- const response = await this.client.readFromOffset(requestOffset, currentStreamCursor, options.live);
94226
+ const response = await this.client.readFromOffset(requestOffset, currentStreamCursor, options.live, options.signal);
94242
94227
  if (response.kind === "gone") {
94243
94228
  if (goneStrategy === "return-gone") return StreamsCrdt2.GONE_CATCHUP;
94244
94229
  return await this.bootstrapState({
@@ -94246,7 +94231,8 @@ stream:${scope2.streamId}`;
94246
94231
  nextOffset: "-1",
94247
94232
  serverLowerBoundVersion: this.adapter.emptyVersion()
94248
94233
  }, {
94249
- cursorManager
94234
+ cursorManager,
94235
+ signal: options.signal
94250
94236
  });
94251
94237
  }
94252
94238
  const result = response.value;
@@ -94413,9 +94399,10 @@ stream:${scope2.streamId}`;
94413
94399
  if (isolated == null) return null;
94414
94400
  return this.createCursorManager(isolated, async (cursor) => cursor);
94415
94401
  }
94416
- async inferRemoteAndAppendLocal(bootstrapCursor, cursorManager) {
94402
+ async inferRemoteAndAppendLocal(bootstrapCursor, cursorManager, signal) {
94417
94403
  const inferredRemote = await this.bootstrapState(bootstrapCursor, {
94418
- cursorManager
94404
+ cursorManager,
94405
+ signal
94419
94406
  });
94420
94407
  let pendingAppend;
94421
94408
  if (this.frozenLocalAppend != null) {
@@ -94425,23 +94412,26 @@ stream:${scope2.streamId}`;
94425
94412
  const localBatch = this.exportUpdates(inferredRemote.cursor.serverLowerBoundVersion);
94426
94413
  if (localBatch.batch != null) pendingAppend = await this.appendLocalBatchRemote(inferredRemote.cursor, localBatch.batch);
94427
94414
  }
94428
- const result = await this.bootstrapState(bootstrapCursor);
94415
+ const result = await this.bootstrapState(bootstrapCursor, {
94416
+ signal
94417
+ });
94429
94418
  if (pendingAppend != null) this.commitProducerAck(pendingAppend.producerAck);
94430
94419
  return result;
94431
94420
  }
94432
- async recoverFromGoneWithLocalBootstrap(cursor) {
94421
+ async recoverFromGoneWithLocalBootstrap(cursor, signal) {
94433
94422
  const isolatedCursorManager = await this.createIsolatedCursorManager();
94434
94423
  const bootstrapCursor = {
94435
94424
  ...cursor,
94436
94425
  nextOffset: "-1",
94437
94426
  serverLowerBoundVersion: this.adapter.emptyVersion()
94438
94427
  };
94439
- if (isolatedCursorManager != null) return await this.inferRemoteAndAppendLocal(bootstrapCursor, isolatedCursorManager);
94428
+ if (isolatedCursorManager != null) return await this.inferRemoteAndAppendLocal(bootstrapCursor, isolatedCursorManager, signal);
94440
94429
  const preservedLocalSnapshot = this.adapter.exportSnapshot();
94441
94430
  let restoredLocalSnapshot = false;
94442
94431
  try {
94443
94432
  const inferredRemote = await this.bootstrapState(bootstrapCursor, {
94444
- cursorManager: this.volatileCursorManager
94433
+ cursorManager: this.volatileCursorManager,
94434
+ signal
94445
94435
  });
94446
94436
  await this.adapter.applySnapshot(preservedLocalSnapshot);
94447
94437
  restoredLocalSnapshot = true;
@@ -94453,7 +94443,9 @@ stream:${scope2.streamId}`;
94453
94443
  const localBatch = this.exportUpdates(inferredRemote.cursor.serverLowerBoundVersion);
94454
94444
  if (localBatch.batch != null) pendingAppend = await this.appendLocalBatchRemote(inferredRemote.cursor, localBatch.batch);
94455
94445
  }
94456
- const result = await this.bootstrapState(bootstrapCursor);
94446
+ const result = await this.bootstrapState(bootstrapCursor, {
94447
+ signal
94448
+ });
94457
94449
  if (pendingAppend != null) this.commitProducerAck(pendingAppend.producerAck);
94458
94450
  return result;
94459
94451
  } catch (error2) {
@@ -94490,6 +94482,8 @@ stream:${scope2.streamId}`;
94490
94482
  ...state2.cursor,
94491
94483
  nextOffset: "-1",
94492
94484
  serverLowerBoundVersion: this.adapter.emptyVersion()
94485
+ }, {
94486
+ signal: capturedController?.signal
94493
94487
  });
94494
94488
  state2.cursor = bootstrapped2.cursor;
94495
94489
  state2.streamCursor = bootstrapped2.streamCursor;
@@ -94962,12 +94956,25 @@ stream:${scope2.streamId}`;
94962
94956
  };
94963
94957
  }
94964
94958
  function EphemeralStoreAdaptor(store) {
94959
+ let applyingRemoteDepth = 0;
94965
94960
  return {
94966
- encodeAll: () => store.encodeAll(),
94967
94961
  applyRemoteUpdates: (updates) => {
94968
- for (const update2 of updates) store.apply(update2);
94962
+ applyingRemoteDepth += 1;
94963
+ try {
94964
+ for (const update2 of updates) store.apply(update2);
94965
+ } finally {
94966
+ applyingRemoteDepth -= 1;
94967
+ }
94969
94968
  },
94970
- subscribeLocalUpdates: (listener) => store.subscribeLocalUpdates(listener)
94969
+ subscribeLocalUpdates: (listener) => {
94970
+ const unsubscribeLocalUpdates = store.subscribeLocalUpdates((update2) => {
94971
+ if (applyingRemoteDepth > 0) return;
94972
+ listener(update2);
94973
+ });
94974
+ return () => {
94975
+ unsubscribeLocalUpdates();
94976
+ };
94977
+ }
94971
94978
  };
94972
94979
  }
94973
94980
  function isAfter(entry2, current2) {
@@ -95862,8 +95869,7 @@ stream:${scope2.streamId}`;
95862
95869
  this.transport = new EphemeralStreamCrdt({
95863
95870
  streamUrl: toLodyPresenceStreamUrl(durableStreamUrl),
95864
95871
  auth: options.auth,
95865
- adaptor: EphemeralStoreAdaptor(this.store),
95866
- heartbeatMs: LODY_PRESENCE_STREAM_HEARTBEAT_MS
95872
+ adaptor: EphemeralStoreAdaptor(this.store)
95867
95873
  });
95868
95874
  this.machineTimer = setInterval(() => {
95869
95875
  this.writeMachineHeartbeat();
@@ -114202,7 +114208,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
114202
114208
  const agent = proxyUrl ? new distExports.HttpsProxyAgent(proxyUrl) : void 0;
114203
114209
  const logger2 = getLogger("loro:websocket");
114204
114210
  logger2.debug(`Creating WebSocket url=${sanitizeUrlForLogging(urlString)} proxy=${proxyUrl ? redactProxyUrl(proxyUrl) : "none"} agentValid=${!!agent}`);
114205
- if (global.Bun && proxyUrl) {
114211
+ if (globalThis.Bun && proxyUrl) {
114206
114212
  logger2.debug("Bun does not support WebSocket proxy yet: https://github.com/oven-sh/bun/issues/14522");
114207
114213
  }
114208
114214
  super(urlString, protocols, {
@@ -115713,12 +115719,12 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
115713
115719
  }));
115714
115720
  return result.nextOffset;
115715
115721
  },
115716
- longPollJson: async (streamId, state2, options2) => {
115722
+ longPollJson: async (streamId, state2, readOptions) => {
115717
115723
  const result = unwrapStreamResult("long-poll stream", streamId, await getStreamClient(streamId).readOnce({
115718
115724
  offset: state2.nextOffset ?? "now",
115719
115725
  cursor: state2.cursor,
115720
115726
  live: "long-poll",
115721
- signal: options2?.signal
115727
+ signal: readOptions?.signal
115722
115728
  }));
115723
115729
  return {
115724
115730
  messages: result.payload === null ? [] : parseJsonBatchPayload(result.payload, streamId, "long-poll read"),
@@ -115727,13 +115733,13 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
115727
115733
  upToDate: result.upToDate
115728
115734
  };
115729
115735
  },
115730
- readJsonLive: async (streamId, state2, onBatch, options2) => {
115736
+ readJsonLive: async (streamId, state2, onBatch, liveOptions) => {
115731
115737
  const pendingMessages = [];
115732
115738
  const streamClient = getStreamClient(streamId);
115733
115739
  for await (const event of streamClient.live({
115734
115740
  offset: state2.nextOffset ?? "now",
115735
115741
  mode: "auto",
115736
- signal: options2?.signal
115742
+ signal: liveOptions?.signal
115737
115743
  })) {
115738
115744
  switch (event.type) {
115739
115745
  case "data":
@@ -117329,7 +117335,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
117329
117335
  throw err2;
117330
117336
  }
117331
117337
  logger2.debug(`[${sessionId}] ${operationName} failed after ${attempt + 1} attempt(s) with transport error: ${formatErrorMessage(err2)}`);
117332
- reportError(`agent-client:${operationName}`, err2 instanceof Error ? err2 : new Error(String(err2)));
117338
+ void reportError(`agent-client:${operationName}`, err2 instanceof Error ? err2 : new Error(String(err2)));
117333
117339
  return void 0;
117334
117340
  }
117335
117341
  }
@@ -118931,6 +118937,7 @@ ${this.stack.split("\n").slice(1).join("\n")}` : this.toString();
118931
118937
  case "idle":
118932
118938
  return "idle";
118933
118939
  }
118940
+ return "unknown";
118934
118941
  };
118935
118942
  class SessionHeartbeat {
118936
118943
  constructor(sessionId, workspaceDocument, logger2, options = {}) {
@@ -120749,11 +120756,7 @@ path=/${options.repoFullName}.git
120749
120756
  });
120750
120757
  }
120751
120758
  async function fetchAcpCapabilities(cliType, agentType, logger2, env2) {
120752
- const safeAgentType = agentType.replace(/[^a-zA-Z0-9._-]/g, "-");
120753
- const workdir = path__default.join(os__default.tmpdir(), "lody-acp-capabilities", `${cliType}-${safeAgentType}`);
120754
- fs__default.mkdirSync(workdir, {
120755
- recursive: true
120756
- });
120759
+ const workdir = process.cwd();
120757
120760
  let capturedCommands;
120758
120761
  let commandsResolve;
120759
120762
  const commandsPromise = new Promise((resolve2) => {
@@ -121905,91 +121908,94 @@ $mem | ConvertTo-Json -Compress
121905
121908
  const runtime = this.createTurnRuntime(sessionId, turnId, userTurnId, session);
121906
121909
  this.registerTurnRuntime(runtime);
121907
121910
  const self2 = this;
121908
- const program2 = scoped(acquireRelease(succeed(runtime), (runtime2, exit2) => gen(function* () {
121911
+ const program2 = scoped(acquireRelease(succeed(runtime), (turnRuntime, exit2) => gen(function* () {
121909
121912
  const wasInterrupted = isFailure(exit2) && isInterrupted(exit2.cause);
121910
- const wasCancelled = runtime2.cancelRequested || self2.isTurnCancelled(sessionId, runtime2.turnId) || wasInterrupted;
121913
+ const wasCancelled = turnRuntime.cancelRequested || self2.isTurnCancelled(sessionId, turnRuntime.turnId) || wasInterrupted;
121911
121914
  if (wasCancelled) {
121912
121915
  yield* self2.finalizeCancelledTurnEffect({
121913
121916
  sessionId,
121914
121917
  sessionDoc,
121915
- turnId: runtime2.turnId,
121918
+ turnId: turnRuntime.turnId,
121916
121919
  userTurnId,
121917
- session: runtime2.session,
121918
- pendingSession: runtime2.pendingSession,
121919
- terminateSession: runtime2.terminateSessionOnCancel,
121920
- reportTurnError: self2.shouldReportCancelledTurnError(runtime2)
121920
+ session: turnRuntime.session,
121921
+ pendingSession: turnRuntime.pendingSession,
121922
+ terminateSession: turnRuntime.terminateSessionOnCancel,
121923
+ reportTurnError: self2.shouldReportCancelledTurnError(turnRuntime)
121921
121924
  });
121922
121925
  }
121923
- self2.releaseTurnRuntime(sessionId, runtime2.turnId);
121924
- })).pipe(flatMap$1((runtime2) => gen(function* () {
121925
- yield* self2.tryPromise(() => self2.deps.createAssistantEntryForTurn(sessionId, sessionDoc, runtime2.turnId, runtime2.session?.agentClient?.currentModel, userTurnId));
121926
+ self2.releaseTurnRuntime(sessionId, turnRuntime.turnId);
121927
+ })).pipe(flatMap$1((turnRuntime) => gen(function* () {
121928
+ yield* self2.tryPromise(() => self2.deps.createAssistantEntryForTurn(sessionId, sessionDoc, turnRuntime.turnId, turnRuntime.session?.agentClient?.currentModel, userTurnId));
121926
121929
  yield* self2.acquireSessionHeartbeat(sessionId);
121927
121930
  if (userTurnId) {
121928
121931
  yield* self2.tryPromise(() => self2.setDispatchProcessing(sessionId, sessionDoc, userTurnId));
121929
121932
  }
121930
121933
  const bindSession = (nextSession) => {
121931
- runtime2.session = nextSession;
121932
- runtime2.pendingSession = void 0;
121934
+ runtime.session = nextSession;
121935
+ runtime.pendingSession = void 0;
121933
121936
  };
121934
121937
  const trackPendingSession = (pendingSession, pendingOptions) => gen(function* () {
121935
121938
  const pending2 = yield* try_({
121936
121939
  try: () => typeof pendingSession === "function" ? pendingSession() : pendingSession,
121937
121940
  catch: (error2) => error2
121938
121941
  });
121939
- runtime2.pendingSession = pending2;
121942
+ runtime.pendingSession = pending2;
121940
121943
  if (pendingOptions?.terminateOnCancel) {
121941
- runtime2.terminateSessionOnCancel = true;
121944
+ runtime.terminateSessionOnCancel = true;
121942
121945
  }
121943
121946
  return yield* self2.tryPromise(() => pending2);
121944
121947
  });
121945
121948
  const abortIfCancelled = (cancelOptions) => gen(function* () {
121946
121949
  if (cancelOptions?.terminateSession) {
121947
- runtime2.terminateSessionOnCancel = true;
121950
+ runtime.terminateSessionOnCancel = true;
121948
121951
  }
121949
121952
  const userTurnWasCancelled = yield* self2.tryPromise(() => self2.isUserTurnCancelled(sessionDoc, userTurnId));
121950
- if (!runtime2.cancelRequested && !self2.isTurnCancelled(sessionId, runtime2.turnId) && !userTurnWasCancelled) {
121951
- return;
121953
+ if (!runtime.cancelRequested && !self2.isTurnCancelled(sessionId, runtime.turnId) && !userTurnWasCancelled) {
121954
+ return void 0;
121952
121955
  }
121953
121956
  yield* self2.finalizeCancelledTurnEffect({
121954
121957
  sessionId,
121955
121958
  sessionDoc,
121956
- turnId: runtime2.turnId,
121959
+ turnId: runtime.turnId,
121957
121960
  userTurnId,
121958
- session: runtime2.session,
121959
- pendingSession: runtime2.pendingSession,
121960
- terminateSession: cancelOptions?.terminateSession ?? runtime2.terminateSessionOnCancel,
121961
- reportTurnError: self2.shouldReportCancelledTurnError(runtime2)
121961
+ session: runtime.session,
121962
+ pendingSession: runtime.pendingSession,
121963
+ terminateSession: cancelOptions?.terminateSession ?? runtime.terminateSessionOnCancel,
121964
+ reportTurnError: self2.shouldReportCancelledTurnError(runtime)
121962
121965
  });
121963
- return yield* fail(new SessionTurnCancelled({
121966
+ yield* fail(new SessionTurnCancelled({
121964
121967
  sessionId,
121965
- turnId: runtime2.turnId
121968
+ turnId: runtime.turnId
121966
121969
  }));
121970
+ return void 0;
121967
121971
  });
121968
- const patchAssistantEntry = () => self2.tryPromise(() => self2.deps.createAssistantEntryForTurn(sessionId, sessionDoc, runtime2.turnId, runtime2.session?.agentClient?.currentModel, userTurnId));
121972
+ const patchAssistantEntry = () => self2.tryPromise(() => self2.deps.createAssistantEntryForTurn(sessionId, sessionDoc, runtime.turnId, runtime.session?.agentClient?.currentModel, userTurnId));
121969
121973
  const prompt2 = (promptBlocks) => gen(function* () {
121970
- const activeSession = runtime2.session;
121974
+ const activeSession = runtime.session;
121971
121975
  const agentClient = activeSession?.agentClient;
121972
121976
  const acpSessionId = activeSession?.acpSessionId;
121973
121977
  if (!agentClient || !acpSessionId) {
121974
- return yield* fail(new Error("Agent session was not ready"));
121978
+ yield* fail(new Error("Agent session was not ready"));
121979
+ return void 0;
121975
121980
  }
121976
- runtime2.terminateSessionOnCancel = false;
121977
- runtime2.promptStarted = true;
121981
+ runtime.terminateSessionOnCancel = false;
121982
+ runtime.promptStarted = true;
121978
121983
  yield* acquireUseRelease(sync(() => {
121979
- runtime2.promptInFlight = true;
121984
+ runtime.promptInFlight = true;
121980
121985
  return activeSession;
121981
121986
  }), () => self2.tryPromise((signal) => agentClient.prompt(acpSessionId, promptBlocks, {
121982
121987
  signal
121983
121988
  })).pipe(catchAll((error2) => sync(() => {
121984
- runtime2.promptFailed = true;
121989
+ runtime.promptFailed = true;
121985
121990
  }).pipe(flatMap$1(() => fail(error2))))), () => sync(() => {
121986
- runtime2.promptInFlight = false;
121991
+ runtime.promptInFlight = false;
121987
121992
  }));
121988
- self2.deps.clearActiveTurnId(sessionId, runtime2.turnId);
121993
+ self2.deps.clearActiveTurnId(sessionId, runtime.turnId);
121994
+ return void 0;
121989
121995
  });
121990
121996
  yield* body({
121991
- turnId: runtime2.turnId,
121992
- runtime: runtime2,
121997
+ turnId: runtime.turnId,
121998
+ runtime,
121993
121999
  bindSession,
121994
122000
  trackPendingSession,
121995
122001
  abortIfCancelled,
@@ -122662,7 +122668,8 @@ $mem | ConvertTo-Json -Compress
122662
122668
  }, ({ turnId, runtime, bindSession, trackPendingSession, abortIfCancelled, patchAssistantEntry, prompt: prompt2 }) => gen(function* () {
122663
122669
  yield* abortIfCancelled();
122664
122670
  if (project?.kind === "local" && !workdir) {
122665
- return yield* fail(new Error(`Local project not found in workspace: ${project.localProjectId}`));
122671
+ yield* fail(new Error(`Local project not found in workspace: ${project.localProjectId}`));
122672
+ return void 0;
122666
122673
  }
122667
122674
  if (shouldPrepareWorktree) {
122668
122675
  yield* self2.tryPromise(() => sessionDoc.setStatus(SessionStatusFactory.initializing("git-clone")));
@@ -122747,6 +122754,7 @@ $mem | ConvertTo-Json -Compress
122747
122754
  yield* abortIfCancelled();
122748
122755
  self2.clearTurnCancellation(sessionId, turnId);
122749
122756
  yield* self2.tryPromise(() => self2.deps.processMessageQueue(sessionId));
122757
+ return void 0;
122750
122758
  }));
122751
122759
  }
122752
122760
  async cancelSession(message) {
@@ -123378,6 +123386,7 @@ $mem | ConvertTo-Json -Compress
123378
123386
  case "cli_token_invalid":
123379
123387
  return "CLI token is invalid or has been revoked.";
123380
123388
  }
123389
+ return "Machine access was denied.";
123381
123390
  }
123382
123391
  async markDispatchAccessDenied(sessionId, sessionDoc, userTurnId, reason) {
123383
123392
  const message = this.getAccessDeniedMessage(reason);
@@ -124315,7 +124324,7 @@ $mem | ConvertTo-Json -Compress
124315
124324
  getActiveTurnId: (sessionId) => this.store.getActiveTurnId(sessionId),
124316
124325
  clearActiveTurnId: (sessionId, turnId) => this.clearActiveTurnIdIfMatches(sessionId, turnId),
124317
124326
  buildAcpPromptBlocks: async (args2) => await this.buildAcpPromptBlocks(args2),
124318
- applyAcpModeAndModel: async (session, config22) => await this.applyAcpModeAndModel(session, config22),
124327
+ applyAcpModeAndModel: async (session, acpConfig) => await this.applyAcpModeAndModel(session, acpConfig),
124319
124328
  createAssistantEntryForTurn: async (sessionId, sessionDoc, turnId, modelInfo, userTurnId) => await this.createAssistantEntryForTurn(sessionId, sessionDoc, turnId, modelInfo, userTurnId),
124320
124329
  turnFinalization: {
124321
124330
  finalizeACPState: async (sessionId) => await this.finalizeACPState(sessionId),
@@ -124620,7 +124629,7 @@ $mem | ConvertTo-Json -Compress
124620
124629
  trackPendingContextWindowPersist(sessionId, promise) {
124621
124630
  const state2 = this.store.get(sessionId);
124622
124631
  state2.pendingContextWindowHandlers.add(promise);
124623
- promise.finally(() => {
124632
+ void promise.finally(() => {
124624
124633
  state2.pendingContextWindowHandlers.delete(promise);
124625
124634
  });
124626
124635
  }
@@ -125088,7 +125097,7 @@ $mem | ConvertTo-Json -Compress
125088
125097
  }
125089
125098
  }
125090
125099
  setupSessionEventHandlers() {
125091
- this.sessionManager.on("output", async (output) => {
125100
+ this.sessionManager.on("output", (output) => {
125092
125101
  this.logger.debug(`Output from session [${output.sessionId}]: <${output.data.length} bytes>`);
125093
125102
  });
125094
125103
  this.sessionManager.on("onACPUpdateMessage", (sessionId, update2) => {
@@ -125098,53 +125107,59 @@ $mem | ConvertTo-Json -Compress
125098
125107
  const promise = this.handleUsageUpdate(sessionId, acpSessionId, usage);
125099
125108
  const usageState = this.store.get(sessionId);
125100
125109
  usageState.pendingUsageHandlers.add(promise);
125101
- promise.finally(() => {
125110
+ void promise.finally(() => {
125102
125111
  usageState.pendingUsageHandlers.delete(promise);
125103
125112
  });
125104
125113
  });
125105
125114
  this.sessionManager.on("onContextWindowUsageUpdate", (sessionId, usage) => {
125106
125115
  this.enqueueContextWindowUsageUpdate(sessionId, usage);
125107
125116
  });
125108
- this.sessionManager.on("onRateLimitUpdate", async (machineId, cliType, limits) => {
125109
- await this.workspaceDocument.updateRateLimits(machineId, cliType, limits);
125117
+ this.sessionManager.on("onRateLimitUpdate", (machineId, cliType, limits) => {
125118
+ void this.workspaceDocument.updateRateLimits(machineId, cliType, limits);
125110
125119
  });
125111
- this.sessionManager.on("error", async (event) => {
125112
- this.logger.error(`[${event.sessionId}] Session error event received:`, event);
125113
- const sessionId = event.sessionId;
125114
- this.stopMachineSessionHeartbeat(sessionId);
125115
- await this.finalizeACPState(sessionId);
125116
- await this.flushSessionUsage(sessionId);
125117
- const sessionDoc = await this.workspaceDocument.getOrCreateSessionDoc(sessionId);
125118
- await sessionDoc.setStatus(SessionStatusFactory.idle());
125119
- this.logger.debug(`[${sessionId}] Status set to idle (via error event)`);
125120
+ this.sessionManager.on("error", (event) => {
125121
+ void (async () => {
125122
+ this.logger.error(`[${event.sessionId}] Session error event received:`, event);
125123
+ const sessionId = event.sessionId;
125124
+ this.stopMachineSessionHeartbeat(sessionId);
125125
+ await this.finalizeACPState(sessionId);
125126
+ await this.flushSessionUsage(sessionId);
125127
+ const sessionDoc = await this.workspaceDocument.getOrCreateSessionDoc(sessionId);
125128
+ await sessionDoc.setStatus(SessionStatusFactory.idle());
125129
+ this.logger.debug(`[${sessionId}] Status set to idle (via error event)`);
125130
+ })();
125120
125131
  });
125121
- this.sessionManager.on("exit", async (exit2) => {
125122
- const sessionId = exit2.sessionId;
125123
- this.logger.debug(`[${sessionId}] Session exit event received (exitCode=${exit2.exitCode})`);
125124
- if (!this.store.has(sessionId)) {
125125
- this.logger.debug(`[${sessionId}] Ignoring exit event for GC-cleaned session`);
125126
- return;
125127
- }
125128
- this.stopMachineSessionHeartbeat(sessionId);
125129
- await this.finalizeACPState(sessionId);
125130
- await this.flushSessionUsage(sessionId);
125131
- const session = await this.workspaceDocument.getOrCreateSessionDoc(sessionId);
125132
- await session.setStatus(SessionStatusFactory.idle());
125133
- this.logger.debug(`[${sessionId}] Status set to idle (via exit event)`);
125134
- });
125135
- this.sessionManager.on("terminated", async (event) => {
125136
- const sessionId = event.sessionId;
125137
- if (!this.store.has(sessionId)) {
125138
- this.logger.debug(`[${sessionId}] Ignoring terminated event for GC-cleaned session`);
125139
- return;
125140
- }
125141
- try {
125132
+ this.sessionManager.on("exit", (exit2) => {
125133
+ void (async () => {
125134
+ const sessionId = exit2.sessionId;
125135
+ this.logger.debug(`[${sessionId}] Session exit event received (exitCode=${exit2.exitCode})`);
125136
+ if (!this.store.has(sessionId)) {
125137
+ this.logger.debug(`[${sessionId}] Ignoring exit event for GC-cleaned session`);
125138
+ return;
125139
+ }
125142
125140
  this.stopMachineSessionHeartbeat(sessionId);
125143
125141
  await this.finalizeACPState(sessionId);
125144
125142
  await this.flushSessionUsage(sessionId);
125145
- } catch (error2) {
125146
- this.logger.error(`[${sessionId}] Failed to handle termination event: ${formatErrorMessage(error2)}`);
125147
- }
125143
+ const session = await this.workspaceDocument.getOrCreateSessionDoc(sessionId);
125144
+ await session.setStatus(SessionStatusFactory.idle());
125145
+ this.logger.debug(`[${sessionId}] Status set to idle (via exit event)`);
125146
+ })();
125147
+ });
125148
+ this.sessionManager.on("terminated", (event) => {
125149
+ void (async () => {
125150
+ const sessionId = event.sessionId;
125151
+ if (!this.store.has(sessionId)) {
125152
+ this.logger.debug(`[${sessionId}] Ignoring terminated event for GC-cleaned session`);
125153
+ return;
125154
+ }
125155
+ try {
125156
+ this.stopMachineSessionHeartbeat(sessionId);
125157
+ await this.finalizeACPState(sessionId);
125158
+ await this.flushSessionUsage(sessionId);
125159
+ } catch (error2) {
125160
+ this.logger.error(`[${sessionId}] Failed to handle termination event: ${formatErrorMessage(error2)}`);
125161
+ }
125162
+ })();
125148
125163
  });
125149
125164
  }
125150
125165
  setupArchiveWatcher() {
@@ -125416,7 +125431,7 @@ $mem | ConvertTo-Json -Compress
125416
125431
  const batchWindowMs = state2.acpFlushCountInTurn === 0 ? MessageHandler.ACP_INITIAL_UPDATE_BATCH_WINDOW_MS : MessageHandler.ACP_SUBSEQUENT_UPDATE_BATCH_WINDOW_MS;
125417
125432
  const timer2 = setTimeout(() => {
125418
125433
  state2.acpFlushTimer = null;
125419
- this.startACPUpdateFlush(sessionId);
125434
+ void this.startACPUpdateFlush(sessionId);
125420
125435
  }, batchWindowMs);
125421
125436
  timer2.unref?.();
125422
125437
  state2.acpFlushTimer = timer2;
@@ -125429,7 +125444,7 @@ $mem | ConvertTo-Json -Compress
125429
125444
  if (state2.acpUpdateBuffer.length === 0) {
125430
125445
  return;
125431
125446
  }
125432
- this.startACPUpdateFlush(sessionId);
125447
+ void this.startACPUpdateFlush(sessionId);
125433
125448
  }
125434
125449
  const inFlight = state2.acpFlushInFlight;
125435
125450
  if (!inFlight) {
@@ -125585,11 +125600,11 @@ $mem | ConvertTo-Json -Compress
125585
125600
  try {
125586
125601
  await handleACPUpdateMessage(sessionDoc, queue2, {
125587
125602
  logger: this.logger,
125588
- getCurrentSessionTurnId: (sessionId2) => {
125589
- const tid = this.store.getTurnId(sessionId2);
125603
+ getCurrentSessionTurnId: (currentSessionId) => {
125604
+ const tid = this.store.getTurnId(currentSessionId);
125590
125605
  if (!tid) {
125591
- captureException(new Error(`No turn id found for session ${sessionId2}`));
125592
- throw new Error(`No turn id found for session ${sessionId2}`);
125606
+ void captureException(new Error(`No turn id found for session ${currentSessionId}`));
125607
+ throw new Error(`No turn id found for session ${currentSessionId}`);
125593
125608
  }
125594
125609
  return tid;
125595
125610
  }
@@ -125967,7 +125982,7 @@ $mem | ConvertTo-Json -Compress
125967
125982
  });
125968
125983
  attachedTo = "new_entry";
125969
125984
  } else {
125970
- const message2 = latestAttachTarget.kind === "unavailable" ? `Session is ${latestAttachTarget.statusType} and the original assistant turn is no longer available for image upload` : "The original assistant turn is no longer available for image upload";
125985
+ const failureMessage = latestAttachTarget.kind === "unavailable" ? `Session is ${latestAttachTarget.statusType} and the original assistant turn is no longer available for image upload` : "The original assistant turn is no longer available for image upload";
125971
125986
  this.captureSessionImageUploadEvent(analyticsUserId, "session/image_upload_failed", {
125972
125987
  entrypoint: dispatchContext.source === "local" ? "cli_command" : "session_runtime",
125973
125988
  session_id: sessionId,
@@ -125977,13 +125992,13 @@ $mem | ConvertTo-Json -Compress
125977
125992
  local_project_id: localProjectId,
125978
125993
  repo_full_name: repoFullName,
125979
125994
  failure_reason: "active_turn_unavailable",
125980
- error_message: message2
125995
+ error_message: failureMessage
125981
125996
  });
125982
125997
  respond({
125983
125998
  success: false,
125984
125999
  workspaceId: this.workspaceId,
125985
126000
  error: "active_turn_unavailable",
125986
- message: message2
126001
+ message: failureMessage
125987
126002
  });
125988
126003
  return;
125989
126004
  }
@@ -126000,7 +126015,7 @@ $mem | ConvertTo-Json -Compress
126000
126015
  });
126001
126016
  if (!replaced) {
126002
126017
  const latestAttachTarget = await this.resolveSessionImageUploadAttachTarget(sessionId, sessionDoc);
126003
- const message2 = latestAttachTarget.kind === "active_turn" ? "Session started a new assistant turn during image upload; retry after the turn completes" : latestAttachTarget.kind === "unavailable" ? `Session is ${latestAttachTarget.statusType} and no idle assistant entry can be created` : "Reserved assistant image entry is no longer available";
126018
+ const failureMessage = latestAttachTarget.kind === "active_turn" ? "Session started a new assistant turn during image upload; retry after the turn completes" : latestAttachTarget.kind === "unavailable" ? `Session is ${latestAttachTarget.statusType} and no idle assistant entry can be created` : "Reserved assistant image entry is no longer available";
126004
126019
  this.captureSessionImageUploadEvent(analyticsUserId, "session/image_upload_failed", {
126005
126020
  entrypoint: dispatchContext.source === "local" ? "cli_command" : "session_runtime",
126006
126021
  session_id: sessionId,
@@ -126010,13 +126025,13 @@ $mem | ConvertTo-Json -Compress
126010
126025
  local_project_id: localProjectId,
126011
126026
  repo_full_name: repoFullName,
126012
126027
  failure_reason: "active_turn_unavailable",
126013
- error_message: message2
126028
+ error_message: failureMessage
126014
126029
  });
126015
126030
  respond({
126016
126031
  success: false,
126017
126032
  workspaceId: this.workspaceId,
126018
126033
  error: "active_turn_unavailable",
126019
- message: message2
126034
+ message: failureMessage
126020
126035
  });
126021
126036
  return;
126022
126037
  }
@@ -126254,20 +126269,22 @@ $mem | ConvertTo-Json -Compress
126254
126269
  });
126255
126270
  }
126256
126271
  checkForOutcome();
126257
- timeoutId = setTimeout(async () => {
126258
- if (resolved) return;
126259
- this.logger.warn(`Permission request timed out for session ${sessionId}`);
126260
- this.logger.debug(`[${sessionId}] Permission request ${requestId} timed out after ${PERMISSION_REQUEST_TIMEOUT_MS}ms`);
126261
- try {
126262
- await updatePermissionOutcomeInHistory(doc, requestId, {
126272
+ timeoutId = setTimeout(() => {
126273
+ void (async () => {
126274
+ if (resolved) return;
126275
+ this.logger.warn(`Permission request timed out for session ${sessionId}`);
126276
+ this.logger.debug(`[${sessionId}] Permission request ${requestId} timed out after ${PERMISSION_REQUEST_TIMEOUT_MS}ms`);
126277
+ try {
126278
+ await updatePermissionOutcomeInHistory(doc, requestId, {
126279
+ outcome: "cancelled"
126280
+ }, this.logger);
126281
+ } catch (error2) {
126282
+ this.logger.error(`[${sessionId}] Failed to update permission timeout in history: ${formatErrorMessage(error2)}`);
126283
+ }
126284
+ void resolveWithOutcome({
126263
126285
  outcome: "cancelled"
126264
- }, this.logger);
126265
- } catch (error2) {
126266
- this.logger.error(`[${sessionId}] Failed to update permission timeout in history: ${formatErrorMessage(error2)}`);
126267
- }
126268
- void resolveWithOutcome({
126269
- outcome: "cancelled"
126270
- });
126286
+ });
126287
+ })();
126271
126288
  }, PERMISSION_REQUEST_TIMEOUT_MS);
126272
126289
  });
126273
126290
  }
@@ -127402,7 +127419,7 @@ $mem | ConvertTo-Json -Compress
127402
127419
  }
127403
127420
  const BROKER_STATE_FILE_PATH = path__default.join(os__default.homedir(), ".lody", "broker.json");
127404
127421
  const createGitCredentialBrokerHandler = (options) => {
127405
- return async (req, res) => {
127422
+ const handleRequest = async (req, res) => {
127406
127423
  try {
127407
127424
  if (req.method === "GET" && req.url === "/health") {
127408
127425
  res.writeHead(200, {
@@ -127495,6 +127512,9 @@ $mem | ConvertTo-Json -Compress
127495
127512
  }));
127496
127513
  }
127497
127514
  };
127515
+ return (req, res) => {
127516
+ void handleRequest(req, res);
127517
+ };
127498
127518
  };
127499
127519
  const DEFAULT_HEALTH_CHECK_INTERVAL_MS = 3e4;
127500
127520
  const HEALTH_CHECK_TIMEOUT_MS = 5e3;
@@ -127600,15 +127620,17 @@ $mem | ConvertTo-Json -Compress
127600
127620
  if (this.healthCheckTimer) {
127601
127621
  return;
127602
127622
  }
127603
- this.healthCheckTimer = setInterval(async () => {
127604
- if (this.isRecovering) {
127605
- return;
127606
- }
127607
- const isHealthy = await this.checkHealth();
127608
- if (!isHealthy && this.env) {
127609
- this.logger.debug("Git credential broker health check failed, attempting recovery...");
127610
- await this.recover();
127611
- }
127623
+ this.healthCheckTimer = setInterval(() => {
127624
+ void (async () => {
127625
+ if (this.isRecovering) {
127626
+ return;
127627
+ }
127628
+ const isHealthy = await this.checkHealth();
127629
+ if (!isHealthy && this.env) {
127630
+ this.logger.debug("Git credential broker health check failed, attempting recovery...");
127631
+ await this.recover();
127632
+ }
127633
+ })();
127612
127634
  }, DEFAULT_HEALTH_CHECK_INTERVAL_MS);
127613
127635
  this.healthCheckTimer.unref();
127614
127636
  }
@@ -133609,37 +133631,14 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
133609
133631
  args: callbacks.args ?? [],
133610
133632
  getStderrTail: () => stderrTail
133611
133633
  });
133612
- agentProcess.stdin?.on("error", (err2) => {
133613
- if (err2.code !== "EPIPE") {
133614
- this.logger.error(`[${this.sessionId}] Agent stdin error: ${err2.message}`);
133615
- }
133616
- });
133617
- const input2 = new WritableStream({
133618
- write(chunk) {
133619
- if (agentProcess.stdin && !agentProcess.stdin.destroyed) {
133620
- agentProcess.stdin.write(chunk);
133621
- }
133622
- },
133623
- close() {
133624
- if (agentProcess.stdin && !agentProcess.stdin.destroyed) {
133625
- agentProcess.stdin.end();
133626
- }
133627
- }
133628
- });
133629
- const output = new ReadableStream({
133630
- start(controller) {
133631
- if (!agentProcess.stdout) {
133632
- controller.error(new Error("Agent process stdout is not available"));
133633
- return;
133634
- }
133635
- agentProcess.stdout.on("data", (chunk) => {
133636
- controller.enqueue(chunk);
133637
- });
133638
- agentProcess.stdout.on("end", () => {
133639
- controller.close();
133640
- });
133641
- }
133642
- });
133634
+ if (!agentProcess.stdin) {
133635
+ throw new Error("Agent process stdin is not available");
133636
+ }
133637
+ if (!agentProcess.stdout) {
133638
+ throw new Error("Agent process stdout is not available");
133639
+ }
133640
+ const input2 = createStdinWritableStream(agentProcess.stdin);
133641
+ const output = createStdoutReadableStream(agentProcess.stdout);
133643
133642
  const stream2 = ndJsonStream(input2, output);
133644
133643
  this.logger.debug(`[${this.sessionId}] ndJsonStream created, calling createAcpClient`);
133645
133644
  const { client, acpSessionId } = await createAcpClient({
@@ -134820,11 +134819,11 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
134820
134819
  responses
134821
134820
  });
134822
134821
  } catch (error2) {
134823
- const message2 = formatErrorMessage(error2);
134824
- config2.logger.debug(`Local session control dispatch failed: ${message2}`);
134822
+ const errorMessage = formatErrorMessage(error2);
134823
+ config2.logger.debug(`Local session control dispatch failed: ${errorMessage}`);
134825
134824
  jsonResponse(res, 500, {
134826
134825
  ok: false,
134827
- error: message2
134826
+ error: errorMessage
134828
134827
  });
134829
134828
  }
134830
134829
  }
@@ -135021,21 +135020,23 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
135021
135020
  }
135022
135021
  chunks.push(chunk);
135023
135022
  });
135024
- req.on("end", async () => {
135025
- if (res.headersSent || res.writableEnded || res.destroyed) {
135026
- return;
135027
- }
135028
- const raw = Buffer.concat(chunks).toString("utf8");
135029
- config2.logger.debug(`[local-control:${requestId}] request body received: bytes=${Buffer.byteLength(raw, "utf8")} path=${requestPath}`);
135030
- if (requestPath === SESSION_CONTROL_PATH$1) {
135031
- await handleSessionControlRequest(config2, raw, res, requestId);
135032
- return;
135033
- }
135034
- if (requestPath === IMAGE_UPLOAD_PATH) {
135035
- await handleImageUploadRequest(config2, raw, res, requestId);
135036
- return;
135037
- }
135038
- await handleProjectControlRequest(config2, raw, res, requestId);
135023
+ req.on("end", () => {
135024
+ void (async () => {
135025
+ if (res.headersSent || res.writableEnded || res.destroyed) {
135026
+ return;
135027
+ }
135028
+ const raw = Buffer.concat(chunks).toString("utf8");
135029
+ config2.logger.debug(`[local-control:${requestId}] request body received: bytes=${Buffer.byteLength(raw, "utf8")} path=${requestPath}`);
135030
+ if (requestPath === SESSION_CONTROL_PATH$1) {
135031
+ await handleSessionControlRequest(config2, raw, res, requestId);
135032
+ return;
135033
+ }
135034
+ if (requestPath === IMAGE_UPLOAD_PATH) {
135035
+ await handleImageUploadRequest(config2, raw, res, requestId);
135036
+ return;
135037
+ }
135038
+ await handleProjectControlRequest(config2, raw, res, requestId);
135039
+ })();
135039
135040
  });
135040
135041
  req.on("error", (error2) => {
135041
135042
  config2.logger.debug(`[local-control:${requestId}] request stream error: ${error2.message}`);
@@ -136041,7 +136042,7 @@ export PATH=${toSingleQuotedShellString(ghShimBinDir)}:"$PATH"
136041
136042
  this.retryTimers.clear();
136042
136043
  this.unsubscribeWorkspaces?.();
136043
136044
  this.unsubscribeWorkspaces = null;
136044
- this.convex?.close();
136045
+ void this.convex?.close();
136045
136046
  this.convex = null;
136046
136047
  const runtimes = Array.from(this.runtimes.values());
136047
136048
  this.runtimes.clear();
@@ -169751,7 +169752,7 @@ ${result.stderr}`;
169751
169752
  if (error2 instanceof Error && error2.name === "CommanderError") {
169752
169753
  process.exit(1);
169753
169754
  } else {
169754
- reportError("cli", error2, {
169755
+ void reportError("cli", error2, {
169755
169756
  logger: cliLogger,
169756
169757
  fatal: true
169757
169758
  }).finally(() => process.exit(1));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lody",
3
- "version": "0.46.2-next.1",
3
+ "version": "0.47.0",
4
4
  "description": "Lody Agent CLI tool for managing remote command execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,7 +28,7 @@
28
28
  "@better-auth/api-key": "1.5.5",
29
29
  "@convex-dev/better-auth": "0.11.2",
30
30
  "@loro-dev/flock-wasm": "^0.2.1",
31
- "@loro-dev/streams-crdt": "0.8.0",
31
+ "@loro-dev/streams-crdt": "0.8.1",
32
32
  "@sentry/node": "^10.29.0",
33
33
  "@types/cross-spawn": "^6.0.6",
34
34
  "@types/iconv-lite": "^0.0.1",
@@ -73,8 +73,8 @@
73
73
  "ws": "^8.18.3",
74
74
  "zod": "^4.1.5",
75
75
  "@lody/convex": "0.0.1",
76
- "@lody/cli-supervisor": "0.0.1",
77
76
  "@lody/loro-streams-rpc": "0.0.1",
77
+ "@lody/cli-supervisor": "0.0.1",
78
78
  "@lody/shared": "0.0.1",
79
79
  "loro-code": "0.0.1"
80
80
  },