@trevonistrevon/pi-loop 0.2.6 → 0.2.8

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/dist/index.js CHANGED
@@ -121,6 +121,7 @@ export default function (pi) {
121
121
  trigger: entry.trigger,
122
122
  timestamp: Date.now(),
123
123
  readOnly: entry.readOnly,
124
+ recurring: entry.recurring,
124
125
  });
125
126
  }
126
127
  // ── Session lifecycle ──
@@ -179,9 +180,12 @@ export default function (pi) {
179
180
  showPersistedLoops(isResume);
180
181
  });
181
182
  // ── Loop fire handler — sends a user message to re-wake the agent ──
182
- const pendingFollowUps = new Set();
183
183
  pi.events.on("loop:fire", (event) => {
184
184
  const data = event;
185
+ if (data.recurring && _latestCtx?.hasPendingMessages()) {
186
+ debug(`loop:fire #${data.loopId} — agent has pending messages, skipping recurring fire`);
187
+ return;
188
+ }
185
189
  const triggerInfo = typeof data.trigger === "string"
186
190
  ? data.trigger
187
191
  : data.trigger?.type === "cron"
@@ -190,22 +194,14 @@ export default function (pi) {
190
194
  ? `event: ${data.trigger.source}`
191
195
  : `hybrid`;
192
196
  const loopId = data.loopId || "?";
193
- if (pendingFollowUps.has(loopId)) {
194
- debug(`loop:fire #${loopId} — follow-up already queued, skipping`);
195
- return;
196
- }
197
- pendingFollowUps.add(loopId);
198
197
  const prompt = data.prompt || "loop fired";
199
198
  const constraint = data.readOnly ? "\n\nREAD-ONLY MODE — use only read tools (Read, TaskList, LoopList, MonitorList, LoopCreate, etc.). No file writes, shell execution, or destructive changes." : "";
200
199
  const message = [
201
200
  `[pi-loop] Loop #${loopId} fired (${triggerInfo}).${constraint}`,
202
201
  prompt,
203
202
  ].join("\n");
204
- // deliverAs: "followUp" queues the message when the agent is busy;
205
- // it delivers after the current turn finishes.
206
203
  pi.sendUserMessage(message, { deliverAs: "followUp" });
207
204
  });
208
- pi.on("turn_end", () => { pendingFollowUps.clear(); });
209
205
  // ──────────────────────────────────────────────────
210
206
  // Tool 1: LoopCreate
211
207
  // ──────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trevonistrevon/pi-loop",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "A pi extension for cron/event-based agent re-wake loops and background process monitoring.",
5
5
  "author": "trevonistrevon",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -41,6 +41,7 @@ interface LoopFireEvent {
41
41
  trigger: Trigger | string;
42
42
  timestamp: number;
43
43
  readOnly?: boolean;
44
+ recurring?: boolean;
44
45
  }
45
46
 
46
47
  interface SessionSwitchEvent {
@@ -136,6 +137,7 @@ export default function (pi: ExtensionAPI) {
136
137
  trigger: entry.trigger,
137
138
  timestamp: Date.now(),
138
139
  readOnly: entry.readOnly,
140
+ recurring: entry.recurring,
139
141
  });
140
142
  }
141
143
 
@@ -204,10 +206,14 @@ export default function (pi: ExtensionAPI) {
204
206
 
205
207
  // ── Loop fire handler — sends a user message to re-wake the agent ──
206
208
 
207
- const pendingFollowUps = new Set<string>();
208
-
209
209
  pi.events.on("loop:fire", (event: unknown) => {
210
210
  const data = event as LoopFireEvent;
211
+
212
+ if (data.recurring && _latestCtx?.hasPendingMessages()) {
213
+ debug(`loop:fire #${data.loopId} — agent has pending messages, skipping recurring fire`);
214
+ return;
215
+ }
216
+
211
217
  const triggerInfo = typeof data.trigger === "string"
212
218
  ? data.trigger
213
219
  : data.trigger?.type === "cron"
@@ -217,12 +223,6 @@ export default function (pi: ExtensionAPI) {
217
223
  : `hybrid`;
218
224
 
219
225
  const loopId = data.loopId || "?";
220
- if (pendingFollowUps.has(loopId)) {
221
- debug(`loop:fire #${loopId} — follow-up already queued, skipping`);
222
- return;
223
- }
224
- pendingFollowUps.add(loopId);
225
-
226
226
  const prompt = data.prompt || "loop fired";
227
227
  const constraint = data.readOnly ? "\n\nREAD-ONLY MODE — use only read tools (Read, TaskList, LoopList, MonitorList, LoopCreate, etc.). No file writes, shell execution, or destructive changes." : "";
228
228
  const message = [
@@ -230,13 +230,9 @@ export default function (pi: ExtensionAPI) {
230
230
  prompt,
231
231
  ].join("\n");
232
232
 
233
- // deliverAs: "followUp" queues the message when the agent is busy;
234
- // it delivers after the current turn finishes.
235
233
  pi.sendUserMessage(message, { deliverAs: "followUp" });
236
234
  });
237
235
 
238
- pi.on("turn_end", () => { pendingFollowUps.clear(); });
239
-
240
236
  // ──────────────────────────────────────────────────
241
237
  // Tool 1: LoopCreate
242
238
  // ──────────────────────────────────────────────────