@trevonistrevon/pi-loop 0.5.0 → 0.5.2

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 (81) hide show
  1. package/README.md +11 -10
  2. package/dist/commands/loop-command.d.ts +22 -0
  3. package/dist/commands/loop-command.js +148 -0
  4. package/dist/commands/tasks-command.d.ts +15 -0
  5. package/dist/commands/tasks-command.js +117 -0
  6. package/dist/goal-coordinator.d.ts +22 -0
  7. package/dist/goal-coordinator.js +28 -0
  8. package/dist/goal-reducer.d.ts +89 -0
  9. package/dist/goal-reducer.js +181 -0
  10. package/dist/goal-store.d.ts +33 -0
  11. package/dist/goal-store.js +310 -0
  12. package/dist/goal-types.d.ts +4 -0
  13. package/dist/index.js +125 -1212
  14. package/dist/runtime/monitor-ondone-runtime.d.ts +13 -0
  15. package/dist/runtime/monitor-ondone-runtime.js +49 -0
  16. package/dist/runtime/notification-runtime.d.ts +34 -0
  17. package/dist/runtime/notification-runtime.js +152 -0
  18. package/dist/runtime/scope.d.ts +8 -0
  19. package/dist/runtime/scope.js +33 -0
  20. package/dist/runtime/session-runtime.d.ts +39 -0
  21. package/dist/runtime/session-runtime.js +110 -0
  22. package/dist/runtime/task-backlog-runtime.d.ts +36 -0
  23. package/dist/runtime/task-backlog-runtime.js +105 -0
  24. package/dist/runtime/task-rpc.d.ts +19 -0
  25. package/dist/runtime/task-rpc.js +118 -0
  26. package/dist/store.d.ts +7 -4
  27. package/dist/store.js +66 -48
  28. package/dist/task-store.d.ts +8 -4
  29. package/dist/task-store.js +80 -51
  30. package/dist/tools/loop-tools.d.ts +41 -0
  31. package/dist/tools/loop-tools.js +241 -0
  32. package/dist/tools/monitor-tools.d.ts +25 -0
  33. package/dist/tools/monitor-tools.js +110 -0
  34. package/dist/tools/native-task-tools.d.ts +15 -0
  35. package/dist/tools/native-task-tools.js +130 -0
  36. package/dist/ui/widget.d.ts +0 -1
  37. package/dist/ui/widget.js +1 -14
  38. package/package.json +1 -1
  39. package/src/commands/loop-command.ts +184 -0
  40. package/src/commands/tasks-command.ts +135 -0
  41. package/src/goal-coordinator.ts +58 -0
  42. package/src/goal-reducer.ts +280 -0
  43. package/src/goal-store.ts +326 -0
  44. package/src/goal-types.ts +5 -0
  45. package/src/index.ts +129 -1299
  46. package/src/runtime/monitor-ondone-runtime.ts +75 -0
  47. package/src/runtime/notification-runtime.ts +212 -0
  48. package/src/runtime/scope.ts +37 -0
  49. package/src/runtime/session-runtime.ts +168 -0
  50. package/src/runtime/task-backlog-runtime.ts +163 -0
  51. package/src/runtime/task-rpc.ts +150 -0
  52. package/src/store.ts +66 -49
  53. package/src/task-store.ts +76 -50
  54. package/src/tools/loop-tools.ts +304 -0
  55. package/src/tools/monitor-tools.ts +145 -0
  56. package/src/tools/native-task-tools.ts +147 -0
  57. package/src/ui/widget.ts +1 -16
  58. package/tmp/perf-cap-data/loops.json +455 -0
  59. package/tmp/perf-cap-data/tasks.json +1 -0
  60. package/tmp/perf-data/loops-0.json +1 -0
  61. package/tmp/perf-data/loops-10.json +1 -0
  62. package/tmp/perf-data/loops-100.json +1 -0
  63. package/tmp/perf-data/loops-1000.json +1 -0
  64. package/tmp/perf-data/loops-500.json +1 -0
  65. package/tmp/perf-data/tasks-0.json +1 -0
  66. package/tmp/perf-data/tasks-10.json +1 -0
  67. package/tmp/perf-data/tasks-100.json +1 -0
  68. package/tmp/perf-data/tasks-1000.json +1 -0
  69. package/tmp/perf-data/tasks-500.json +1 -0
  70. package/tmp/perf-data-session/loops-0.json +4 -0
  71. package/tmp/perf-data-session/loops-100.json +1805 -0
  72. package/tmp/perf-data-session/loops-1000.json +18005 -0
  73. package/tmp/perf-data-session/loops-25.json +455 -0
  74. package/tmp/perf-data-session/loops-500.json +9005 -0
  75. package/tmp/perf-data-session/tasks-0.json +1 -0
  76. package/tmp/perf-data-session/tasks-100.json +1 -0
  77. package/tmp/perf-data-session/tasks-1000.json +1 -0
  78. package/tmp/perf-data-session/tasks-25.json +1 -0
  79. package/tmp/perf-data-session/tasks-500.json +1 -0
  80. package/tmp/perf-session-init.js +98 -0
  81. package/tmp/perf-startup-audit.js +80 -0
