lark-coding-assistant 0.2.7 → 0.2.8

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.
@@ -450,6 +450,7 @@ var SessionReconciler = class {
450
450
  misses = /* @__PURE__ */ new Map();
451
451
  async reconcile(input, discover = false, signal) {
452
452
  const sessions = { ...input.sessions };
453
+ const removedSessions = [];
453
454
  let changed = false;
454
455
  for (const [id, session] of Object.entries(sessions)) {
455
456
  if (signal?.aborted) throw signal.reason;
@@ -467,9 +468,14 @@ var SessionReconciler = class {
467
468
  continue;
468
469
  }
469
470
  if (result2.status === "dead") {
471
+ const terminalOutput = await this.tmux.capture(result2.pane.paneId, 80, signal).catch((error) => {
472
+ void this.log(`failed to capture dead tmux pane ${result2.pane.paneId}: ${errorMessage2(error)}`);
473
+ return "";
474
+ });
470
475
  await this.tmux.killSession(result2.pane.sessionName, signal).catch((error) => this.log(
471
476
  `failed to clean dead tmux session ${result2.pane.sessionName}: ${errorMessage2(error)}`
472
477
  ));
478
+ removedSessions.push({ session, pane: result2.pane, terminalOutput });
473
479
  delete sessions[id];
474
480
  this.misses.delete(id);
475
481
  changed = true;
@@ -478,6 +484,7 @@ var SessionReconciler = class {
478
484
  const misses = (this.misses.get(id) ?? 0) + 1;
479
485
  this.misses.set(id, misses);
480
486
  if (misses < this.missingThreshold) continue;
487
+ removedSessions.push({ session });
481
488
  delete sessions[id];
482
489
  this.misses.delete(id);
483
490
  changed = true;
@@ -489,7 +496,7 @@ var SessionReconciler = class {
489
496
  if (activeSessionId !== input.activeSessionId) changed = true;
490
497
  const removedActive = input.activeSessionId && !sessions[input.activeSessionId] ? input.sessions?.[input.activeSessionId] : void 0;
491
498
  const state = changed ? { ...input, sessions, activeSessionId, updatedAt: Date.now() } : input;
492
- return { state, liveSessions: Object.values(sessions), removedActive, changed };
499
+ return { state, liveSessions: Object.values(sessions), removedActive, removedSessions, changed };
493
500
  }
494
501
  async confirm(session, signal) {
495
502
  const direct = await this.tmux.inspectStatus(session.paneId, signal);
@@ -3222,6 +3229,10 @@ var AssistantDaemon = class {
3222
3229
  }
3223
3230
  case "start":
3224
3231
  return this.startSession(request.sessionId, request.cwd, request.agent, request.resume);
3232
+ case "reconcile": {
3233
+ await this.reconcileSessions(true);
3234
+ return { ok: true };
3235
+ }
3225
3236
  case "status":
3226
3237
  return { ok: true, value: await this.runtimeStatus(request.sessionId) };
3227
3238
  case "tail":
@@ -3333,11 +3344,6 @@ var AssistantDaemon = class {
3333
3344
  agentVersion,
3334
3345
  updatedAt: Date.now()
3335
3346
  };
3336
- const pendingClaim = this.pendingAgentSessionClaims.get(sessionId);
3337
- if (pendingClaim) {
3338
- const claimed = this.claimStartingAgentSession(session, pendingClaim);
3339
- if (!claimed.ok) return claimed;
3340
- }
3341
3347
  if (resume && resume.mode !== "picker") {
3342
3348
  const initialClaim = await context.stage(
3343
3349
  "agent-identity",
@@ -3376,11 +3382,6 @@ var AssistantDaemon = class {
3376
3382
  ));
3377
3383
  await this.rememberSessionWorkspace(session.cwd);
3378
3384
  }
3379
- const lateClaim = this.pendingAgentSessionClaims.get(sessionId);
3380
- if (lateClaim) {
3381
- const claimed = this.claimStartingAgentSession(session, lateClaim);
3382
- if (!claimed.ok) return claimed;
3383
- }
3384
3385
  try {
3385
3386
  await context.stage("metadata", () => this.tmux.writeMetadata(pane.sessionName, {
3386
3387
  managed: true,
@@ -4654,20 +4655,56 @@ ${escapeFence2(output).slice(-6500)}
4654
4655
  if (session && session.agent !== candidate.agent) {
4655
4656
  return { ok: false, error: "agent-session candidate does not match managed session" };
4656
4657
  }
4657
- const owner = this.findAgentSessionOwner(candidate.agent, candidate.agentSessionId, candidate.sessionId);
4658
- if (owner) {
4658
+ if (!session) {
4659
4659
  this.pendingAgentSessionClaims.set(candidate.sessionId, candidate);
4660
4660
  await this.log(
4661
- `agent session conflict: session=${candidate.sessionId} agent=${candidate.agent} agentSession=${candidate.agentSessionId} owner=${owner.id}`
4661
+ `agent session hook candidate awaiting managed session: session=${candidate.sessionId} agent=${candidate.agent} agentSession=${candidate.agentSessionId}`
4662
4662
  );
4663
- setTimeout(() => void this.rejectDuplicateAgentSession(candidate, owner), 100);
4664
- return fail(agentSessionInUse(candidate.sessionId, owner.id, candidate.agentSessionId));
4663
+ return { ok: true };
4665
4664
  }
4666
- if (!session) {
4667
- this.pendingAgentSessionClaims.set(candidate.sessionId, candidate);
4665
+ if (session.agentSessionId) {
4666
+ this.pendingAgentSessionClaims.delete(session.id);
4667
+ if (session.agentSessionId !== candidate.agentSessionId) {
4668
+ await this.log(
4669
+ `ignored agent session hook candidate: session=${session.id} agent=${session.agent} hookSession=${candidate.agentSessionId} confirmedSession=${session.agentSessionId}`
4670
+ );
4671
+ }
4668
4672
  return { ok: true };
4669
4673
  }
4670
- if (session.agentSessionId === candidate.agentSessionId) return { ok: true };
4674
+ this.pendingAgentSessionClaims.set(candidate.sessionId, candidate);
4675
+ const agentSessionId = await this.resolvePaneNativeAgentSessionId(session);
4676
+ if (!agentSessionId) return { ok: true };
4677
+ if (agentSessionId !== candidate.agentSessionId) {
4678
+ await this.log(
4679
+ `ignored stale agent session hook candidate: session=${session.id} agent=${session.agent} hookSession=${candidate.agentSessionId} pidSession=${agentSessionId}`
4680
+ );
4681
+ }
4682
+ const confirmed = { ...candidate, agentSessionId, source: "pid-confirmed" };
4683
+ const owner = this.findAgentSessionOwner(session.agent, agentSessionId, session.id);
4684
+ if (owner) {
4685
+ await this.log(
4686
+ `confirmed agent session conflict: session=${session.id} agent=${session.agent} agentSession=${agentSessionId} owner=${owner.id}`
4687
+ );
4688
+ await this.rejectDuplicateAgentSession(confirmed, owner);
4689
+ return fail(agentSessionInUse(session.id, owner.id, agentSessionId));
4690
+ }
4691
+ return this.persistConfirmedAgentSessionClaim(session, confirmed);
4692
+ }
4693
+ async resolvePaneNativeAgentSessionId(session) {
4694
+ let pane;
4695
+ try {
4696
+ pane = await this.tmux.inspect(session.paneId);
4697
+ } catch (error) {
4698
+ void this.log(`failed to inspect pane for native session: session=${session.id} error=${errorMessage3(error)}`);
4699
+ return void 0;
4700
+ }
4701
+ if (!pane || pane.dead) return void 0;
4702
+ return resolveNativeAgentSessionId(session.agent, pane.pid).catch((error) => {
4703
+ void this.log(`failed to resolve native agent session for ${session.id}: ${errorMessage3(error)}`);
4704
+ return void 0;
4705
+ });
4706
+ }
4707
+ async persistConfirmedAgentSessionClaim(session, candidate) {
4671
4708
  const updated = { ...session, agentSessionId: candidate.agentSessionId, updatedAt: Date.now() };
4672
4709
  this.state = {
4673
4710
  ...this.state,
@@ -4692,12 +4729,7 @@ ${escapeFence2(output).slice(-6500)}
4692
4729
  async refreshNativeAgentSessionClaims() {
4693
4730
  for (const session of Object.values(this.state.sessions ?? {})) {
4694
4731
  if (session.agentSessionId) continue;
4695
- const pane = await this.tmux.inspect(session.paneId);
4696
- if (!pane || pane.dead) continue;
4697
- const agentSessionId = await resolveNativeAgentSessionId(session.agent, pane.pid).catch((error) => {
4698
- void this.log(`failed to resolve native agent session for ${session.id}: ${errorMessage3(error)}`);
4699
- return void 0;
4700
- });
4732
+ const agentSessionId = await this.resolvePaneNativeAgentSessionId(session);
4701
4733
  if (!agentSessionId) continue;
4702
4734
  await this.handleAgentSessionStarted({
4703
4735
  sessionId: session.id,
@@ -4717,11 +4749,6 @@ ${escapeFence2(output).slice(-6500)}
4717
4749
  session.agentSessionId = committedClaim2;
4718
4750
  return { ok: true };
4719
4751
  }
4720
- const pending2 = this.pendingAgentSessionClaims.get(session.id);
4721
- if (pending2) {
4722
- const claimed = this.claimStartingAgentSession(session, pending2);
4723
- if (!claimed.ok || session.agentSessionId) return claimed;
4724
- }
4725
4752
  if (signal?.aborted) throw signal.reason;
4726
4753
  const pane2 = await this.tmux.inspect(session.paneId, signal);
4727
4754
  if (signal?.aborted) throw signal.reason;
@@ -4746,8 +4773,6 @@ ${escapeFence2(output).slice(-6500)}
4746
4773
  const pane = await this.tmux.inspect(session.paneId, signal);
4747
4774
  if (signal?.aborted) throw signal.reason;
4748
4775
  if (!pane || pane.dead) return fail(await this.startupExitedError(session, pane));
4749
- const pending = this.pendingAgentSessionClaims.get(session.id);
4750
- if (pending) return this.claimStartingAgentSession(session, pending);
4751
4776
  const committedClaim = this.state.sessions?.[session.id]?.agentSessionId;
4752
4777
  if (committedClaim) {
4753
4778
  session.agentSessionId = committedClaim;
@@ -4900,6 +4925,22 @@ ${output}`
4900
4925
  ).catch((error) => this.log(`exit notification failed: ${errorMessage3(error)}`));
4901
4926
  }
4902
4927
  this.state = result2.state;
4928
+ for (const removed of result2.removedSessions) {
4929
+ const exitStatus = removed.pane?.exitStatus;
4930
+ if (exitStatus === void 0 || exitStatus === 0) continue;
4931
+ const terminalExcerpt = startupTerminalExcerpt(tailScreen(removed.terminalOutput ?? "", 80).slice(-3e3));
4932
+ await this.rememberSessionExitEvent({
4933
+ sessionId: removed.session.id,
4934
+ agent: removed.session.agent,
4935
+ reason: "agent-exited",
4936
+ exitStatus,
4937
+ terminalExcerpt: terminalExcerpt || void 0,
4938
+ occurredAt: Date.now()
4939
+ });
4940
+ await this.log(
4941
+ `agent exited: session=${removed.session.id} agent=${removed.session.agent} exit=${exitStatus}`
4942
+ );
4943
+ }
4903
4944
  for (const sessionId of this.pendingResumePickers.keys()) {
4904
4945
  if (!this.state.sessions?.[sessionId]) this.pendingResumePickers.delete(sessionId);
4905
4946
  }