shadok-ai 0.16.2 → 0.16.3

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.
package/README.md CHANGED
@@ -531,6 +531,17 @@ every 25s (tunable with `SHADOK_WS_PING_MS`) to keep the connection warm, so no
531
531
  proxy change is normally needed; if your proxy still cuts it, raise
532
532
  `proxy_read_timeout` above the ping interval.
533
533
 
534
+ **Sleep / wake.** When a laptop sleeps, the socket's TCP connection usually dies
535
+ without the browser ever seeing a `close`, so it keeps reporting the link as
536
+ "open" while nothing flows. On wake the cockpit no longer trusts that stale
537
+ state: a real wake — the page becoming visible after more than ~10s away, a
538
+ network change (`online`), or a page restored from the back/forward cache —
539
+ tears the suspect socket down and opens a fresh one at once, reattaching to the
540
+ same session. A quick tab switch is left alone (the link is almost certainly
541
+ fine), so you don't pay a reconnect every time you glance away. Previously a
542
+ woken tab waited out the browser's own TCP timeout, tens of seconds to minutes,
543
+ stuck on "reconnecting…".
544
+
534
545
  ### Cross-instance peers
535
546
 
536
547
  Agents can spawn and drive agents on **another** instance — another of yours, or
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shadok-ai",
3
- "version": "0.16.2",
3
+ "version": "0.16.3",
4
4
  "main": "dist/session.js",
5
5
  "scripts": {
6
6
  "test": "node --import tsx --test --test-timeout=120000 test/*.ts context/*/test/*.test.mjs",
package/public/index.html CHANGED
@@ -7065,19 +7065,50 @@
7065
7065
  }
7066
7066
 
7067
7067
  /** Reconnect every dropped tab immediately (no backoff wait) — fired when the
7068
- * machine/tab comes back (online / visible), the common laptop-wake case. */
7069
- function reconnectAllNow() {
7068
+ * machine/tab comes back (online / visible), the common laptop-wake case.
7069
+ * `force` replaces even a socket that still reports OPEN: after a laptop
7070
+ * sleep the TCP connection is usually dead while the browser has seen no
7071
+ * `close`, so `readyState` lies "OPEN" and the gentle path would skip the
7072
+ * tab — leaving it stranded until the browser's own TCP timeout fires
7073
+ * (tens of seconds to minutes), which is exactly the "stuck reconnecting,
7074
+ * very slow" symptom. A CONNECTING socket is always left alone: a link is
7075
+ * already in flight, and tearing it down would only restart the clock. */
7076
+ function reconnectAllNow(force) {
7070
7077
  for (const t of tabs) {
7071
7078
  if (t.closing || !t.sessionId) continue;
7072
7079
  const st = t.ws && t.ws.readyState;
7073
- if (st === WebSocket.OPEN || st === WebSocket.CONNECTING) continue;
7080
+ if (st === WebSocket.CONNECTING) continue; // a link is already on its way
7081
+ if (st === WebSocket.OPEN && !force) continue; // healthy and no reason to doubt it
7074
7082
  if (t.reconnectTimer) clearTimeout(t.reconnectTimer);
7075
7083
  t.reconnectAttempts = 0;
7076
- openLink(t);
7077
- }
7078
- }
7079
- window.addEventListener("online", reconnectAllNow);
7080
- document.addEventListener("visibilitychange", () => { if (!document.hidden) reconnectAllNow(); });
7084
+ const old = t.ws;
7085
+ openLink(t); // replaces t.ws with the fresh socket
7086
+ // Close the suspected-zombie only after openLink swapped t.ws, so its
7087
+ // `close` handler sees `t.ws !== ws` and does not schedule a competing
7088
+ // reconnect. A no-op when `old` was already closed.
7089
+ if (old && old !== t.ws) { try { old.close(); } catch {} }
7090
+ }
7091
+ }
7092
+
7093
+ // A real wake (laptop sleep, bfcache restore, a network change) must force a
7094
+ // fresh link; a quick tab switch must not — forcing there would flash
7095
+ // "reconnecting" and pay a resume round-trip every time you glance away.
7096
+ // The tell is how long we were hidden: seconds for an alt-tab, minutes for a
7097
+ // sleep. `online` and a bfcache `pageshow` are forced outright — the network
7098
+ // or the page itself just changed under us.
7099
+ const STALE_WAKE_MS = 10_000;
7100
+ let hiddenAt = 0;
7101
+ function wokeAfterLongAway() {
7102
+ const away = hiddenAt ? Date.now() - hiddenAt : 0;
7103
+ hiddenAt = 0;
7104
+ return away > STALE_WAKE_MS;
7105
+ }
7106
+ window.addEventListener("online", () => reconnectAllNow(true));
7107
+ window.addEventListener("pageshow", (e) => reconnectAllNow(!!e.persisted || wokeAfterLongAway()));
7108
+ document.addEventListener("visibilitychange", () => {
7109
+ if (document.hidden) { hiddenAt = Date.now(); return; }
7110
+ reconnectAllNow(wokeAfterLongAway());
7111
+ });
7081
7112
 
7082
7113
  /**
7083
7114
  * Lists the agents alive in the server right now — including ones spawned