@trevonistrevon/pi-loop 0.5.5 → 0.5.7

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 (70) hide show
  1. package/coverage/coverage-final.json +24 -24
  2. package/coverage/index.html +28 -28
  3. package/coverage/src/commands/index.html +1 -1
  4. package/coverage/src/commands/loop-command.ts.html +3 -3
  5. package/coverage/src/commands/tasks-command.ts.html +3 -3
  6. package/coverage/src/coordinator.ts.html +20 -20
  7. package/coverage/src/goal-coordinator.ts.html +1 -1
  8. package/coverage/src/goal-reducer.ts.html +1 -1
  9. package/coverage/src/goal-store.ts.html +1 -1
  10. package/coverage/src/goal-verifier.ts.html +1 -1
  11. package/coverage/src/index.html +25 -25
  12. package/coverage/src/index.ts.html +88 -79
  13. package/coverage/src/loop-parse.ts.html +44 -44
  14. package/coverage/src/loop-reducer.ts.html +22 -22
  15. package/coverage/src/monitor-completion-coordinator.ts.html +1 -1
  16. package/coverage/src/monitor-manager.ts.html +161 -92
  17. package/coverage/src/monitor-reducer.ts.html +31 -31
  18. package/coverage/src/notification-reducer.ts.html +15 -15
  19. package/coverage/src/reducer-backed-store.ts.html +52 -52
  20. package/coverage/src/runtime/index.html +18 -18
  21. package/coverage/src/runtime/monitor-ondone-runtime.ts.html +5 -5
  22. package/coverage/src/runtime/notification-runtime.ts.html +42 -42
  23. package/coverage/src/runtime/scope.ts.html +17 -17
  24. package/coverage/src/runtime/session-runtime.ts.html +133 -49
  25. package/coverage/src/runtime/task-backlog-runtime.ts.html +7 -7
  26. package/coverage/src/runtime/task-rpc.ts.html +10 -10
  27. package/coverage/src/scheduler.ts.html +29 -29
  28. package/coverage/src/store.ts.html +17 -17
  29. package/coverage/src/task-backlog-coordinator.ts.html +1 -1
  30. package/coverage/src/task-reducer.ts.html +1 -1
  31. package/coverage/src/task-store.ts.html +5 -5
  32. package/coverage/src/tools/index.html +1 -1
  33. package/coverage/src/tools/loop-tools.ts.html +24 -24
  34. package/coverage/src/tools/monitor-tools.ts.html +5 -5
  35. package/coverage/src/tools/native-task-tools.ts.html +6 -6
  36. package/coverage/src/trigger-system.ts.html +11 -11
  37. package/coverage/src/ui/index.html +1 -1
  38. package/coverage/src/ui/widget.ts.html +24 -24
  39. package/dist/index.js +3 -0
  40. package/dist/monitor-manager.d.ts +9 -0
  41. package/dist/monitor-manager.js +33 -12
  42. package/dist/runtime/session-runtime.js +27 -0
  43. package/package.json +1 -1
  44. package/src/index.ts +3 -0
  45. package/src/monitor-manager.ts +35 -12
  46. package/src/runtime/session-runtime.ts +28 -0
  47. package/tmp/perf-cap-data/loops.json +0 -455
  48. package/tmp/perf-cap-data/tasks.json +0 -1
  49. package/tmp/perf-data/loops-0.json +0 -1
  50. package/tmp/perf-data/loops-10.json +0 -1
  51. package/tmp/perf-data/loops-100.json +0 -1
  52. package/tmp/perf-data/loops-1000.json +0 -1
  53. package/tmp/perf-data/loops-500.json +0 -1
  54. package/tmp/perf-data/tasks-0.json +0 -1
  55. package/tmp/perf-data/tasks-10.json +0 -1
  56. package/tmp/perf-data/tasks-100.json +0 -1
  57. package/tmp/perf-data/tasks-1000.json +0 -1
  58. package/tmp/perf-data/tasks-500.json +0 -1
  59. package/tmp/perf-data-session/loops-0.json +0 -4
  60. package/tmp/perf-data-session/loops-100.json +0 -1805
  61. package/tmp/perf-data-session/loops-1000.json +0 -18005
  62. package/tmp/perf-data-session/loops-25.json +0 -455
  63. package/tmp/perf-data-session/loops-500.json +0 -9005
  64. package/tmp/perf-data-session/tasks-0.json +0 -1
  65. package/tmp/perf-data-session/tasks-100.json +0 -1
  66. package/tmp/perf-data-session/tasks-1000.json +0 -1
  67. package/tmp/perf-data-session/tasks-25.json +0 -1
  68. package/tmp/perf-data-session/tasks-500.json +0 -1
  69. package/tmp/perf-session-init.js +0 -98
  70. package/tmp/perf-startup-audit.js +0 -80
