@trevonistrevon/pi-loop 0.4.11 → 0.5.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.
Files changed (87) hide show
  1. package/README.md +20 -9
  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/coordinator.d.ts +35 -0
  7. package/dist/coordinator.js +56 -0
  8. package/dist/goal-coordinator.d.ts +22 -0
  9. package/dist/goal-coordinator.js +28 -0
  10. package/dist/goal-reducer.d.ts +89 -0
  11. package/dist/goal-reducer.js +181 -0
  12. package/dist/goal-store.d.ts +31 -0
  13. package/dist/goal-store.js +298 -0
  14. package/dist/goal-types.d.ts +82 -0
  15. package/dist/goal-types.js +1 -0
  16. package/dist/goal-verifier.d.ts +20 -0
  17. package/dist/goal-verifier.js +198 -0
  18. package/dist/index.js +130 -1087
  19. package/dist/loop-reducer.d.ts +63 -0
  20. package/dist/loop-reducer.js +67 -0
  21. package/dist/monitor-completion-coordinator.d.ts +10 -0
  22. package/dist/monitor-completion-coordinator.js +13 -0
  23. package/dist/monitor-manager.d.ts +2 -0
  24. package/dist/monitor-manager.js +107 -29
  25. package/dist/monitor-reducer.d.ts +82 -0
  26. package/dist/monitor-reducer.js +69 -0
  27. package/dist/notification-reducer.d.ts +81 -0
  28. package/dist/notification-reducer.js +65 -0
  29. package/dist/runtime/monitor-ondone-runtime.d.ts +13 -0
  30. package/dist/runtime/monitor-ondone-runtime.js +49 -0
  31. package/dist/runtime/notification-runtime.d.ts +34 -0
  32. package/dist/runtime/notification-runtime.js +152 -0
  33. package/dist/runtime/scope.d.ts +8 -0
  34. package/dist/runtime/scope.js +33 -0
  35. package/dist/runtime/session-runtime.d.ts +39 -0
  36. package/dist/runtime/session-runtime.js +110 -0
  37. package/dist/runtime/task-backlog-runtime.d.ts +36 -0
  38. package/dist/runtime/task-backlog-runtime.js +105 -0
  39. package/dist/runtime/task-rpc.d.ts +19 -0
  40. package/dist/runtime/task-rpc.js +118 -0
  41. package/dist/store.d.ts +7 -4
  42. package/dist/store.js +129 -49
  43. package/dist/task-backlog-coordinator.d.ts +12 -0
  44. package/dist/task-backlog-coordinator.js +22 -0
  45. package/dist/task-reducer.d.ts +66 -0
  46. package/dist/task-reducer.js +76 -0
  47. package/dist/task-store.d.ts +8 -4
  48. package/dist/task-store.js +102 -33
  49. package/dist/tools/loop-tools.d.ts +41 -0
  50. package/dist/tools/loop-tools.js +241 -0
  51. package/dist/tools/monitor-tools.d.ts +25 -0
  52. package/dist/tools/monitor-tools.js +110 -0
  53. package/dist/tools/native-task-tools.d.ts +15 -0
  54. package/dist/tools/native-task-tools.js +127 -0
  55. package/docs/architecture/goal-state-schema.md +505 -0
  56. package/docs/architecture/state-machine-migration.md +546 -0
  57. package/docs/architecture/state-machine-reducer-event-model.md +823 -0
  58. package/docs/architecture/state-machine-test-matrix.md +249 -0
  59. package/docs/architecture/state-machine-transition-map.md +436 -0
  60. package/package.json +1 -1
  61. package/src/commands/loop-command.ts +184 -0
  62. package/src/commands/tasks-command.ts +135 -0
  63. package/src/coordinator.ts +115 -0
  64. package/src/goal-coordinator.ts +58 -0
  65. package/src/goal-reducer.ts +280 -0
  66. package/src/goal-store.ts +315 -0
  67. package/src/goal-types.ts +104 -0
  68. package/src/goal-verifier.ts +241 -0
  69. package/src/index.ts +134 -1147
  70. package/src/loop-reducer.ts +148 -0
  71. package/src/monitor-completion-coordinator.ts +24 -0
  72. package/src/monitor-manager.ts +115 -27
  73. package/src/monitor-reducer.ts +166 -0
  74. package/src/notification-reducer.ts +155 -0
  75. package/src/runtime/monitor-ondone-runtime.ts +75 -0
  76. package/src/runtime/notification-runtime.ts +212 -0
  77. package/src/runtime/scope.ts +37 -0
  78. package/src/runtime/session-runtime.ts +168 -0
  79. package/src/runtime/task-backlog-runtime.ts +163 -0
  80. package/src/runtime/task-rpc.ts +150 -0
  81. package/src/store.ts +132 -50
  82. package/src/task-backlog-coordinator.ts +32 -0
  83. package/src/task-reducer.ts +152 -0
  84. package/src/task-store.ts +103 -31
  85. package/src/tools/loop-tools.ts +304 -0
  86. package/src/tools/monitor-tools.ts +145 -0
  87. package/src/tools/native-task-tools.ts +144 -0
