osborn 0.9.101 → 0.9.102

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 +32 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1682,6 +1682,10 @@ async function main() {
1682
1682
  clearTimeout(meetingMaxTimer);
1683
1683
  meetingMaxTimer = null;
1684
1684
  }
1685
+ if (meetingLeaveGraceTimer) {
1686
+ clearTimeout(meetingLeaveGraceTimer);
1687
+ meetingLeaveGraceTimer = null;
1688
+ }
1685
1689
  activeMeetingBotId = null;
1686
1690
  // leaveBot=false when Recall itself reported the meeting over (bot already gone).
1687
1691
  if (opts.leaveBot !== false) {
@@ -1703,6 +1707,31 @@ async function main() {
1703
1707
  armIdleExitTimer(`meeting ended (${reason}), no user`);
1704
1708
  }
1705
1709
  };
1710
+ // WiFi-blip grace: a TRANSIENT participant drop must not kill the meeting
1711
+ // (observed 2026-08-01: LiveKit WS blip → user_disconnected → bot left
1712
+ // within seconds while the user rejoined moments later). Arm a grace timer
1713
+ // instead; a rejoin inside the window cancels it. The deliberate coupling
1714
+ // (bot follows the voice session) survives — it just tolerates blips.
1715
+ const MEETING_LEAVE_GRACE_MS = 75_000;
1716
+ let meetingLeaveGraceTimer = null;
1717
+ const armMeetingLeaveGrace = () => {
1718
+ if (!activeMeetingBotId || meetingLeaveGraceTimer)
1719
+ return;
1720
+ console.log(`🏁 Meeting leave-grace armed (${MEETING_LEAVE_GRACE_MS / 1000}s) — bot stays unless the user is really gone`);
1721
+ meetingLeaveGraceTimer = setTimeout(() => {
1722
+ meetingLeaveGraceTimer = null;
1723
+ const userPresent = activeRoom && activeRoom.remoteParticipants.size > 0;
1724
+ if (activeMeetingBotId && !userPresent)
1725
+ void endMeeting('user_disconnected_grace_expired');
1726
+ }, MEETING_LEAVE_GRACE_MS);
1727
+ };
1728
+ const cancelMeetingLeaveGrace = () => {
1729
+ if (meetingLeaveGraceTimer) {
1730
+ clearTimeout(meetingLeaveGraceTimer);
1731
+ meetingLeaveGraceTimer = null;
1732
+ console.log('🏁 Meeting leave-grace cancelled — user is back');
1733
+ }
1734
+ };
1706
1735
  const armMeetingMaxTimer = (botId) => {
1707
1736
  if (meetingMaxTimer)
1708
1737
  clearTimeout(meetingMaxTimer);
@@ -3393,6 +3422,8 @@ async function main() {
3393
3422
  // connect callback, not via events).
3394
3423
  participantConnectedHandler = async (participant) => {
3395
3424
  console.log(`\n👤 User joined: ${participant.identity}`);
3425
+ // A user (re)arriving cancels the meeting leave-grace — the bot stays.
3426
+ cancelMeetingLeaveGrace();
3396
3427
  // A user is present — cancel any pending agent-side "alone" leave.
3397
3428
  if (aloneTimer) {
3398
3429
  clearTimeout(aloneTimer);
@@ -4307,7 +4338,7 @@ async function main() {
4307
4338
  // status surface for them yet). Revisit only WITH a tracking UI.
4308
4339
  // endMeeting() centralizes the teardown (flush, poller, max-timer, Recall
4309
4340
  // leave, frontend notify).
4310
- void endMeeting('user_disconnected');
4341
+ armMeetingLeaveGrace();
4311
4342
  // 0.9.83: a real session just ended → use the FAST leave (~20s), not the
4312
4343
  // 3-min alone grace. Runs on the agent, so it fires even on an abrupt tab
4313
4344
  // close. Cancelled if a user rejoins within the grace (handled where the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osborn",
3
- "version": "0.9.101",
3
+ "version": "0.9.102",
4
4
  "description": "Voice AI coding assistant - local agent that connects to Osborn frontend",
5
5
  "type": "module",
6
6
  "bin": {