@@ -0,0 +1,118 @@
1
+ import { randomUUID } from "node:crypto";
2
+ export function createTaskRuntimeBridge(options) {
3
+ const { pi, isTasksAvailable, setTasksAvailable, getNativeTaskStore, onNativeTaskCreated, onNativeTasksPruned, debug, } = options;
4
+ function checkTasksVersion() {
5
+ const requestId = randomUUID();
6
+ const timer = setTimeout(() => {
7
+ unsub();
8
+ }, 5000);
9
+ const unsub = pi.events.on(`tasks:rpc:ping:reply:${requestId}`, (raw) => {
10
+ unsub();
11
+ clearTimeout(timer);
12
+ const remoteVersion = raw?.data?.version;
13
+ if (remoteVersion !== undefined)
14
+ setTasksAvailable(true);
15
+ });
16
+ pi.events.emit("tasks:rpc:ping", { requestId });
17
+ }
18
+ async function autoCreateTask(entry) {
19
+ if (!entry.autoTask)
20
+ return undefined;
21
+ if (isTasksAvailable()) {
22
+ try {
23
+ const requestId = randomUUID();
24
+ const taskId = await new Promise((resolve) => {
25
+ const timer = setTimeout(() => {
26
+ unsub();
27
+ resolve(undefined);
28
+ }, 5000);
29
+ const unsub = pi.events.on(`tasks:rpc:create:reply:${requestId}`, (raw) => {
30
+ unsub();
31
+ clearTimeout(timer);
32
+ const reply = raw;
33
+ if (reply.success && reply.data)
34
+ resolve(reply.data.id);
35
+ else
36
+ resolve(undefined);
37
+ });
38
+ pi.events.emit("tasks:rpc:create", {
39
+ requestId,
40
+ subject: entry.prompt.slice(0, 80),
41
+ description: `Auto-created from loop #${entry.id}`,
42
+ metadata: { loopId: entry.id, trigger: entry.trigger },
43
+ });
44
+ });
45
+ return taskId;
46
+ }
47
+ catch {
48
+ return undefined;
49
+ }
50
+ }
51
+ const nativeTaskStore = getNativeTaskStore();
52
+ if (!nativeTaskStore)
53
+ return undefined;
54
+ const task = nativeTaskStore.create(entry.prompt.slice(0, 80), `Auto-created from loop #${entry.id}`, { loopId: entry.id, trigger: entry.trigger });
55
+ onNativeTaskCreated?.(nativeTaskStore);
56
+ return task.id;
57
+ }
58
+ async function hasPendingTasks() {
59
+ if (isTasksAvailable()) {
60
+ try {
61
+ const requestId = randomUUID();
62
+ const count = await new Promise((resolve) => {
63
+ const timer = setTimeout(() => {
64
+ unsub();
65
+ resolve(-1);
66
+ }, 3000);
67
+ const unsub = pi.events.on(`tasks:rpc:pending:reply:${requestId}`, (raw) => {
68
+ unsub();
69
+ clearTimeout(timer);
70
+ const reply = raw;
71
+ resolve(reply.success && reply.data ? reply.data.pending : -1);
72
+ });
73
+ pi.events.emit("tasks:rpc:pending", { requestId });
74
+ });
75
+ return count;
76
+ }
77
+ catch {
78
+ return -1;
79
+ }
80
+ }
81
+ return getNativeTaskStore()?.pendingCount() ?? -1;
82
+ }
83
+ async function cleanDoneTasks() {
84
+ if (isTasksAvailable()) {
85
+ try {
86
+ const requestId = randomUUID();
87
+ await new Promise((resolve) => {
88
+ const timer = setTimeout(() => {
89
+ unsub();
90
+ resolve();
91
+ }, 3000);
92
+ const unsub = pi.events.on(`tasks:rpc:clean:reply:${requestId}`, () => {
93
+ unsub();
94
+ clearTimeout(timer);
95
+ debug?.("tasks:rpc:clean — done tasks swept");
96
+ resolve();
97
+ });
98
+ pi.events.emit("tasks:rpc:clean", { requestId });
99
+ });
100
+ }
101
+ catch {
102
+ // timeout or error, ignore
103
+ }
104
+ return;
105
+ }
106
+ const nativeTaskStore = getNativeTaskStore();
107
+ if (!nativeTaskStore)
108
+ return;
109
+ nativeTaskStore.pruneCompleted();
110
+ await onNativeTasksPruned?.(nativeTaskStore);
111
+ }
112
+ return {
113
+ checkTasksVersion,
114
+ autoCreateTask,
115
+ hasPendingTasks,
116
+ cleanDoneTasks,
117
+ };
118
+ }
package/dist/store.d.ts CHANGED
@@ -1,10 +1,12 @@
1
- import type { LoopEntry, LoopStatus, Trigger } from "./types.js";
1
+ import type { LoopEntry, Trigger } from "./types.js";
2
2
  export declare class LoopStore {
3
3
  private filePath;
4
4
  private lockPath;
5
+ private lastLoadedSignature;
5
6
  private nextId;
6
7
  private loops;
7
8
  constructor(listIdOrPath?: string);
9
+ private getFileSignature;
8
10
  private load;
9
11
  private save;
10
12
  private withLock;
@@ -19,11 +21,12 @@ export declare class LoopStore {
19
21
  }): LoopEntry;