@@ -1,7 +1,29 @@
1
+ // Wall-clock cadence for the idle heartbeat that pumps the scheduler. Cron is
2
+ // minute-granular, so 30s gives sub-minute wake latency while idle.
3
+ const HEARTBEAT_MS = 30_000;
1
4
  export function registerSessionRuntimeHooks(options) {
2
5
  const { pi, getLoopScope, getPiLoopEnv, recreateSessionStore, clearAllLoops, getStore, getScheduler, getTriggerSystem, setLatestCtx, setSessionId, widget, notificationRuntime, flushPendingNotifications, cleanupTaskBacklogLoops, hasPendingTasks, cleanDoneTasks, } = options;
3
6
  let storeUpgraded = false;
4
7
  let persistedShown = false;
8
+ let heartbeatTimer;
9
+ // The CronScheduler is pump-driven; without this heartbeat it only advances at
10
+ // turn boundaries (turn_start/agent_end), so a loop whose fire time elapses
11
+ // while the agent is idle would never fire and never re-wake the agent. The
12
+ // timer is unref'd so it never keeps a one-shot (`pi -p`) process alive.
13
+ function ensureHeartbeat() {
14
+ if (heartbeatTimer)
15
+ return;
16
+ heartbeatTimer = setInterval(() => {
17
+ void pumpLoops();
18
+ }, HEARTBEAT_MS);
19
+ heartbeatTimer.unref?.();
20
+ }
21
+ function stopHeartbeat() {
22
+ if (!heartbeatTimer)
23
+ return;
24
+ clearInterval(heartbeatTimer);
25
+ heartbeatTimer = undefined;
26
+ }
5
27
  function upgradeStoreIfNeeded(ctx) {
6
28
  if (storeUpgraded)
7
29
  return;
@@ -20,6 +42,7 @@ export function registerSessionRuntimeHooks(options) {
20
42
  getStore().clearExpired();
21
43
  getStore().expireEventLoops(sessionStartedAt);
22
44
  getTriggerSystem().start();
45
+ ensureHeartbeat();
23
46
  widget.update();
24
47
  }
25
48
  }
@@ -46,6 +69,7 @@ export function registerSessionRuntimeHooks(options) {
46
69
  setSessionId(ctx.sessionManager.getSessionId());
47
70
  widget.setUICtx(ctx.ui);
48
71
  upgradeStoreIfNeeded(ctx);
72
+ ensureHeartbeat();
49
73
  widget.update();
50
74
  await pumpLoops();
51
75
  });
@@ -53,6 +77,7 @@ export function registerSessionRuntimeHooks(options) {
53
77
  setLatestCtx(ctx);
54
78
  widget.setUICtx(ctx.ui);
55
79
  upgradeStoreIfNeeded(ctx);
80
+ ensureHeartbeat();
56
81
  showPersistedLoops();
57
82
  widget.update();
58
83
  });
@@ -76,12 +101,14 @@ export function registerSessionRuntimeHooks(options) {
76
101
  await pumpLoops();
77
102
  });
78
103
  pi.on("session_shutdown", async () => {
104
+ stopHeartbeat();
79
105
  notificationRuntime.clear("session_shutdown");
80
106
  });
