lark-coding-assistant 0.2.5 → 0.2.7

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.
@@ -2280,7 +2280,6 @@ var LarkGateway = class {
2280
2280
  source: "lark-coding-assistant",
2281
2281
  policy: { dmMode: "open", requireMention: false, respondToMentionAll: false },
2282
2282
  safety: { chatQueue: { enabled: true, mergeWhileBusy: false } },
2283
- handshakeTimeoutMs: 8e3,
2284
2283
  httpTimeoutMs: 3e4,
2285
2284
  respectProxyEnv: true
2286
2285
  });
@@ -3392,6 +3391,7 @@ var AssistantDaemon = class {
3392
3391
  agentSessionId: session.agentSessionId
3393
3392
  }, context.signal));
3394
3393
  await context.stage("state", () => this.commitStartedSession(session, binding));
3394
+ await this.clearSessionExitEvent(sessionId, context.startedAt);
3395
3395
  } catch (error) {
3396
3396
  return fail(isAppError(error) ? error : new AppError("START_FAILED", "failed to persist completed session", { sessionId }, { cause: error }));
3397
3397
  }
@@ -4481,6 +4481,26 @@ ${escapeFence2(output).slice(-6500)}
4481
4481
  }
4482
4482
  await this.store.saveState(this.state);
4483
4483
  }
4484
+ async clearSessionExitEvent(sessionId, before) {
4485
+ const event = this.state.recentSessionExits?.[sessionId];
4486
+ if (!event || event.occurredAt >= before) return;
4487
+ const recentSessionExits = { ...this.state.recentSessionExits };
4488
+ delete recentSessionExits[sessionId];
4489
+ this.state = { ...this.state, recentSessionExits, updatedAt: Date.now() };
4490
+ await this.store.saveState(this.state);
4491
+ }
4492
+ async rememberSessionExitEvent(event) {
4493
+ const cutoff = Date.now() - 24 * 60 * 60 * 1e3;
4494
+ const recentSessionExits = Object.fromEntries(Object.entries(this.state.recentSessionExits ?? {}).filter(([, value]) => value.occurredAt >= cutoff));
4495
+ recentSessionExits[event.sessionId] = event;
4496
+ const retained = Object.entries(recentSessionExits).sort(([, left], [, right]) => right.occurredAt - left.occurredAt).slice(0, 20);
4497
+ this.state = {
4498
+ ...this.state,
4499
+ recentSessionExits: Object.fromEntries(retained),
4500
+ updatedAt: Date.now()
4501
+ };
4502
+ await this.store.saveState(this.state);
4503
+ }
4484
4504
  async resetOwner() {
4485
4505
  this.pendingMessages.length = 0;
4486
4506
  this.pendingInteractionInput = void 0;
@@ -4641,7 +4661,7 @@ ${escapeFence2(output).slice(-6500)}
4641
4661
  `agent session conflict: session=${candidate.sessionId} agent=${candidate.agent} agentSession=${candidate.agentSessionId} owner=${owner.id}`
4642
4662
  );
4643
4663
  setTimeout(() => void this.rejectDuplicateAgentSession(candidate, owner), 100);
4644
- return fail(agentSessionInUse(candidate.sessionId, owner.id));
4664
+ return fail(agentSessionInUse(candidate.sessionId, owner.id, candidate.agentSessionId));
4645
4665
  }
4646
4666
  if (!session) {
4647
4667
  this.pendingAgentSessionClaims.set(candidate.sessionId, candidate);
@@ -4750,7 +4770,7 @@ ${escapeFence2(output).slice(-6500)}
4750
4770
  const owner = this.findAgentSessionOwner(candidate.agent, candidate.agentSessionId, session.id);
4751
4771
  if (owner) {
4752
4772
  this.pendingAgentSessionClaims.delete(session.id);
4753
- return fail(agentSessionInUse(session.id, owner.id));
4773
+ return fail(agentSessionInUse(session.id, owner.id, candidate.agentSessionId));
4754
4774
  }
4755
4775
  session.agentSessionId = candidate.agentSessionId;
4756
4776
  session.updatedAt = Date.now();
@@ -4788,6 +4808,14 @@ ${escapeFence2(output).slice(-6500)}
4788
4808
  return pending ? { id: pending.sessionId } : void 0;
4789
4809
  }
