osborn 0.9.100 → 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.
@@ -13,12 +13,21 @@ capture, DevTools diagnostics, and metrics. It can speak into the page's mic
13
13
  (synthesized on the fly), hear and transcribe what it plays back, click via a
14
14
  natural-language brain, and improvise conversations. Works on any website.
15
15
 
16
- USE IT BROADLY — not just for tests: MANDATORY for debugging anything
16
+ USE IT BROADLY — not just for tests: it is THE QA PROTOCOL for UI work —
17
+ IMMEDIATELY AFTER building/styling/changing any frontend, the final build
18
+ step is recording proof at mobile + desktop widths (never ask the user to
19
+ eyeball or screenshot your own change). Also MANDATORY for debugging anything
17
20
  web-reachable (real browser + DevTools on camera + /logs, never debug a
18
21
  frontend blind) and for verifying deployed web changes (journey + clip, or it
19
22
  is a claim, not a verification). Also: reproducing user issues (mobile view),
20
- the meeting-canvas cast source, site research, voice testing. Every use
21
- returns media — review it and deliver it to the user, no silent runs.
23
+ the meeting-canvas cast source, site research, voice testing.
24
+
25
+ IRON LAW (applies even before you fetch the served copy): NO RESULT REPORT
26
+ WITHOUT FRAME-REVIEW EVIDENCE — a green assertion is a claim; a read frame is
27
+ proof. The served skill contains a Run Checklist you MUST copy into your
28
+ response and check off; `scripts/review-run.sh <clip>` in the harness does the
29
+ frame extraction. Media is reviewed by you AND delivered to the user, every
30
+ run, no exceptions.
22
31
 
23
32
  ## UPDATE CHECK (notify-first, run before major runs)
24
33
  This packaged copy ships with every machine and may lag the served version.
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.100",
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": {