@brettinternet/pi-loop 0.1.4 → 0.1.5

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 (3) hide show
  1. package/README.md +2 -0
  2. package/index.ts +33 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -26,6 +26,8 @@ pi install npm:@brettinternet/pi-loop
26
26
 
27
27
  Durations use `ms`, `s`, `m`, `h`, or `d`. Delays range from 1 second to 24 hours. Timed loops run for at most 30 days. During a loop, `/loop time <duration>` switches to a deadline from now or resets the current deadline; timed mode requires a non-zero delay. `/loop <count>` switches back to a count of future iterations.
28
28
 
29
+ A pending `/wait` or `until` watch keeps the current iteration's session alive until its wake-up turn settles (or it is cancelled). A paused wait or recurring watch holds the iteration until resumed or completed. Use `/loop delay` for a simple fixed gap between iterations; use `/wait` for a same-session follow-up and `until` for a condition that might become true earlier than a fixed deadline.
30
+
29
31
  Errors retry after 30 seconds, 1 minute, and 2 minutes, then pause. Aborting pauses the loop. `/loop pause` pauses after the active iteration settles; if the loop is between iterations, it pauses immediately. Resuming then starts the next iteration. An agent `loop_pause` request pauses mid-iteration for human blockers, and resuming continues that iteration. Recovery preserves the loop so you can resume or advance it.
30
32
 
31
33
  Chain commands in the prompt:
package/index.ts CHANGED
@@ -639,6 +639,24 @@ export default function loopExtension(pi: ExtensionAPI): void {
639
639
  let pendingFailure: PendingFailure | undefined;
640
640
  let currentSessionManagerRef: unknown;
641
641
  let herdrBlocked = false;
642
+ const pendingWakes = new Map<string, boolean>();
643
+ let waitingForWake = false;
644
+ let wakeContext: ExtensionContext | undefined;
645
+
646
+ for (const channel of ["pi-until:busy", "pi-wait:busy"]) {
647
+ pi.events.on(channel, (value) => {
648
+ if (typeof value !== "boolean") return;
649
+ pendingWakes.set(channel, value);
650
+ if (!waitingForWake || [...pendingWakes.values()].some(Boolean)) return;
651
+ const ctx = wakeContext;
652
+ if (!ctx || !ctx.isIdle()) return;
653
+ const state = currentState(ctx);
654
+ if (!state || !statusIsActive(state) || transitionInFlight) return;
655
+ waitingForWake = false;
656
+ handledSettlementKey = stateKey(ctx, state);
657
+ scheduleContinuation(ctx, state);
658
+ });
659
+ }
642
660
 
643
661
  function reportHerdrBlocked(state: LoopState | undefined): void {
644
662
  const blocked = state?.status === "paused";
@@ -1523,6 +1541,9 @@ export default function loopExtension(pi: ExtensionAPI): void {
1523
1541
  clearCommandInterruption();
1524
1542
  pendingFailure = undefined;
1525
1543
  currentSessionManagerRef = ctx.sessionManager;
1544
+ pendingWakes.clear();
1545
+ waitingForWake = false;
1546
+ wakeContext = ctx;
1526
1547
  transitionInFlight = false;
1527
1548
  handledSettlementKey = undefined;
1528
1549
  const loaded = latestStateFromContext(ctx);
@@ -1593,6 +1614,12 @@ export default function loopExtension(pi: ExtensionAPI): void {
1593
1614
  return;
1594
1615
  }
1595
1616
  if (handledSettlementKey === key) return;
1617
+ if ([...pendingWakes.values()].some(Boolean)) {
1618
+ waitingForWake = true;
1619
+ wakeContext = ctx;
1620
+ return;
1621
+ }
1622
+ waitingForWake = false;
1596
1623
  handledSettlementKey = key;
1597
1624
  scheduleContinuation(ctx, loaded);
1598
1625
  });
@@ -1604,6 +1631,9 @@ export default function loopExtension(pi: ExtensionAPI): void {
1604
1631
  clearCommandInterruption();
1605
1632
  pendingFailure = undefined;
1606
1633
  currentSessionManagerRef = ctx.sessionManager;
1634
+ pendingWakes.clear();
1635
+ waitingForWake = false;
1636
+ wakeContext = ctx;
1607
1637
  handledSettlementKey = undefined;
1608
1638
  const loaded = latestStateFromContext(ctx);
1609
1639
  const owned = loaded && stateBelongsToContext(loaded, ctx) ? loaded : undefined;
@@ -1633,6 +1663,9 @@ export default function loopExtension(pi: ExtensionAPI): void {
1633
1663
  reportHerdrBlocked(undefined);
1634
1664
  clearWidget(ctx);
1635
1665
  currentSessionManagerRef = undefined;
1666
+ waitingForWake = false;
1667
+ wakeContext = undefined;
1668
+ pendingWakes.clear();
1636
1669
  });
1637
1670
 
1638
1671
  pi.registerCommand("loop", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brettinternet/pi-loop",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Run a prompt repeatedly in fresh Pi sessions",
5
5
  "type": "module",
6
6
  "license": "MIT",