@@ -0,0 +1,144 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { Type } from "typebox";
3
+ import { TaskStore } from "../task-store.js";
4
+
5
+ export interface TaskBacklogResult {
6
+ created: boolean;
7
+ entry?: { id: string };
8
+ }
9
+
10
+ export interface NativeTaskToolsOptions {
11
+ pi: ExtensionAPI;
12
+ taskStore: TaskStore;
13
+ evaluateTaskBacklog: (taskStore: TaskStore, pendingCount: number) => Promise<TaskBacklogResult>;
14
+ updateWidget: () => void;
15
+ }
16
+
17
+ function textResult(msg: string) {
18
+ return { content: [{ type: "text" as const, text: msg }], details: undefined as any };
19
+ }
20
+
21
+ export function registerNativeTaskTools(options: NativeTaskToolsOptions): void {
22
+ const { pi, taskStore, evaluateTaskBacklog, updateWidget } = options;
23
+
24
+ pi.registerTool({
25
+ name: "TaskCreate",
26
+ label: "TaskCreate",
27
+ description: `Create a task for tracking work across turns. Use when you need to track progress on complex multi-step tasks.
28
+
29
+ Fields:
30
+ - subject: brief actionable title
31
+ - description: detailed requirements
32
+ - metadata: optional tags/metadata`,
33
+ promptGuidelines: [
34
+ "Use TaskCreate to track complex multi-step work across turns.",
35
+ "Break work into small, independently completable tasks. A task should be finishable in one focused session — if a task would take multiple turns, split it further.",
36
+ "TaskCreate accepts `subject` and `description` parameters only — do not invent extra fields unless the schema explicitly adds them.",
37
+ ],
38
+ parameters: Type.Object({
39
+ subject: Type.String({ description: "Brief actionable title for the task" }),
40
+ description: Type.String({ description: "Detailed description of what needs to be done" }),
41
+ }),
42
+ async execute(_toolCallId, params) {
43
+ const entry = taskStore.create(params.subject, params.description);
44
+ pi.events.emit("tasks:created", {
45
+ taskId: entry.id,
46
+ subject: entry.subject,
47
+ description: entry.description,
48
+ status: entry.status,
49
+ });
50
+ const backlog = await evaluateTaskBacklog(taskStore, taskStore.pendingCount());
51
+ updateWidget();
52
+
53
+ const autoLoopMsg = backlog.created && backlog.entry
54
+ ? `\nWorker loop #${backlog.entry.id} auto-created`
55
+ : "";
56
+ return Promise.resolve(textResult(`Task #${entry.id} created: ${entry.subject}${autoLoopMsg}`));
57
+ },
58
+ });
59
+
60
+ pi.registerTool({
61
+ name: "TaskList",
62
+ label: "TaskList",
63
+ description: "List all tasks with status. Use to check progress and find available work.",
64
+ parameters: Type.Object({}),
65
+ execute() {
66
+ const tasks = taskStore.list();
67
+ if (tasks.length === 0) return Promise.resolve(textResult("No tasks."));
68
+
69
+ const lines: string[] = [];
70
+ const statuses: Record<"pending" | "in_progress" | "completed", number> = {
71
+ pending: 0,
72
+ in_progress: 0,
73
+ completed: 0,
74
+ };
75
+ for (const t of tasks) {
76
+ statuses[t.status]++;
77
+ const icon = t.status === "completed" ? "ok" : t.status === "in_progress" ? ">" : "*";
78
+ lines.push(`${icon} #${t.id} [${t.status}] ${t.subject.slice(0, 80)}`);
79
+ }
80
+ lines.unshift(`${tasks.length} tasks (${statuses.pending} pending, ${statuses.in_progress} in progress, ${statuses.completed} done)`);
81
+ return Promise.resolve(textResult(lines.join("\n")));
82
+ },
83
+ });
84
+
85
+ pi.registerTool({
86
+ name: "TaskUpdate",
87
+ label: "TaskUpdate",
88
+ description: `Update task status or details. Set status to "in_progress" before starting work, "completed" when done.
89
+
90
+ Statuses: pending → in_progress → completed
91
+ Parameters: id (required), status, subject, description`,
92
+ promptGuidelines: [
93
+ "TaskUpdate uses parameter `id`, not `taskId`.",
94
+ "Accepted parameters: `id` (required), `status`, `subject`, `description`.",
95
+ "When validation fails with 'must have required properties id', you passed `taskId` instead of `id`. Correct silently and retry.",
96
+ ],
97
+ parameters: Type.Object({
98
+ id: Type.String({ description: "Task ID to update" }),
99
+ status: Type.Optional(Type.String({ description: "New status", enum: ["pending", "in_progress", "completed"] })),
100
+ subject: Type.Optional(Type.String({ description: "New title" })),
101
+ description: Type.Optional(Type.String({ description: "New description" })),
102
+ }),
103
+ async execute(_toolCallId, params) {
104
+ const { id, status, subject, description } = params;
105
+ let entry = taskStore.get(id);
106
+ if (!entry) return Promise.resolve(textResult(`Task #${id} not found`));
107
+
108
+ if (status === "in_progress") entry = taskStore.start(id);
109
+ else if (status === "completed") entry = taskStore.complete(id);
110
+ else if (status === "pending") entry = taskStore.reopen(id);
111
+
112
+ if (!entry) return Promise.resolve(textResult(`Task #${id} not found`));
113
+ if (subject !== undefined || description !== undefined) {
114
+ entry = taskStore.updateDetails(id, { subject, description });
115
+ }
116
+ if (!entry) return Promise.resolve(textResult(`Task #${id} not found`));
117
+ updateWidget();
118
+ const backlog = await evaluateTaskBacklog(taskStore, taskStore.pendingCount());
119
+ const statusMsg = status ? ` → ${status}` : "";
120
+ const autoLoopMsg = backlog.created && backlog.entry
121
+ ? `\nWorker loop #${backlog.entry.id} auto-created`
122
+ : "";
123
+ return Promise.resolve(textResult(`Task #${id} updated${statusMsg}${autoLoopMsg}`));
124
+ },
125
+ });
126
+
127
+ pi.registerTool({
128
+ name: "TaskDelete",
129
+ label: "TaskDelete",
130
+ description: "Delete a task by ID. Use for cleaning up completed or irrelevant tasks.",
131
+ parameters: Type.Object({
132
+ id: Type.String({ description: "Task ID to delete" }),
133
+ }),
134
+ async execute(_toolCallId, params) {
135
+ const deleted = taskStore.delete(params.id);
136
+ updateWidget();
137
+ if (deleted) {
138
+ await evaluateTaskBacklog(taskStore, taskStore.pendingCount());
139
+ return Promise.resolve(textResult(`Task #${params.id} deleted`));
140
+ }
141
+ return Promise.resolve(textResult(`Task #${params.id} not found`));
142
+ },
143
+ });
144
+ }