@mjasnikovs/pi-task 0.14.1 → 0.14.2

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.
@@ -53,6 +53,7 @@ export function clientScript(wsUrl) {
53
53
  let autoScroll = true;
54
54
  let reconnectDelay = 1000;
55
55
  let reconnectAnim = null;
56
+ let reconnectTimer = null;
56
57
  let ws = null;
57
58
 
58
59
  const BT = String.fromCharCode(96);
@@ -784,8 +785,15 @@ export function clientScript(wsUrl) {
784
785
  });
785
786
 
786
787
  function connect() {
787
- ws = new WebSocket(WS_URL);
788
- ws.addEventListener('open', () => {
788
+ // Capture the socket locally so a superseded socket's late events (a
789
+ // delayed 'close' after we already opened a replacement) can't touch the
790
+ // overlay or schedule a second reconnect — every handler bails unless it's
791
+ // still the current socket.
792
+ const sock = new WebSocket(WS_URL);
793
+ ws = sock;
794
+ sock.addEventListener('open', () => {
795
+ if (ws !== sock) return;
796
+ if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
789
797
  if (reconnectAnim) { clearInterval(reconnectAnim); reconnectAnim = null; }
790
798
  reconnectOverlay.classList.remove('visible');
791
799
  reconnectDelay = 1000;
@@ -797,10 +805,12 @@ export function clientScript(wsUrl) {
797
805
  // endpoint, so a redundant re-POST is harmless.
798
806
  if (notifyEnabled()) { subscribePush().catch(function () {}); }
799
807
  });
800
- ws.addEventListener('message', (e) => {
808
+ sock.addEventListener('message', (e) => {
809
+ if (ws !== sock) return;
801
810
  try { handleMsg(JSON.parse(e.data)); } catch {}
802
811
  });
803
- ws.addEventListener('close', () => {
812
+ sock.addEventListener('close', () => {
813
+ if (ws !== sock) return;
804
814
  setEnabled(false);
805
815
  reconnectOverlay.classList.add('visible');
806
816
  // Animate the same braille spinner used elsewhere, with a live countdown.
@@ -816,9 +826,31 @@ export function clientScript(wsUrl) {
816
826
  if (reconnectAnim) clearInterval(reconnectAnim);
817
827
  paint();
818
828
  reconnectAnim = setInterval(paint, 90);
819
- setTimeout(() => { reconnectDelay = Math.min(reconnectDelay * 2, 30000); connect(); }, reconnectDelay);
829
+ if (reconnectTimer) clearTimeout(reconnectTimer);
830
+ reconnectTimer = setTimeout(() => {
831
+ reconnectTimer = null;
832
+ reconnectDelay = Math.min(reconnectDelay * 2, 30000);
833
+ connect();
834
+ }, reconnectDelay);
820
835
  });
821
836
  }
822
837
 
838
+ // Reconnect immediately instead of waiting out the exponential backoff. A
839
+ // phone that backgrounds the PWA throttles our retry timer and the radio
840
+ // drops, so by the time it foregrounds reconnectDelay can be pinned at 30s —
841
+ // leaving the user staring at a spinner (over an already-updated question)
842
+ // while the server is reachable RIGHT NOW. Returning to the tab, regaining
843
+ // network, or refocusing the window should all retry at once. No-op if a
844
+ // socket is already open or a connect is in flight.
845
+ function connectNow() {
846
+ if (ws && (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN)) return;
847
+ if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
848
+ reconnectDelay = 1000; // a deliberate return shouldn't inherit a stale 30s backoff
849
+ connect();
850
+ }
851
+ document.addEventListener('visibilitychange', () => { if (!document.hidden) connectNow(); });
852
+ window.addEventListener('online', connectNow);
853
+ window.addEventListener('focus', connectNow);
854
+
823
855
  connect();`;
824
856
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mjasnikovs/pi-task",
3
- "version": "0.14.1",
3
+ "version": "0.14.2",
4
4
  "description": "Deterministic spec-orchestration for local models, with a bundled real-time remote web view and web/docs/fetch/worker subagent tools.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",