transitions-refine 0.3.12 → 0.3.13

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/package.json +1 -1
  2. package/server/relay.mjs +25 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "transitions-refine",
3
- "version": "0.3.12",
3
+ "version": "0.3.13",
4
4
  "description": "Live, agent-driven Refine panel for CSS/Motion transitions — injects a timeline + Refine UI and runs transitions.dev suggestions via your coding agent.",
5
5
  "type": "module",
6
6
  "bin": {
package/server/relay.mjs CHANGED
@@ -91,6 +91,13 @@ const PENDING_TIMEOUT_MS = Number(process.env.REFINE_PENDING_TIMEOUT_MS) || 1200
91
91
  // to stop (it returns {stop:true} from /jobs/next). 0 disables. Only the chat
92
92
  // loop is affected — a wired REFINE_AGENT_CMD never polls /jobs/next.
93
93
  const POLLER_IDLE_STOP_MS = Number(process.env.REFINE_POLLER_IDLE_STOP_MS) || 600000;
94
+ // After a Stop (manual or idle), hold the poller "stopped" for this long: every
95
+ // GET /jobs/next keeps getting {stop:true} and /health reports the poller
96
+ // inactive. Without it, a single straggler poll fired right after the stop
97
+ // (an agent mid-backoff, or one that batches a poll before exiting) re-registers
98
+ // as live and the panel flips "Live" back on a few seconds later. A genuine
99
+ // re-run of `/refine live` after the window resumes normally.
100
+ const STOP_GRACE_MS = Number(process.env.REFINE_STOP_GRACE_MS) || 5000;
94
101
 
95
102
  /** @type {Map<string, Job>} */
96
103
  const jobs = new Map();
@@ -99,7 +106,10 @@ const now = () => Date.now();
99
106
  // When did a `/refine live` agent last poll? Used to know if LLM mode can be
100
107
  // served by a live editor agent (vs. needing REFINE_AGENT_CMD).
101
108
  let lastPollAt = 0;
102
- const pollerActive = () => now() - lastPollAt < POLLER_TTL_MS;
109
+ // While now() < stoppedUntil the relay holds a just-issued Stop (see STOP_GRACE_MS):
110
+ // the poller reports inactive and every /jobs/next answers {stop:true}.
111
+ let stoppedUntil = 0;
112
+ const pollerActive = () => now() >= stoppedUntil && now() - lastPollAt < POLLER_TTL_MS;
103
113
  const llmAvailable = () => Boolean(AGENT_CMD) || pollerActive();
104
114
 
105
115
  // Stop signal for the in-chat `/refine live` loop. Set by POST /poller/stop
@@ -653,6 +663,13 @@ const server = createServer(async (req, res) => {
653
663
 
654
664
  // GET /jobs/next — long-poll claimed by a `/refine live` agent (LLM jobs).
655
665
  if (method === "GET" && path === "/jobs/next") {
666
+ // Stop-grace window: a Stop was just issued, so keep telling every poller to
667
+ // exit and don't count these polls as live (we deliberately do NOT update
668
+ // lastPollAt here). This stops a straggler poll from reviving the session.
669
+ // A pending job still wins so we never drop real work on the stop edge.
670
+ if (now() < stoppedUntil && !nextPendingLlm()) {
671
+ return send(res, 200, { stop: true });
672
+ }
656
673
  lastPollAt = now();
657
674
  if (!lastJobAt) lastJobAt = now(); // seed idle window on the loop's first poll
658
675
  // Stop signal (manual Stop button or idle auto-stop) → tell the loop to exit.
@@ -661,15 +678,17 @@ const server = createServer(async (req, res) => {
661
678
  stopRequested = false;
662
679
  lastJobAt = 0;
663
680
  lastPollAt = 0; // loop is exiting → report it inactive immediately on /health
681
+ stoppedUntil = now() + STOP_GRACE_MS; // hold the stop so re-polls can't revive it
664
682
  return send(res, 200, { stop: true });
665
683
  }
666
684
  const deadline = now() + LONGPOLL_MS;
667
685
  const attempt = () => {
668
686
  if (res.writableEnded) return;
669
- if (stopRequested && !nextPendingLlm()) {
687
+ if ((stopRequested || now() < stoppedUntil) && !nextPendingLlm()) {
670
688
  stopRequested = false;
671
689
  lastJobAt = 0;
672
690
  lastPollAt = 0; // loop is exiting → report it inactive immediately on /health
691
+ stoppedUntil = now() + STOP_GRACE_MS; // hold the stop so re-polls can't revive it
673
692
  return send(res, 200, { stop: true });
674
693
  }
675
694
  const job = nextPendingLlm();
@@ -688,8 +707,11 @@ const server = createServer(async (req, res) => {
688
707
  // POST /poller/stop — the panel's "Stop" button. Flags the in-chat loop to
689
708
  // exit on its next poll. No-op for a wired REFINE_AGENT_CMD (never polls).
690
709
  if (method === "POST" && path === "/poller/stop") {
710
+ const wasActive = now() - lastPollAt < POLLER_TTL_MS;
691
711
  stopRequested = true;
692
- return send(res, 200, { ok: true, stopping: pollerActive() });
712
+ lastPollAt = 0; // report inactive immediately; a straggler poll won't revive it
713
+ stoppedUntil = now() + STOP_GRACE_MS; // hold the stop through the grace window
714
+ return send(res, 200, { ok: true, stopping: wasActive });
693
715
  }
694
716
 
695
717
  const m = path.match(/^\/jobs\/([^/]+)(?:\/(status|result|error))?$/);