@trevonistrevon/pi-loop 0.1.8 → 0.2.0
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/CONTRIBUTING.md +1 -1
- package/dist/index.js +8 -17
- package/package.json +1 -1
- package/src/index.ts +8 -20
package/CONTRIBUTING.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -30,8 +30,6 @@ 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
34
|
Scheduled loop "%propmpt%" fired. Trigger: %trigger_info%.
|
|
37
35
|
[loop:%loop_id%]
|
|
@@ -183,9 +181,7 @@ export default function (pi) {
|
|
|
183
181
|
showPersistedLoops(isResume);
|
|
184
182
|
});
|
|
185
183
|
// ── System-reminder injection for loop fires ──
|
|
186
|
-
let
|
|
187
|
-
let lastLoopToolUseTurn = 0;
|
|
188
|
-
let reminderInjectedThisCycle = false;
|
|
184
|
+
let canInjectReminder = true;
|
|
189
185
|
const pendingReminders = [];
|
|
190
186
|
pi.on("loop:fire", (data) => {
|
|
191
187
|
const triggerInfo = typeof data.trigger === "string"
|
|
@@ -201,22 +197,15 @@ export default function (pi) {
|
|
|
201
197
|
.replace("%loop_id%", data.loopId || "unknown");
|
|
202
198
|
pendingReminders.push(reminder);
|
|
203
199
|
});
|
|
200
|
+
// Allow one reminder injection per agent turn (at the first tool call),
|
|
201
|
+
// so the reminder arrives between batches of work, not mid-execution.
|
|
204
202
|
pi.on("turn_start", async () => {
|
|
205
|
-
|
|
203
|
+
canInjectReminder = true;
|
|
206
204
|
});
|
|
207
205
|
pi.on("tool_result", async (event) => {
|
|
208
|
-
if (
|
|
209
|
-
lastLoopToolUseTurn = currentTurn;
|
|
210
|
-
reminderInjectedThisCycle = false;
|
|
206
|
+
if (!canInjectReminder || pendingReminders.length === 0)
|
|
211
207
|
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;
|
|
208
|
+
canInjectReminder = false;
|
|
220
209
|
const reminder = pendingReminders.shift();
|
|
221
210
|
return {
|
|
222
211
|
content: [...event.content, { type: "text", text: reminder }],
|
|
@@ -237,6 +226,7 @@ Use this tool whenever the user asks to:
|
|
|
237
226
|
- "watch for..." or "when X happens, do Y"
|
|
238
227
|
- "schedule a recurring check"
|
|
239
228
|
- set up a periodic monitor or poller
|
|
229
|
+
- has a task list with open items — create a loop to work through tasks automatically
|
|
240
230
|
|
|
241
231
|
DO NOT use raw Bash loops (for/sleep/while). Use LoopCreate instead — it integrates with the session lifecycle, survives across turns, and the scheduler handles timing.
|
|
242
232
|
|
|
@@ -261,6 +251,7 @@ Skip this tool when the task is a one-off check (just do it directly) or when th
|
|
|
261
251
|
"When the user asks for a loop, repeating task, periodic check, or scheduled reminder, use LoopCreate — not raw Bash for/sleep/while.",
|
|
262
252
|
"Use LoopCreate for any 'every X seconds/minutes/hours' requests.",
|
|
263
253
|
"After creating a loop, tell the user the loop ID so they can cancel it with LoopDelete.",
|
|
254
|
+
"After creating tasks with TaskCreate, create a loop to keep working through them: LoopCreate trigger='30s' prompt='Run TaskList, pick the next available pending task, work on it. Continue until all tasks are completed.' This ensures the agent keeps making progress across turns instead of stopping after one task.",
|
|
264
255
|
],
|
|
265
256
|
parameters: Type.Object({
|
|
266
257
|
trigger: Type.String({ description: "Cron expression (e.g., '5m', '1h', '0 9 * * 1-5'), event source (e.g., 'tool_execution_start'), or JSON hybrid spec" }),
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -35,9 +35,6 @@ 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
39
|
Scheduled loop "%propmpt%" fired. Trigger: %trigger_info%.
|
|
43
40
|
[loop:%loop_id%]
|
|
@@ -197,9 +194,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
197
194
|
|
|
198
195
|
// ── System-reminder injection for loop fires ──
|
|
199
196
|
|
|
200
|
-
let
|
|
201
|
-
let lastLoopToolUseTurn = 0;
|
|
202
|
-
let reminderInjectedThisCycle = false;
|
|
197
|
+
let canInjectReminder = true;
|
|
203
198
|
const pendingReminders: string[] = [];
|
|
204
199
|
|
|
205
200
|
pi.on("loop:fire" as any, (data: any) => {
|
|
@@ -219,25 +214,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
219
214
|
pendingReminders.push(reminder);
|
|
220
215
|
});
|
|
221
216
|
|
|
217
|
+
// Allow one reminder injection per agent turn (at the first tool call),
|
|
218
|
+
// so the reminder arrives between batches of work, not mid-execution.
|
|
222
219
|
pi.on("turn_start", async () => {
|
|
223
|
-
|
|
220
|
+
canInjectReminder = true;
|
|
224
221
|
});
|
|
225
222
|
|
|
226
223
|
pi.on("tool_result", async (event) => {
|
|
227
|
-
if (
|
|
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 {};
|
|
224
|
+
if (!canInjectReminder || pendingReminders.length === 0) return {};
|
|
238
225
|
|
|
239
|
-
|
|
240
|
-
lastLoopToolUseTurn = currentTurn;
|
|
226
|
+
canInjectReminder = false;
|
|
241
227
|
const reminder = pendingReminders.shift()!;
|
|
242
228
|
return {
|
|
243
229
|
content: [...event.content, { type: "text" as const, text: reminder }],
|
|
@@ -260,6 +246,7 @@ Use this tool whenever the user asks to:
|
|
|
260
246
|
- "watch for..." or "when X happens, do Y"
|
|
261
247
|
- "schedule a recurring check"
|
|
262
248
|
- set up a periodic monitor or poller
|
|
249
|
+
- has a task list with open items — create a loop to work through tasks automatically
|
|
263
250
|
|
|
264
251
|
DO NOT use raw Bash loops (for/sleep/while). Use LoopCreate instead — it integrates with the session lifecycle, survives across turns, and the scheduler handles timing.
|
|
265
252
|
|
|
@@ -284,6 +271,7 @@ Skip this tool when the task is a one-off check (just do it directly) or when th
|
|
|
284
271
|
"When the user asks for a loop, repeating task, periodic check, or scheduled reminder, use LoopCreate — not raw Bash for/sleep/while.",
|
|
285
272
|
"Use LoopCreate for any 'every X seconds/minutes/hours' requests.",
|
|
286
273
|
"After creating a loop, tell the user the loop ID so they can cancel it with LoopDelete.",
|
|
274
|
+
"After creating tasks with TaskCreate, create a loop to keep working through them: LoopCreate trigger='30s' prompt='Run TaskList, pick the next available pending task, work on it. Continue until all tasks are completed.' This ensures the agent keeps making progress across turns instead of stopping after one task.",
|
|
287
275
|
],
|
|
288
276
|
parameters: Type.Object({
|
|
289
277
|
trigger: Type.String({ description: "Cron expression (e.g., '5m', '1h', '0 9 * * 1-5'), event source (e.g., 'tool_execution_start'), or JSON hybrid spec" }),
|