@trevonistrevon/pi-loop 0.1.9 → 0.2.1

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
@@ -30,10 +30,8 @@ function debug(...args) {
30
30
  function textResult(msg) {
31
31
  return { content: [{ type: "text", text: msg }], details: undefined };
32
32
  }
33
- const LOOP_TOOL_NAMES = new Set(["LoopCreate", "LoopList", "LoopDelete", "MonitorCreate", "MonitorList", "MonitorStop"]);
34
- const REMINDER_INTERVAL = 3;
35
33
  const SYSTEM_REMINDER_TEMPLATE = `<system-reminder>
36
- Scheduled loop "%propmpt%" fired. Trigger: %trigger_info%.
34
+ Scheduled loop "%prompt%" fired. Trigger: %trigger_info%.
37
35
  [loop:%loop_id%]
38
36
  </system-reminder>`;
39
37
  export default function (pi) {
@@ -150,10 +148,11 @@ export default function (pi) {
150
148
  if (persistedShown)
151
149
  return;
152
150
  persistedShown = true;
151
+ const sessionStartedAt = Date.now();
153
152
  const loops = store.list();
154
153
  if (loops.length > 0) {
155
154
  store.clearExpired();
156
- store.expireEventLoops();
155
+ store.expireEventLoops(sessionStartedAt);
157
156
  triggerSystem.start();
158
157
  widget.update();
159
158
  }
@@ -183,11 +182,9 @@ export default function (pi) {
183
182
  showPersistedLoops(isResume);
184
183
  });
185
184
  // ── System-reminder injection for loop fires ──
186
- let currentTurn = 0;
187
- let lastLoopToolUseTurn = 0;
188
- let reminderInjectedThisCycle = false;
185
+ let canInjectReminder = true;
189
186
  const pendingReminders = [];
190
- pi.on("loop:fire", (data) => {
187
+ pi.events.on("loop:fire", (data) => {
191
188
  const triggerInfo = typeof data.trigger === "string"
192
189
  ? data.trigger
193
190
  : data.trigger?.type === "cron"
@@ -201,22 +198,15 @@ export default function (pi) {
201
198
  .replace("%loop_id%", data.loopId || "unknown");
202
199
  pendingReminders.push(reminder);
203
200
  });
201
+ // Allow one reminder injection per agent turn (at the first tool call),
202
+ // so the reminder arrives between batches of work, not mid-execution.
204
203
  pi.on("turn_start", async () => {
205
- currentTurn++;
204
+ canInjectReminder = true;
206
205
  });
207
206
  pi.on("tool_result", async (event) => {
208
- if (LOOP_TOOL_NAMES.has(event.toolName)) {
209
- lastLoopToolUseTurn = currentTurn;
210
- reminderInjectedThisCycle = false;
207
+ if (!canInjectReminder || pendingReminders.length === 0)
211
208
  return {};
212
- }
213
- if (currentTurn - lastLoopToolUseTurn < REMINDER_INTERVAL || reminderInjectedThisCycle) {
214
- return {};
215
- }
216
- if (pendingReminders.length === 0)
217
- return {};
218
- reminderInjectedThisCycle = true;
219
- lastLoopToolUseTurn = currentTurn;
209
+ canInjectReminder = false;
220
210
  const reminder = pendingReminders.shift();
221
211
  return {
222
212
  content: [...event.content, { type: "text", text: reminder }],
package/dist/store.d.ts CHANGED
@@ -25,7 +25,7 @@ export declare class LoopStore {
25
25
  };
26
26
  delete(id: string): boolean;
27
27
  clearExpired(): number;
28
- expireEventLoops(): number;
28
+ expireEventLoops(sessionStartedAt: number): number;
29
29
  clearAll(): number;
30
30
  deleteFileIfEmpty(): boolean;
31
31
  }
package/dist/store.js CHANGED
@@ -176,15 +176,17 @@ export class LoopStore {
176
176
  return count;
177
177
  });
178
178
  }
179
- expireEventLoops() {
179
+ expireEventLoops(sessionStartedAt) {
180
180
  return this.withLock(() => {
181
181
  let count = 0;
182
182
  for (const [_id, entry] of this.loops) {
183
183
  if (entry.status !== "active")
184
184
  continue;
185
185
  if (entry.trigger.type === "event" || entry.trigger.type === "hybrid") {
186
- entry.status = "expired";
187
- count++;
186
+ if (entry.createdAt < sessionStartedAt) {
187
+ entry.status = "expired";
188
+ count++;
189
+ }
188
190
  }
189
191
  }
190
192
  return count;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trevonistrevon/pi-loop",
3
- "version": "0.1.9",
3
+ "version": "0.2.1",
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
@@ -35,11 +35,8 @@ function textResult(msg: string) {
35
35
  return { content: [{ type: "text" as const, text: msg }], details: undefined as any };
36
36
  }
37
37
 
38
- const LOOP_TOOL_NAMES = new Set(["LoopCreate", "LoopList", "LoopDelete", "MonitorCreate", "MonitorList", "MonitorStop"]);
39
- const REMINDER_INTERVAL = 3;
40
-
41
38
  const SYSTEM_REMINDER_TEMPLATE = `<system-reminder>
42
- Scheduled loop "%propmpt%" fired. Trigger: %trigger_info%.
39
+ Scheduled loop "%prompt%" fired. Trigger: %trigger_info%.
43
40
  [loop:%loop_id%]
44
41
  </system-reminder>`;
45
42
 
@@ -156,10 +153,11 @@ export default function (pi: ExtensionAPI) {
156
153
  function showPersistedLoops(_isResume = false) {
157
154
  if (persistedShown) return;
158
155
  persistedShown = true;
156
+ const sessionStartedAt = Date.now();
159
157
  const loops = store.list();
160
158
  if (loops.length > 0) {
161
159
  store.clearExpired();
162
- store.expireEventLoops();
160
+ store.expireEventLoops(sessionStartedAt);
163
161
  triggerSystem.start();
164
162
  widget.update();
165
163
  }
@@ -197,12 +195,10 @@ export default function (pi: ExtensionAPI) {
197
195
 
198
196
  // ── System-reminder injection for loop fires ──
199
197
 
200
- let currentTurn = 0;
201
- let lastLoopToolUseTurn = 0;
202
- let reminderInjectedThisCycle = false;
198
+ let canInjectReminder = true;
203
199
  const pendingReminders: string[] = [];
204
200
 
205
- pi.on("loop:fire" as any, (data: any) => {
201
+ pi.events.on("loop:fire", (data: any) => {
206
202
  const triggerInfo = typeof data.trigger === "string"
207
203
  ? data.trigger
208
204
  : data.trigger?.type === "cron"
@@ -219,25 +215,16 @@ export default function (pi: ExtensionAPI) {
219
215
  pendingReminders.push(reminder);
220
216
  });
221
217
 
218
+ // Allow one reminder injection per agent turn (at the first tool call),
219
+ // so the reminder arrives between batches of work, not mid-execution.
222
220
  pi.on("turn_start", async () => {
223
- currentTurn++;
221
+ canInjectReminder = true;
224
222
  });
225
223
 
226
224
  pi.on("tool_result", async (event) => {
227
- if (LOOP_TOOL_NAMES.has(event.toolName)) {
228
- lastLoopToolUseTurn = currentTurn;
229
- reminderInjectedThisCycle = false;
230
- return {};
231
- }
232
-
233
- if (currentTurn - lastLoopToolUseTurn < REMINDER_INTERVAL || reminderInjectedThisCycle) {
234
- return {};
235
- }
236
-
237
- if (pendingReminders.length === 0) return {};
225
+ if (!canInjectReminder || pendingReminders.length === 0) return {};
238
226
 
239
- reminderInjectedThisCycle = true;
240
- lastLoopToolUseTurn = currentTurn;
227
+ canInjectReminder = false;
241
228
  const reminder = pendingReminders.shift()!;
242
229
  return {
243
230
  content: [...event.content, { type: "text" as const, text: reminder }],
package/src/store.ts CHANGED
@@ -173,14 +173,16 @@ export class LoopStore {
173
173
  });
174
174
  }
175
175
 
176
- expireEventLoops(): number {
176
+ expireEventLoops(sessionStartedAt: number): number {
177
177
  return this.withLock(() => {
178
178
  let count = 0;
179
179
  for (const [_id, entry] of this.loops) {
180
180
  if (entry.status !== "active") continue;
181
181
  if (entry.trigger.type === "event" || entry.trigger.type === "hybrid") {
182
- entry.status = "expired";
183
- count++;
182
+ if (entry.createdAt < sessionStartedAt) {
183
+ entry.status = "expired";
184
+ count++;
185
+ }
184
186
  }
185
187
  }
186
188
  return count;