4790
4810
  async rejectDuplicateAgentSession(candidate, owner) {
4811
+ await this.rememberSessionExitEvent({
4812
+ sessionId: candidate.sessionId,
4813
+ agent: candidate.agent,
4814
+ reason: "agent-session-conflict",
4815
+ agentSessionId: candidate.agentSessionId,
4816
+ ownerSessionId: owner.id,
4817
+ occurredAt: Date.now()
4818
+ });
4791
4819
  const duplicate = this.state.sessions?.[candidate.sessionId];
4792
4820
  if (duplicate) await this.stopSession(duplicate.id).catch((error) => this.log(
4793
4821
  `failed to stop duplicate agent session ${duplicate.id}: ${errorMessage3(error)}`
@@ -4800,7 +4828,7 @@ ${escapeFence2(output).slice(-6500)}
4800
4828
  if (this.state.boundChatId) {
4801
4829
  await this.gateway?.sendText(
4802
4830
  this.state.boundChatId,
4803
- `${candidate.agent} \u539F\u751F session \u5DF2\u7531 LCA session\u300C${owner.id}\u300D\u8FDE\u63A5\uFF1B\u5DF2\u505C\u6B62\u91CD\u590D\u521B\u5EFA\u7684\u300C${candidate.sessionId}\u300D\u3002\u8BF7\u7528 /sessions \u8FDE\u63A5\u300C${owner.id}\u300D\u3002`
4831
+ `${candidate.agent} \u539F\u751F session\u300C${candidate.agentSessionId}\u300D\u5DF2\u7531 LCA session\u300C${owner.id}\u300D\u8FDE\u63A5\uFF1B\u5DF2\u505C\u6B62\u91CD\u590D\u521B\u5EFA\u7684\u300C${candidate.sessionId}\u300D\u3002\u8BF7\u7528 /sessions \u8FDE\u63A5\u300C${owner.id}\u300D\u3002`
4804
4832
  ).catch((error) => this.log(`agent session conflict notification failed: ${errorMessage3(error)}`));
4805
4833
  }
4806
4834
  }
@@ -5036,11 +5064,11 @@ function abortableDelay(ms, signal) {
5036
5064
  signal.addEventListener("abort", aborted, { once: true });
5037
5065
  });
5038
5066
  }
5039
- function agentSessionInUse(sessionId, ownerSessionId) {
5067
+ function agentSessionInUse(sessionId, ownerSessionId, agentSessionId) {
5040
5068
  return new AppError(
5041
5069
  "AGENT_SESSION_IN_USE",
5042
5070
  `agent session is already managed by ${ownerSessionId}`,
5043
- { sessionId, ownerSessionId }
5071
+ { sessionId, ownerSessionId, agentSessionId }
5044
5072
  );
5045
5073
  }
5046
5074
  function errorMessage3(error) {
@@ -5219,6 +5247,14 @@ function resolveAppPaths(root = process.env.LARK_CODING_ASSISTANT_HOME) {
5219
5247
 
5220
5248
  // src/daemon-entry.ts
5221
5249
  import { readFile as readFile4 } from "fs/promises";
5250
+
5251
+ // src/daemon/transport-errors.ts
5252
+ function isRecoverableTransportError(error) {
5253
+ if (!(error instanceof Error)) return false;
5254
+ return error.message === "WebSocket was closed before the connection was established";
5255
+ }
5256
+
5257
+ // src/daemon-entry.ts
5222
5258
  var paths = resolveAppPaths();
5223
5259
  var packageInfo = JSON.parse(
5224
5260
  await readFile4(new URL("../package.json", import.meta.url), "utf8")
@@ -5245,10 +5281,18 @@ try {
5245
5281
  process.exitCode = 1;
5246
5282
  }
5247
5283
  process.on("uncaughtException", (error) => {
5284
+ if (isRecoverableTransportError(error)) {
5285
+ console.error(`${(/* @__PURE__ */ new Date()).toISOString()} daemon recoverable transport exception; keeping daemon alive`, error);
5286
+ return;
5287
+ }
5248
5288
  console.error(`${(/* @__PURE__ */ new Date()).toISOString()} daemon uncaught exception`, error);
5249
5289
  process.exit(1);
5250
5290
  });
5251
5291
  process.on("unhandledRejection", (error) => {
5292
+ if (isRecoverableTransportError(error)) {
5293
+ console.error(`${(/* @__PURE__ */ new Date()).toISOString()} daemon recoverable transport rejection; keeping daemon alive`, error);
5294
+ return;
5295
+ }
5252
5296
  console.error(`${(/* @__PURE__ */ new Date()).toISOString()} daemon unhandled rejection`, error);
5253
5297
  process.exit(1);
5254
5298
  });