81
107
  pi.on("session_switch", async (event, ctx) => {
82
108
  setLatestCtx(ctx);
83
109
  widget.setUICtx(ctx.ui);
84
110
  getTriggerSystem().stop();
111
+ stopHeartbeat();
85
112
  notificationRuntime.clear("session_switch");
86
113
  setSessionId(undefined);
87
114
  const isResume = event?.reason === "resume";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trevonistrevon/pi-loop",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
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
@@ -55,6 +55,9 @@ export default function (pi: ExtensionAPI) {
55
55
  let scheduler: CronScheduler;
56
56
  let triggerSystem: TriggerSystem;
57
57
  const widget = new LoopWidget(store, monitorManager);
58
+ // Repaint the status bar when a monitor finishes/prunes on its own (no tool
59
+ // call), so stale monitors don't linger in the count between turns.
60
+ monitorManager.setOnChange(() => widget.update());
58
61
  widget.setTaskSummaryProvider(() => {
59
62
  if (!nativeTaskStore) return { count: 0 };
60
63
  const tasks = nativeTaskStore.list().filter(t => t.status === "pending" || t.status === "in_progress");
@@ -11,9 +11,20 @@ import type { MonitorEntry, MonitorProcess } from "./types.js";
11
11
  export class MonitorManager {
12
12
  private processes = new Map<string, MonitorProcess>();
13
13
  private nextId = 1;
14
+ private onChange: (() => void) | undefined;
14
15
 
15
16
  constructor(private pi: ExtensionAPI) {}
16
17
 
18
+ /**
19
+ * Register a callback fired when a monitor's status changes or it is pruned
20
+ * (e.g. autonomous completion/error/stop/prune with no tool call). Used to
21
+ * repaint the status widget, which otherwise only refreshes on turn
22
+ * boundaries and explicit tool actions. Not fired for output lines.
23
+ */
24
+ setOnChange(cb: () => void): void {
25
+ this.onChange = cb;
26
+ }
27
+
17
28
  private toReducerState(): MonitorReducerState {
18
29
  return {
19
30
  nextId: this.nextId,
@@ -34,9 +45,31 @@ export class MonitorManager {
34
45
  }
35
46
  process.entry = updated;
36
47
  }
48
+ // Repaint on status/set transitions, but not on the frequent output-line
49
+ // events (which never change the visible count).
50
+ if (event.type !== "MONITOR_OUTPUT" && event.type !== "MONITOR_ONDONE_REGISTERED") {
51
+ this.onChange?.();
52
+ }
37
53
  return result;
38
54
  }
39
55
 
56
+ // Remove a finished monitor (completed/errored/stopped) after a brief delay so
57
+ // tool consumers can still read its final state via MonitorList. Shared by
58
+ // finish() and stop() so every terminal status is pruned consistently — a
59
+ // stopped monitor that is never pruned lingers in list() and the widget count.
60
+ private schedulePrune(id: string): void {
61
+ setTimeout(() => {
62
+ this.applyReducerEvent({
63
+ type: "MONITOR_PRUNED",
64
+ at: Date.now(),
65
+ source: "system",
66
+ entityType: "monitor",
67
+ entityId: id,
68
+ payload: { id },
69
+ });
70
+ }, 30000);
71
+ }
72
+
40
73
  create(command: string, description?: string, timeout = 300000): MonitorEntry {
41
74
  const now = Date.now();
42
75
  const result = reduceMonitorState(this.toReducerState(), {
@@ -132,18 +165,7 @@ export class MonitorManager {
132
165
  bp.completionCallbacks = [];
133
166
  for (const resolve of bp.waiters) resolve();
134
167
  bp.waiters = [];
135
- // Remove completed/errored monitors after a brief delay so tool
136
- // consumers have time to read the final state via MonitorList.
137
- setTimeout(() => {
138
- this.applyReducerEvent({
139
- type: "MONITOR_PRUNED",
140
- at: Date.now(),
141
- source: "system",
142
- entityType: "monitor",
143
- entityId: id,
144
- payload: { id },
145
- });
146
- }, 30000);
168
+ this.schedulePrune(id);
147
169
  };
148
170
 
149
171
  child.on("close", (code) => {
@@ -214,6 +236,7 @@ export class MonitorManager {
214
236
  reason: "manual",
215
237
  },
216
238
  });
239
+ this.schedulePrune(id);
217
240
  bp.proc.kill("SIGTERM");
218
241
 
219
242
  await new Promise<void>((resolve) => {
@@ -7,6 +7,10 @@ export interface SessionSwitchEvent {
7
7
  reason?: string;
8
8
  }
9
9
 
10
+ // Wall-clock cadence for the idle heartbeat that pumps the scheduler. Cron is
11
+ // minute-granular, so 30s gives sub-minute wake latency while idle.
12
+ const HEARTBEAT_MS = 30_000;
13
+
10
14
  export interface SessionRuntimeOptions {
11
15
  pi: ExtensionAPI;
12
16
  getLoopScope: () => LoopScope;
@@ -48,6 +52,25 @@ export function registerSessionRuntimeHooks(options: SessionRuntimeOptions): voi
48
52
 
49
53
  let storeUpgraded = false;
50
54
  let persistedShown = false;
55
+ let heartbeatTimer: ReturnType<typeof setInterval> | undefined;
56
+
57
+ // The CronScheduler is pump-driven; without this heartbeat it only advances at
58
+ // turn boundaries (turn_start/agent_end), so a loop whose fire time elapses
59
+ // while the agent is idle would never fire and never re-wake the agent. The
60
+ // timer is unref'd so it never keeps a one-shot (`pi -p`) process alive.
61
+ function ensureHeartbeat(): void {
62
+ if (heartbeatTimer) return;
63
+ heartbeatTimer = setInterval(() => {
64
+ void pumpLoops();
65
+ }, HEARTBEAT_MS);
66
+ heartbeatTimer.unref?.();
67
+ }
68
+
69
+ function stopHeartbeat(): void {
70
+ if (!heartbeatTimer) return;
71
+ clearInterval(heartbeatTimer);
72
+ heartbeatTimer = undefined;
73
+ }
51
74
 
52
75
  function upgradeStoreIfNeeded(ctx: ExtensionContext) {
53
76
  if (storeUpgraded) return;
@@ -66,6 +89,7 @@ export function registerSessionRuntimeHooks(options: SessionRuntimeOptions): voi
66
89
  getStore().clearExpired();
67
90
  getStore().expireEventLoops(sessionStartedAt);
68
91
  getTriggerSystem().start();
92
+ ensureHeartbeat();
69
93
  widget.update();
70
94
  }
71
95
  }
@@ -89,6 +113,7 @@ export function registerSessionRuntimeHooks(options: SessionRuntimeOptions): voi
89
113
  setSessionId(ctx.sessionManager.getSessionId());
90
114
  widget.setUICtx(ctx.ui);
91
115
  upgradeStoreIfNeeded(ctx);
116
+ ensureHeartbeat();
92
117
  widget.update();
93
118
  await pumpLoops();
94
119
  });
@@ -97,6 +122,7 @@ export function registerSessionRuntimeHooks(options: SessionRuntimeOptions): voi
97
122
  setLatestCtx(ctx);
98
123
  widget.setUICtx(ctx.ui);
99
124
  upgradeStoreIfNeeded(ctx);
125
+ ensureHeartbeat();
100
126
  showPersistedLoops();
101
127
  widget.update();
102
128
  });
@@ -123,6 +149,7 @@ export function registerSessionRuntimeHooks(options: SessionRuntimeOptions): voi
123
149
  });
124
150
 
125
151
  pi.on("session_shutdown", async () => {
152
+ stopHeartbeat();
126
153
  notificationRuntime.clear("session_shutdown");
127
154
  });
128
155
 
@@ -130,6 +157,7 @@ export function registerSessionRuntimeHooks(options: SessionRuntimeOptions): voi
130
157
  setLatestCtx(ctx);
131
158
  widget.setUICtx(ctx.ui);
132
159
  getTriggerSystem().stop();
160
+ stopHeartbeat();
133
161
  notificationRuntime.clear("session_switch");
134
162
  setSessionId(undefined);
135
163
 
@@ -1,455 +0,0 @@
1
- {
2
- "nextId": 26,
3
- "loops": [
4
- {
5
- "id": "1",
6
- "prompt": "Loop 1",
7
- "trigger": {
8
- "type": "cron",
9
- "schedule": "*/5 * * * *"
10
- },
11
- "recurring": true,
12
- "autoTask": false,
13
- "taskBacklog": false,
14
- "readOnly": false,
15
- "maxFires": 30,
16
- "fireCount": 0,
17
- "status": "active",
18
- "createdAt": 1780943919086,
19
- "updatedAt": 1780943919086,
20
- "expiresAt": 1781548719086
21
- },
22
- {
23
- "id": "2",
24
- "prompt": "Loop 2",
25
- "trigger": {
26
- "type": "cron",
27
- "schedule": "*/5 * * * *"
28
- },
29
- "recurring": true,
30
- "autoTask": false,
31
- "taskBacklog": false,
32
- "readOnly": false,
33
- "maxFires": 30,
34
- "fireCount": 0,
35
- "status": "active",
36
- "createdAt": 1780943919086,
37
- "updatedAt": 1780943919086,
38
- "expiresAt": 1781548719086
39
- },
40
- {
41
- "id": "3",
42
- "prompt": "Loop 3",
43
- "trigger": {
44
- "type": "cron",
45
- "schedule": "*/5 * * * *"
46
- },
47
- "recurring": true,
48
- "autoTask": false,
49
- "taskBacklog": false,
50
- "readOnly": false,
51
- "maxFires": 30,
52
- "fireCount": 0,
53
- "status": "active",
54
- "createdAt": 1780943919086,
55
- "updatedAt": 1780943919086,
56
- "expiresAt": 1781548719086
57
- },
58
- {
59
- "id": "4",
60
- "prompt": "Loop 4",
61
- "trigger": {
62
- "type": "cron",
63
- "schedule": "*/5 * * * *"
64
- },
65
- "recurring": true,
66
- "autoTask": false,
67
- "taskBacklog": false,
68
- "readOnly": false,
69
- "maxFires": 30,
70
- "fireCount": 0,
71
- "status": "active",
72
- "createdAt": 1780943919086,
73
- "updatedAt": 1780943919086,
74
- "expiresAt": 1781548719086
75
- },
76
- {
77
- "id": "5",
78
- "prompt": "Loop 5",
79
- "trigger": {
80
- "type": "cron",
81
- "schedule": "*/5 * * * *"
82
- },
83
- "recurring": true,
84
- "autoTask": false,
85
- "taskBacklog": false,
86
- "readOnly": false,
87
- "maxFires": 30,
88
- "fireCount": 0,
89
- "status": "active",
90
- "createdAt": 1780943919086,
91
- "updatedAt": 1780943919086,
92
- "expiresAt": 1781548719086
93
- },
94
- {
95
- "id": "6",
96
- "prompt": "Loop 6",
97
- "trigger": {
98
- "type": "cron",
99
- "schedule": "*/5 * * * *"
100
- },
101
- "recurring": true,
102
- "autoTask": false,
103
- "taskBacklog": false,
104
- "readOnly": false,
105
- "maxFires": 30,
106
- "fireCount": 0,
107
- "status": "active",
108
- "createdAt": 1780943919086,
109
- "updatedAt": 1780943919086,
110
- "expiresAt": 1781548719086
111
- },
112
- {
113
- "id": "7",
114
- "prompt": "Loop 7",
115
- "trigger": {
116
- "type": "cron",
117
- "schedule": "*/5 * * * *"
118
- },
119
- "recurring": true,
120
- "autoTask": false,
121
- "taskBacklog": false,
122
- "readOnly": false,
123
- "maxFires": 30,
124
- "fireCount": 0,
125
- "status": "active",
126
- "createdAt": 1780943919086,
127
- "updatedAt": 1780943919086,
128
- "expiresAt": 1781548719086
129
- },
130
- {
131
- "id": "8",
132
- "prompt": "Loop 8",
133
- "trigger": {
134
- "type": "cron",
135
- "schedule": "*/5 * * * *"
136
- },
137
- "recurring": true,
138
- "autoTask": false,
139
- "taskBacklog": false,
140
- "readOnly": false,
141
- "maxFires": 30,
142
- "fireCount": 0,
143
- "status": "active",
144
- "createdAt": 1780943919086,
145
- "updatedAt": 1780943919086,
146
- "expiresAt": 1781548719086
147
- },
148
- {
149
- "id": "9",
150
- "prompt": "Loop 9",
151
- "trigger": {
152
- "type": "cron",
153
- "schedule": "*/5 * * * *"
154
- },
155
- "recurring": true,
156
- "autoTask": false,
157
- "taskBacklog": false,
158
- "readOnly": false,
159
- "maxFires": 30,
160
- "fireCount": 0,
161
- "status": "active",
162
- "createdAt": 1780943919086,
163
- "updatedAt": 1780943919086,
164
- "expiresAt": 1781548719086
165
- },
166
- {
167
- "id": "10",
168
- "prompt": "Loop 10",
169
- "trigger": {
170
- "type": "cron",
171
- "schedule": "*/5 * * * *"
172
- },
173
- "recurring": true,
174
- "autoTask": false,
175
- "taskBacklog": false,
176
- "readOnly": false,
177
- "maxFires": 30,
178
- "fireCount": 0,
179
- "status": "active",
180
- "createdAt": 1780943919086,
181
- "updatedAt": 1780943919086,
182
- "expiresAt": 1781548719086
183
- },
184
- {
185
- "id": "11",
186
- "prompt": "Loop 11",
187
- "trigger": {
188
- "type": "cron",
189
- "schedule": "*/5 * * * *"
190
- },
191
- "recurring": true,
192
- "autoTask": false,
193
- "taskBacklog": false,
194
- "readOnly": false,
195
- "maxFires": 30,
196
- "fireCount": 0,
197
- "status": "active",
198
- "createdAt": 1780943919086,
199
- "updatedAt": 1780943919086,
200
- "expiresAt": 1781548719086
201
- },
202
- {
203
- "id": "12",
204
- "prompt": "Loop 12",
205
- "trigger": {
206
- "type": "cron",
207
- "schedule": "*/5 * * * *"
208
- },
209
- "recurring": true,
210
- "autoTask": false,
211
- "taskBacklog": false,
212
- "readOnly": false,
213
- "maxFires": 30,
214
- "fireCount": 0,
215
- "status": "active",
216
- "createdAt": 1780943919086,
217
- "updatedAt": 1780943919086,
218
- "expiresAt": 1781548719086
219
- },
220
- {
221
- "id": "13",
222
- "prompt": "Loop 13",
223
- "trigger": {
224
- "type": "cron",
225
- "schedule": "*/5 * * * *"
226
- },
227
- "recurring": true,
228
- "autoTask": false,
229
- "taskBacklog": false,
230
- "readOnly": false,
231
- "maxFires": 30,
232
- "fireCount": 0,
233
- "status": "active",
234
- "createdAt": 1780943919086,
235
- "updatedAt": 1780943919086,
236
- "expiresAt": 1781548719086
237
- },
238
- {
239
- "id": "14",
240
- "prompt": "Loop 14",
241
- "trigger": {
242
- "type": "cron",
243
- "schedule": "*/5 * * * *"
244
- },
245
- "recurring": true,
246
- "autoTask": false,
247
- "taskBacklog": false,
248
- "readOnly": false,
249
- "maxFires": 30,
250
- "fireCount": 0,
251
- "status": "active",
252
- "createdAt": 1780943919086,
253
- "updatedAt": 1780943919086,
254
- "expiresAt": 1781548719086
255
- },
256
- {
257
- "id": "15",
258
- "prompt": "Loop 15",
259
- "trigger": {
260
- "type": "cron",
261
- "schedule": "*/5 * * * *"
262
- },
263
- "recurring": true,
264
- "autoTask": false,
265
- "taskBacklog": false,
266
- "readOnly": false,
267
- "maxFires": 30,
268
- "fireCount": 0,
269
- "status": "active",
270
- "createdAt": 1780943919086,
271
- "updatedAt": 1780943919086,
272
- "expiresAt": 1781548719086
273
- },
274
- {
275
- "id": "16",
276
- "prompt": "Loop 16",
277
- "trigger": {
278
- "type": "cron",
279
- "schedule": "*/5 * * * *"
280
- },
281
- "recurring": true,
282
- "autoTask": false,
283
- "taskBacklog": false,
284
- "readOnly": false,
285
- "maxFires": 30,
286
- "fireCount": 0,
287
- "status": "active",
288
- "createdAt": 1780943919086,
289
- "updatedAt": 1780943919086,
290
- "expiresAt": 1781548719086
291
- },
292
- {
293
- "id": "17",
294
- "prompt": "Loop 17",
295
- "trigger": {
296
- "type": "cron",
297
- "schedule": "*/5 * * * *"
298
- },
299
- "recurring": true,
300
- "autoTask": false,
301
- "taskBacklog": false,
302
- "readOnly": false,
303
- "maxFires": 30,
304
- "fireCount": 0,
305
- "status": "active",
306
- "createdAt": 1780943919086,
307
- "updatedAt": 1780943919086,
308
- "expiresAt": 1781548719086
309
- },
310
- {
311
- "id": "18",
312
- "prompt": "Loop 18",
313
- "trigger": {
314
- "type": "cron",
315
- "schedule": "*/5 * * * *"
316
- },
317
- "recurring": true,
318
- "autoTask": false,
319
- "taskBacklog": false,
320
- "readOnly": false,
321
- "maxFires": 30,
322
- "fireCount": 0,
323
- "status": "active",
324
- "createdAt": 1780943919086,
325
- "updatedAt": 1780943919086,
326
- "expiresAt": 1781548719086
327
- },
328
- {
329
- "id": "19",
330
- "prompt": "Loop 19",
331
- "trigger": {
332
- "type": "cron",
333
- "schedule": "*/5 * * * *"
334
- },
335
- "recurring": true,
336
- "autoTask": false,
337
- "taskBacklog": false,
338
- "readOnly": false,
339
- "maxFires": 30,
340
- "fireCount": 0,
341
- "status": "active",
342
- "createdAt": 1780943919086,
343
- "updatedAt": 1780943919086,
344
- "expiresAt": 1781548719086
345
- },
346
- {
347
- "id": "20",
348
- "prompt": "Loop 20",
349
- "trigger": {
350
- "type": "cron",
351
- "schedule": "*/5 * * * *"
352
- },
353
- "recurring": true,
354
- "autoTask": false,
355
- "taskBacklog": false,
356
- "readOnly": false,
357
- "maxFires": 30,
358
- "fireCount": 0,
359
- "status": "active",
360
- "createdAt": 1780943919086,
361
- "updatedAt": 1780943919086,
362
- "expiresAt": 1781548719086
363
- },
364
- {
365
- "id": "21",
366
- "prompt": "Loop 21",
367
- "trigger": {
368
- "type": "cron",
369
- "schedule": "*/5 * * * *"
370
- },
371
- "recurring": true,
372
- "autoTask": false,
373
- "taskBacklog": false,
374
- "readOnly": false,
375
- "maxFires": 30,
376
- "fireCount": 0,
377
- "status": "active",
378
- "createdAt": 1780943919086,
379
- "updatedAt": 1780943919086,
380
- "expiresAt": 1781548719086
381
- },
382
- {
383
- "id": "22",
384
- "prompt": "Loop 22",
385
- "trigger": {
386
- "type": "cron",
387
- "schedule": "*/5 * * * *"
388
- },
389
- "recurring": true,
390
- "autoTask": false,
391
- "taskBacklog": false,
392
- "readOnly": false,
393
- "maxFires": 30,
394
- "fireCount": 0,
395
- "status": "active",
396
- "createdAt": 1780943919086,
397
- "updatedAt": 1780943919086,
398
- "expiresAt": 1781548719086
399
- },
400
- {
401
- "id": "23",
402
- "prompt": "Loop 23",
403
- "trigger": {
404
- "type": "cron",
405
- "schedule": "*/5 * * * *"
406
- },
407
- "recurring": true,
408
- "autoTask": false,
409
- "taskBacklog": false,
410
- "readOnly": false,
411
- "maxFires": 30,
412
- "fireCount": 0,
413
- "status": "active",
414
- "createdAt": 1780943919086,
415
- "updatedAt": 1780943919086,
416
- "expiresAt": 1781548719086
417
- },
418
- {
419
- "id": "24",
420
- "prompt": "Loop 24",
421
- "trigger": {
422
- "type": "cron",
423
- "schedule": "*/5 * * * *"
424
- },
425
- "recurring": true,
426
- "autoTask": false,
427
- "taskBacklog": false,
428
- "readOnly": false,
429
- "maxFires": 30,
430
- "fireCount": 0,
431
- "status": "active",
432
- "createdAt": 1780943919086,
433
- "updatedAt": 1780943919086,
434
- "expiresAt": 1781548719086
435
- },
436
- {
437
- "id": "25",
438
- "prompt": "Loop 25",
439
- "trigger": {
440
- "type": "cron",
441
- "schedule": "*/5 * * * *"
442
- },
443
- "recurring": true,
444
- "autoTask": false,
445
- "taskBacklog": false,
446
- "readOnly": false,
447
- "maxFires": 30,
448
- "fireCount": 0,
449
- "status": "active",
450
- "createdAt": 1780943919086,
451
- "updatedAt": 1780943919086,
452
- "expiresAt": 1781548719086
453
- }
454
- ]
455
- }