20
22
  get(id: string): LoopEntry | undefined;
21
23
  list(): LoopEntry[];
22
- update(id: string, fields: {
23
- status?: LoopStatus;
24
+ pause(id: string): LoopEntry | undefined;
25
+ resume(id: string): LoopEntry | undefined;
26
+ fire(id: string): LoopEntry | undefined;
27
+ updateMetadata(id: string, fields: {
24
28
  trigger?: Trigger;
25
29
  prompt?: string;
26
- fireCount?: number;
27
30
  }): {
28
31
  entry: LoopEntry | undefined;
29
32
  changedFields: string[];
package/dist/store.js CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, isAbsolute, join } from "node:path";
4
4
  import { reduceLoopState } from "./loop-reducer.js";
@@ -52,6 +52,7 @@ function isProcessRunning(pid) {
52
52
  export class LoopStore {
53
53
  filePath;
54
54
  lockPath;
55
+ lastLoadedSignature;
55
56
  nextId = 1;
56
57
  loops = new Map();
57
58
  constructor(listIdOrPath) {
@@ -64,10 +65,19 @@ export class LoopStore {
64
65
  this.lockPath = filePath + ".lock";
65
66
  this.load();
66
67
  }
67
- load() {
68
+ getFileSignature() {
69
+ if (!this.filePath || !existsSync(this.filePath))
70
+ return undefined;
71
+ const stat = statSync(this.filePath);
72
+ return `${stat.mtimeMs}:${stat.size}`;
73
+ }
74
+ load(force = false) {
68
75
  if (!this.filePath)
69
76
  return;
70
- if (!existsSync(this.filePath))
77
+ const signature = this.getFileSignature();
78
+ if (!signature)
79
+ return;
80
+ if (!force && signature === this.lastLoadedSignature)
71
81
  return;
72
82
  try {
73
83
  const data = JSON.parse(readFileSync(this.filePath, "utf-8"));
@@ -76,6 +86,7 @@ export class LoopStore {
76
86
  for (const loop of data.loops) {
77
87
  this.loops.set(loop.id, loop);
78
88
  }
89
+ this.lastLoadedSignature = signature;
79
90
  }
80
91
  catch { /* corrupt file — start fresh */ }
81
92
  }
@@ -89,13 +100,14 @@ export class LoopStore {
89
100
  const tmpPath = this.filePath + ".tmp";
90
101
  writeFileSync(tmpPath, JSON.stringify(data, null, 2));
91
102
  renameSync(tmpPath, this.filePath);
103
+ this.lastLoadedSignature = this.getFileSignature();
92
104
  }
93
105
  withLock(fn) {
94
106
  if (!this.lockPath)
95
107
  return fn();
96
108
  acquireLock(this.lockPath);
97
109
  try {
98
- this.load();
110
+ this.load(true);
99
111
  const result = fn();
100
112
  this.save();
101
113
  return result;
@@ -149,38 +161,61 @@ export class LoopStore {
149
161
  this.load();
150
162
  return Array.from(this.loops.values()).sort((a, b) => Number(a.id) - Number(b.id));
151
163
  }
152
- update(id, fields) {
164
+ pause(id) {
165
+ return this.withLock(() => {
166
+ const entry = this.loops.get(id);
167
+ if (!entry)
168
+ return undefined;
169
+ this.applyReducerEvent({
170
+ type: "LOOP_PAUSED",
171
+ at: Date.now(),
172
+ source: "tool",
173
+ entityType: "loop",
174
+ entityId: id,
175
+ payload: { id },
176
+ });
177
+ return this.loops.get(id);
178
+ });
179
+ }
180
+ resume(id) {
153
181
  return this.withLock(() => {
154
182
  const entry = this.loops.get(id);
155
183
  if (!entry)
184
+ return undefined;
185
+ this.applyReducerEvent({
186
+ type: "LOOP_RESUMED",
187
+ at: Date.now(),
188
+ source: "tool",
189
+ entityType: "loop",
190
+ entityId: id,
191
+ payload: { id },
192
+ });
193
+ return this.loops.get(id);
194
+ });
195
+ }
196
+ fire(id) {
197
+ return this.withLock(() => {
198
+ const entry = this.loops.get(id);
199
+ if (!entry)
200
+ return undefined;
201
+ this.applyReducerEvent({
202
+ type: "LOOP_FIRED",
203
+ at: Date.now(),
204
+ source: "system",
205
+ entityType: "loop",
206
+ entityId: id,
207
+ payload: { id },
208
+ });
209
+ return this.loops.get(id);
210
+ });
211
+ }
212
+ updateMetadata(id, fields) {
213
+ return this.withLock(() => {
214
+ const current = this.loops.get(id);
215
+ if (!current)
156
216
  return { entry: undefined, changedFields: [] };
157
217
  const changedFields = [];
158
218
  const now = Date.now();
159
- if (fields.status === "paused") {
160
- this.applyReducerEvent({
161
- type: "LOOP_PAUSED",
162
- at: now,
163
- source: "tool",
164
- entityType: "loop",
165
- entityId: id,
166
- payload: { id },
167
- });
168
- changedFields.push("status");
169
- }
170
- else if (fields.status === "active") {
171
- this.applyReducerEvent({
172
- type: "LOOP_RESUMED",
173
- at: now,
174
- source: "tool",
175
- entityType: "loop",
176
- entityId: id,
177
- payload: { id },
178
- });
179
- changedFields.push("status");
180
- }
181
- const current = this.loops.get(id);
182
- if (!current)
183
- return { entry: undefined, changedFields };
184
219
  if (fields.trigger !== undefined) {
185
220
  current.trigger = fields.trigger;
186
221
  changedFields.push("trigger");
@@ -189,24 +224,7 @@ export class LoopStore {
189
224
  current.prompt = fields.prompt;
190
225
  changedFields.push("prompt");
191
226
  }
192
- if (fields.fireCount !== undefined) {
193
- if (fields.fireCount === (current.fireCount ?? 0) + 1) {
194
- this.applyReducerEvent({
195
- type: "LOOP_FIRED",
196
- at: now,
197
- source: "system",
198
- entityType: "loop",
199
- entityId: id,
200
- payload: { id },
201
- });
202
- }
203
- else {
204
- current.fireCount = fields.fireCount;
205
- current.updatedAt = now;
206
- }
207
- changedFields.push("fireCount");
208
- }
209
- if (fields.trigger !== undefined || fields.prompt !== undefined) {
227
+ if (changedFields.length > 0) {
210
228
  current.updatedAt = now;
211
229
  }
212
230
  return { entry: this.loops.get(id), changedFields };
@@ -1,10 +1,12 @@
1
- import type { TaskEntry, TaskStatus } from "./task-types.js";
1
+ import type { TaskEntry } from "./task-types.js";
2
2
  export declare class TaskStore {
3
3
  private filePath;
4
4
  private lockPath;
5
+ private lastLoadedSignature;
5
6
  private nextId;
6
7
  private tasks;
7
8
  constructor(listIdOrPath?: string);
9
+ private getFileSignature;
8
10
  private load;
9
11
  private save;
10
12
  private withLock;
@@ -13,12 +15,14 @@ export declare class TaskStore {
13
15
  create(subject: string, description: string, metadata?: Record<string, unknown>): TaskEntry;
14
16
  get(id: string): TaskEntry | undefined;
15
17
  list(): TaskEntry[];
16
- update(id: string, fields: {
17
- status?: TaskStatus;
18
+ start(id: string): TaskEntry | undefined;
19
+ complete(id: string): TaskEntry | undefined;
20
+ reopen(id: string): TaskEntry | undefined;
21
+ updateDetails(id: string, fields: {
18
22
  subject?: string;
19
23
  description?: string;
20
24
  }): TaskEntry | undefined;
21
25
  delete(id: string): boolean;
22
26
  pendingCount(): number;
23
- sweepCompleted(): number;
27
+ pruneCompleted(): number;
24
28
  }
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, isAbsolute, join } from "node:path";
4
4
  import { reduceTaskState } from "./task-reducer.js";
@@ -52,6 +52,7 @@ function isProcessRunning(pid) {
52
52
  export class TaskStore {
53
53
  filePath;
54
54
  lockPath;
55
+ lastLoadedSignature;
55
56
  nextId = 1;
56
57
  tasks = new Map();
57
58
  constructor(listIdOrPath) {
@@ -64,10 +65,19 @@ export class TaskStore {
64
65
  this.lockPath = filePath + ".lock";
65
66
  this.load();
66
67
  }
67
- load() {
68
+ getFileSignature() {
69
+ if (!this.filePath || !existsSync(this.filePath))
70
+ return undefined;
71
+ const stat = statSync(this.filePath);
72
+ return `${stat.mtimeMs}:${stat.size}`;
73
+ }
74
+ load(force = false) {
68
75
  if (!this.filePath)
69
76
  return;
70
- if (!existsSync(this.filePath))
77
+ const signature = this.getFileSignature();
78
+ if (!signature)
79
+ return;
80
+ if (!force && signature === this.lastLoadedSignature)
71
81
  return;
72
82
  try {
73
83
  const data = JSON.parse(readFileSync(this.filePath, "utf-8"));
@@ -76,6 +86,7 @@ export class TaskStore {
76
86
  for (const task of data.tasks) {
77
87
  this.tasks.set(task.id, task);
78
88
  }
89
+ this.lastLoadedSignature = signature;
79
90
  }
80
91
  catch { /* corrupt file — start fresh */ }
81
92
  }
@@ -89,13 +100,14 @@ export class TaskStore {
89
100
  const tmpPath = this.filePath + ".tmp";
90
101
  writeFileSync(tmpPath, JSON.stringify(data, null, 2));
91
102
  renameSync(tmpPath, this.filePath);
103
+ this.lastLoadedSignature = this.getFileSignature();
92
104
  }
93
105
  withLock(fn) {
94
106
  if (!this.lockPath)
95
107
  return fn();
96
108
  acquireLock(this.lockPath);
97
109
  try {
98
- this.load();
110
+ this.load(true);
99
111
  const result = fn();
100
112
  this.save();
101
113
  return result;
@@ -141,56 +153,73 @@ export class TaskStore {
141
153
  this.load();
142
154
  return Array.from(this.tasks.values()).sort((a, b) => Number(a.id) - Number(b.id));
143
155
  }
144
- update(id, fields) {
156
+ start(id) {
145
157
  return this.withLock(() => {
146
158
  const entry = this.tasks.get(id);
147
159
  if (!entry)
148
160
  return undefined;
149
- const now = Date.now();
150
- if (fields.status === "in_progress") {
151
- this.applyReducerEvent({
152
- type: "TASK_STARTED",
153
- at: now,
154
- source: "tool",
155
- entityType: "task",
156
- entityId: id,
157
- payload: { id },
158
- });
159
- }
160
- else if (fields.status === "completed") {
161
- this.applyReducerEvent({
162
- type: "TASK_COMPLETED",
163
- at: now,
164
- source: "tool",
165
- entityType: "task",
166
- entityId: id,
167
- payload: { id },
168
- });
169
- }
170
- else if (fields.status === "pending") {
171
- this.applyReducerEvent({
172
- type: "TASK_REOPENED",
173
- at: now,
174
- source: "tool",
175
- entityType: "task",
176
- entityId: id,
177
- payload: { id },
178
- });
179
- }
180
- if (fields.subject !== undefined || fields.description !== undefined) {
181
- this.applyReducerEvent({
182
- type: "TASK_UPDATED",
183
- at: now,
184
- source: "tool",
185
- entityType: "task",
186
- entityId: id,
187
- payload: {
188
- id,
189
- subject: fields.subject,
190
- description: fields.description,
191
- },
192
- });
193
- }
161
+ this.applyReducerEvent({
162
+ type: "TASK_STARTED",
163
+ at: Date.now(),
164
+ source: "tool",
165
+ entityType: "task",
166
+ entityId: id,
167
+ payload: { id },
168
+ });
169
+ return this.tasks.get(id);
170
+ });
171
+ }
172
+ complete(id) {
173
+ return this.withLock(() => {
174
+ const entry = this.tasks.get(id);
175
+ if (!entry)
176
+ return undefined;
177
+ this.applyReducerEvent({
178
+ type: "TASK_COMPLETED",
179
+ at: Date.now(),
180
+ source: "tool",
181
+ entityType: "task",
182
+ entityId: id,
183
+ payload: { id },
184
+ });
185
+ return this.tasks.get(id);
186
+ });
187
+ }
188
+ reopen(id) {
189
+ return this.withLock(() => {
190
+ const entry = this.tasks.get(id);
191
+ if (!entry)
192
+ return undefined;
193
+ this.applyReducerEvent({
194
+ type: "TASK_REOPENED",
195
+ at: Date.now(),
196
+ source: "tool",
197
+ entityType: "task",
198
+ entityId: id,
199
+ payload: { id },
200
+ });
201
+ return this.tasks.get(id);
202
+ });
203
+ }
204
+ updateDetails(id, fields) {
205
+ return this.withLock(() => {
206
+ const entry = this.tasks.get(id);
207
+ if (!entry)
208
+ return undefined;
209
+ if (fields.subject === undefined && fields.description === undefined)
210
+ return entry;
211
+ this.applyReducerEvent({
212
+ type: "TASK_UPDATED",
213
+ at: Date.now(),
214
+ source: "tool",
215
+ entityType: "task",
216
+ entityId: id,
217
+ payload: {
218
+ id,
219
+ subject: fields.subject,
220
+ description: fields.description,
221
+ },
222
+ });
194
223
  return this.tasks.get(id);
195
224
  });
196
225
  }
@@ -217,7 +246,7 @@ export class TaskStore {
217
246
  }
218
247
  return count;
219
248
  }
220
- sweepCompleted() {
249
+ pruneCompleted() {
221
250
  return this.withLock(() => {
222
251
  const before = this.tasks.size;
223
252
  this.applyReducerEvent({
@@ -0,0 +1,41 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import type { LoopEntry, Trigger } from "../types.js";
3
+ interface LoopStoreLike {
4
+ list(): LoopEntry[];
5
+ get(id: string): LoopEntry | undefined;
6
+ create(trigger: Trigger, prompt: string, opts: {
7
+ recurring: boolean;
8
+ autoTask?: boolean;
9
+ taskBacklog?: boolean;
10
+ readOnly?: boolean;
11
+ maxFires?: number;
12
+ }): LoopEntry;
13
+ pause(id: string): LoopEntry | undefined;
14
+ delete(id: string): boolean;
15
+ }
16
+ interface TriggerSystemLike {
17
+ add(entry: LoopEntry): void;
18
+ remove(id: string): void;
19
+ }
20
+ interface SchedulerLike {
21
+ nextFire(id: string): number | undefined;
22
+ }
23
+ interface MonitorLike {
24
+ id: string;
25
+ status: string;
26
+ }
27
+ interface MonitorManagerLike {
28
+ get(id: string): MonitorLike | undefined;
29
+ }
30
+ export interface LoopToolsOptions {
31
+ pi: ExtensionAPI;
32
+ getStore: () => LoopStoreLike;
33
+ getTriggerSystem: () => TriggerSystemLike;
34
+ getScheduler: () => SchedulerLike;
35
+ getMonitorManager: () => MonitorManagerLike;
36
+ updateWidget: () => void;
37
+ maybeBootstrapTaskLoop: (entry: LoopEntry) => Promise<boolean>;
38
+ isTaskSystemReady: () => boolean;
39
+ }
40
+ export declare function registerLoopTools(options: LoopToolsOptions): void;
41
+ export {};