@trevonistrevon/pi-loop 0.5.9 → 0.6.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.
Files changed (121) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/AGENTS.md +52 -12
  3. package/CHANGELOG.md +59 -0
  4. package/CONTRIBUTING.md +13 -3
  5. package/README.md +47 -0
  6. package/coverage/coverage-final.json +35 -32
  7. package/coverage/index.html +64 -49
  8. package/coverage/src/api.ts.html +193 -0
  9. package/coverage/src/commands/index.html +32 -32
  10. package/coverage/src/commands/loop-command.ts.html +170 -170
  11. package/coverage/src/commands/tasks-command.ts.html +129 -129
  12. package/coverage/src/coordinator.ts.html +26 -26
  13. package/coverage/src/index.html +61 -106
  14. package/coverage/src/index.ts.html +224 -86
  15. package/coverage/src/loop-parse.ts.html +106 -73
  16. package/coverage/src/loop-reducer.ts.html +21 -21
  17. package/coverage/src/monitor-completion-coordinator.ts.html +1 -1
  18. package/coverage/src/monitor-manager.ts.html +95 -71
  19. package/coverage/src/monitor-reducer.ts.html +15 -15
  20. package/coverage/src/notification-reducer.ts.html +6 -6
  21. package/coverage/src/reducer-backed-store.ts.html +57 -57
  22. package/coverage/src/rpc/channels.ts.html +376 -0
  23. package/coverage/src/rpc/cross-extension-rpc.ts.html +541 -0
  24. package/coverage/src/rpc/index.html +131 -0
  25. package/coverage/src/runtime/index.html +68 -23
  26. package/coverage/src/{goal-coordinator.ts.html → runtime/loop-events.ts.html} +79 -73
  27. package/coverage/src/runtime/monitor-ondone-runtime.ts.html +5 -5
  28. package/coverage/src/runtime/native-task-rpc.ts.html +502 -0
  29. package/coverage/src/runtime/notification-runtime.ts.html +22 -22
  30. package/coverage/src/runtime/scope.ts.html +14 -14
  31. package/coverage/src/runtime/session-runtime.ts.html +32 -32
  32. package/coverage/src/runtime/task-backlog-runtime.ts.html +65 -23
  33. package/coverage/src/runtime/task-events.ts.html +2 -2
  34. package/coverage/src/runtime/task-mutations.ts.html +394 -0
  35. package/coverage/src/runtime/task-rpc.ts.html +102 -120
  36. package/coverage/src/scheduler.ts.html +29 -29
  37. package/coverage/src/store.ts.html +27 -27
  38. package/coverage/src/task-backlog-coordinator.ts.html +8 -8
  39. package/coverage/src/task-reducer.ts.html +42 -42
  40. package/coverage/src/task-store.ts.html +49 -49
  41. package/coverage/src/tools/index.html +39 -24
  42. package/coverage/src/tools/loop-tools.ts.html +12 -21
  43. package/coverage/src/tools/monitor-tools.ts.html +12 -21
  44. package/coverage/src/tools/native-task-tools.ts.html +57 -120
  45. package/coverage/src/tools/tool-result.ts.html +97 -0
  46. package/coverage/src/trigger-system.ts.html +12 -12
  47. package/coverage/src/ui/index.html +1 -1
  48. package/coverage/src/ui/widget.ts.html +23 -23
  49. package/dist/api.d.ts +11 -0
  50. package/dist/api.js +10 -0
  51. package/dist/commands/loop-command.js +2 -2
  52. package/dist/commands/tasks-command.js +3 -3
  53. package/dist/coordinator.d.ts +1 -1
  54. package/dist/index.js +48 -3
  55. package/dist/loop-parse.js +14 -5
  56. package/dist/reducer-backed-store.d.ts +1 -1
  57. package/dist/reducer-backed-store.js +1 -1
  58. package/dist/rpc/channels.d.ts +73 -0
  59. package/dist/rpc/channels.js +30 -0
  60. package/dist/rpc/cross-extension-rpc.d.ts +59 -0
  61. package/dist/rpc/cross-extension-rpc.js +117 -0
  62. package/dist/runtime/loop-events.d.ts +26 -0
  63. package/dist/runtime/loop-events.js +26 -0
  64. package/dist/runtime/native-task-rpc.d.ts +22 -0
  65. package/dist/runtime/native-task-rpc.js +50 -0
  66. package/dist/runtime/task-backlog-runtime.d.ts +3 -0
  67. package/dist/runtime/task-backlog-runtime.js +4 -1
  68. package/dist/runtime/task-mutations.d.ts +39 -0
  69. package/dist/runtime/task-mutations.js +68 -0
  70. package/dist/runtime/task-rpc.d.ts +4 -0
  71. package/dist/runtime/task-rpc.js +40 -58
  72. package/dist/scheduler.js +1 -1
  73. package/dist/tools/loop-tools.js +1 -3
  74. package/dist/tools/monitor-tools.js +1 -3
  75. package/dist/tools/native-task-tools.d.ts +2 -6
  76. package/dist/tools/native-task-tools.js +25 -55
  77. package/dist/tools/tool-result.d.ts +8 -0
  78. package/dist/tools/tool-result.js +4 -0
  79. package/docs/architecture/state-machine-migration.md +0 -44
  80. package/docs/architecture/state-machine-reducer-event-model.md +2 -3
  81. package/package.json +6 -1
  82. package/src/api.ts +36 -0
  83. package/src/commands/loop-command.ts +2 -2
  84. package/src/commands/tasks-command.ts +3 -3
  85. package/src/coordinator.ts +1 -1
  86. package/src/index.ts +49 -3
  87. package/src/loop-parse.ts +18 -7
  88. package/src/reducer-backed-store.ts +3 -3
  89. package/src/rpc/channels.ts +97 -0
  90. package/src/rpc/cross-extension-rpc.ts +152 -0
  91. package/src/runtime/loop-events.ts +60 -0
  92. package/src/runtime/native-task-rpc.ts +139 -0
  93. package/src/runtime/task-backlog-runtime.ts +14 -0
  94. package/src/runtime/task-mutations.ts +103 -0
  95. package/src/runtime/task-rpc.ts +48 -54
  96. package/src/scheduler.ts +1 -1
  97. package/src/tools/loop-tools.ts +1 -4
  98. package/src/tools/monitor-tools.ts +1 -4
  99. package/src/tools/native-task-tools.ts +31 -52
  100. package/src/tools/tool-result.ts +4 -0
  101. package/src/trigger-system.ts +4 -4
  102. package/vitest.config.ts +7 -7
  103. package/coverage/src/goal-reducer.ts.html +0 -982
  104. package/coverage/src/goal-store.ts.html +0 -742
  105. package/coverage/src/goal-verifier.ts.html +0 -808
  106. package/dist/goal-coordinator.d.ts +0 -22
  107. package/dist/goal-coordinator.js +0 -28
  108. package/dist/goal-reducer.d.ts +0 -98
  109. package/dist/goal-reducer.js +0 -190
  110. package/dist/goal-store.d.ts +0 -22
  111. package/dist/goal-store.js +0 -188
  112. package/dist/goal-types.d.ts +0 -82
  113. package/dist/goal-types.js +0 -1
  114. package/dist/goal-verifier.d.ts +0 -20
  115. package/dist/goal-verifier.js +0 -198
  116. package/docs/architecture/goal-state-schema.md +0 -505
  117. package/src/goal-coordinator.ts +0 -58
  118. package/src/goal-reducer.ts +0 -299
  119. package/src/goal-store.ts +0 -219
  120. package/src/goal-types.ts +0 -104
  121. package/src/goal-verifier.ts +0 -241
@@ -23,9 +23,9 @@
23
23
  <div class='clearfix'>
24
24
 
25
25
  <div class='fl pad1y space-right2'>
26
- <span class="strong">84.93% </span>
26
+ <span class="strong">87.5% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>62/73</span>
28
+ <span class='fraction'>49/56</span>
29
29
  </div>
30
30
 
31
31
 
@@ -37,16 +37,16 @@
37
37
 
38
38
 
39
39
  <div class='fl pad1y space-right2'>
40
- <span class="strong">81.25% </span>
40
+ <span class="strong">100% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>13/16</span>
42
+ <span class='fraction'>8/8</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
- <span class="strong">86.76% </span>
47
+ <span class="strong">95.91% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>59/68</span>
49
+ <span class='fraction'>47/49</span>
50
50
  </div>
51
51
 
52
52
 
@@ -209,13 +209,13 @@
209
209
  <a name='L144'></a><a href='#L144'>144</a>
210
210
  <a name='L145'></a><a href='#L145'>145</a>
211
211
  <a name='L146'></a><a href='#L146'>146</a>
212
- <a name='L147'></a><a href='#L147'>147</a>
213
- <a name='L148'></a><a href='#L148'>148</a>
214
- <a name='L149'></a><a href='#L149'>149</a>
215
- <a name='L150'></a><a href='#L150'>150</a>
216
- <a name='L151'></a><a href='#L151'>151</a>
217
- <a name='L152'></a><a href='#L152'>152</a>
218
- <a name='L153'></a><a href='#L153'>153</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
212
+ <a name='L147'></a><a href='#L147'>147</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
213
+ <span class="cline-any cline-neutral">&nbsp;</span>
214
+ <span class="cline-any cline-neutral">&nbsp;</span>
215
+ <span class="cline-any cline-neutral">&nbsp;</span>
216
+ <span class="cline-any cline-neutral">&nbsp;</span>
217
+ <span class="cline-any cline-neutral">&nbsp;</span>
218
+ <span class="cline-any cline-neutral">&nbsp;</span>
219
219
  <span class="cline-any cline-neutral">&nbsp;</span>
220
220
  <span class="cline-any cline-neutral">&nbsp;</span>
221
221
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -247,46 +247,57 @@
247
247
  <span class="cline-any cline-neutral">&nbsp;</span>
248
248
  <span class="cline-any cline-neutral">&nbsp;</span>
249
249
  <span class="cline-any cline-neutral">&nbsp;</span>
250
- <span class="cline-any cline-yes">73x</span>
251
250
  <span class="cline-any cline-neutral">&nbsp;</span>
252
251
  <span class="cline-any cline-neutral">&nbsp;</span>
253
- <span class="cline-any cline-yes">65x</span>
254
- <span class="cline-any cline-yes">65x</span>
255
- <span class="cline-any cline-yes">47x</span>
256
252
  <span class="cline-any cline-neutral">&nbsp;</span>
257
- <span class="cline-any cline-yes">65x</span>
258
- <span class="cline-any cline-yes">3x</span>
259
- <span class="cline-any cline-yes">3x</span>
260
- <span class="cline-any cline-yes">3x</span>
261
- <span class="cline-any cline-yes">3x</span>
262
253
  <span class="cline-any cline-neutral">&nbsp;</span>
263
- <span class="cline-any cline-yes">65x</span>
264
254
  <span class="cline-any cline-neutral">&nbsp;</span>
265
255
  <span class="cline-any cline-neutral">&nbsp;</span>
266
256
  <span class="cline-any cline-neutral">&nbsp;</span>
267
- <span class="cline-any cline-yes">4x</span>
268
- <span class="cline-any cline-yes">3x</span>
269
- <span class="cline-any cline-yes">1x</span>
270
- <span class="cline-any cline-yes">1x</span>
271
- <span class="cline-any cline-yes">1x</span>
272
- <span class="cline-any cline-yes">1x</span>
273
- <span class="cline-any cline-no">&nbsp;</span>
274
- <span class="cline-any cline-no">&nbsp;</span>
275
257
  <span class="cline-any cline-neutral">&nbsp;</span>
276
- <span class="cline-any cline-yes">1x</span>
277
- <span class="cline-any cline-yes">1x</span>
278
- <span class="cline-any cline-yes">1x</span>
279
- <span class="cline-any cline-yes">1x</span>
280
- <span class="cline-any cline-yes">1x</span>
281
- <span class="cline-any cline-no">&nbsp;</span>
282
258
  <span class="cline-any cline-neutral">&nbsp;</span>
283
- <span class="cline-any cline-yes">1x</span>
284
259
  <span class="cline-any cline-neutral">&nbsp;</span>
260
+ <span class="cline-any cline-yes">77x</span>
261
+ <span class="cline-any cline-yes">77x</span>
285
262
  <span class="cline-any cline-neutral">&nbsp;</span>
286
263
  <span class="cline-any cline-neutral">&nbsp;</span>
287
264
  <span class="cline-any cline-neutral">&nbsp;</span>
288
265
  <span class="cline-any cline-neutral">&nbsp;</span>
289
266
  <span class="cline-any cline-neutral">&nbsp;</span>
267
+ <span class="cline-any cline-neutral">&nbsp;</span>
268
+ <span class="cline-any cline-yes">70x</span>
269
+ <span class="cline-any cline-yes">70x</span>
270
+ <span class="cline-any cline-yes">70x</span>
271
+ <span class="cline-any cline-yes">54x</span>
272
+ <span class="cline-any cline-yes">54x</span>
273
+ <span class="cline-any cline-neutral">&nbsp;</span>
274
+ <span class="cline-any cline-yes">70x</span>
275
+ <span class="cline-any cline-yes">70x</span>
276
+ <span class="cline-any cline-yes">49x</span>
277
+ <span class="cline-any cline-yes">49x</span>
278
+ <span class="cline-any cline-neutral">&nbsp;</span>
279
+ <span class="cline-any cline-yes">70x</span>
280
+ <span class="cline-any cline-yes">71x</span>
281
+ <span class="cline-any cline-yes">71x</span>
282
+ <span class="cline-any cline-yes">71x</span>
283
+ <span class="cline-any cline-yes">5x</span>
284
+ <span class="cline-any cline-yes">5x</span>
285
+ <span class="cline-any cline-yes">5x</span>
286
+ <span class="cline-any cline-yes">5x</span>
287
+ <span class="cline-any cline-yes">5x</span>
288
+ <span class="cline-any cline-neutral">&nbsp;</span>
289
+ <span class="cline-any cline-yes">70x</span>
290
+ <span class="cline-any cline-neutral">&nbsp;</span>
291
+ <span class="cline-any cline-neutral">&nbsp;</span>
292
+ <span class="cline-any cline-neutral">&nbsp;</span>
293
+ <span class="cline-any cline-yes">4x</span>
294
+ <span class="cline-any cline-yes">3x</span>
295
+ <span class="cline-any cline-yes">1x</span>
296
+ <span class="cline-any cline-yes">1x</span>
297
+ <span class="cline-any cline-neutral">&nbsp;</span>
298
+ <span class="cline-any cline-neutral">&nbsp;</span>
299
+ <span class="cline-any cline-neutral">&nbsp;</span>
300
+ <span class="cline-any cline-neutral">&nbsp;</span>
290
301
  <span class="cline-any cline-yes">1x</span>
291
302
  <span class="cline-any cline-neutral">&nbsp;</span>
292
303
  <span class="cline-any cline-no">&nbsp;</span>
@@ -306,29 +317,18 @@
306
317
  <span class="cline-any cline-neutral">&nbsp;</span>
307
318
  <span class="cline-any cline-neutral">&nbsp;</span>
308
319
  <span class="cline-any cline-neutral">&nbsp;</span>
309
- <span class="cline-any cline-yes">43x</span>
310
- <span class="cline-any cline-yes">3x</span>
311
- <span class="cline-any cline-yes">3x</span>
312
- <span class="cline-any cline-yes">3x</span>
313
- <span class="cline-any cline-yes">3x</span>
314
- <span class="cline-any cline-no">&nbsp;</span>
315
- <span class="cline-any cline-no">&nbsp;</span>
320
+ <span class="cline-any cline-yes">39x</span>
316
321
  <span class="cline-any cline-neutral">&nbsp;</span>
317
- <span class="cline-any cline-yes">3x</span>
318
- <span class="cline-any cline-yes">3x</span>
319
- <span class="cline-any cline-yes">3x</span>
320
- <span class="cline-any cline-yes">3x</span>
321
- <span class="cline-any cline-yes">3x</span>
322
322
  <span class="cline-any cline-neutral">&nbsp;</span>
323
323
  <span class="cline-any cline-yes">3x</span>
324
- <span class="cline-any cline-neutral">&nbsp;</span>
324
+ <span class="cline-any cline-yes">3x</span>
325
325
  <span class="cline-any cline-yes">3x</span>
326
326
  <span class="cline-any cline-neutral">&nbsp;</span>
327
327
  <span class="cline-any cline-no">&nbsp;</span>
328
328
  <span class="cline-any cline-neutral">&nbsp;</span>
329
329
  <span class="cline-any cline-neutral">&nbsp;</span>
330
330
  <span class="cline-any cline-neutral">&nbsp;</span>
331
- <span class="cline-any cline-yes">40x</span>
331
+ <span class="cline-any cline-yes">36x</span>
332
332
  <span class="cline-any cline-neutral">&nbsp;</span>
333
333
  <span class="cline-any cline-neutral">&nbsp;</span>
334
334
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -336,18 +336,6 @@
336
336
  <span class="cline-any cline-yes">2x</span>
337
337
  <span class="cline-any cline-yes">2x</span>
338
338
  <span class="cline-any cline-yes">2x</span>
339
- <span class="cline-any cline-yes">2x</span>
340
- <span class="cline-any cline-no">&nbsp;</span>
341
- <span class="cline-any cline-no">&nbsp;</span>
342
- <span class="cline-any cline-neutral">&nbsp;</span>
343
- <span class="cline-any cline-yes">2x</span>
344
- <span class="cline-any cline-yes">2x</span>
345
- <span class="cline-any cline-yes">2x</span>
346
- <span class="cline-any cline-yes">2x</span>
347
- <span class="cline-any cline-yes">2x</span>
348
- <span class="cline-any cline-neutral">&nbsp;</span>
349
- <span class="cline-any cline-yes">2x</span>
350
- <span class="cline-any cline-neutral">&nbsp;</span>
351
339
  <span class="cline-any cline-neutral">&nbsp;</span>
352
340
  <span class="cline-any cline-neutral">&nbsp;</span>
353
341
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -360,7 +348,7 @@
360
348
  <span class="cline-any cline-yes">4x</span>
361
349
  <span class="cline-any cline-neutral">&nbsp;</span>
362
350
  <span class="cline-any cline-neutral">&nbsp;</span>
363
- <span class="cline-any cline-yes">73x</span>
351
+ <span class="cline-any cline-yes">77x</span>
364
352
  <span class="cline-any cline-neutral">&nbsp;</span>
365
353
  <span class="cline-any cline-neutral">&nbsp;</span>
366
354
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -369,8 +357,18 @@
369
357
  <span class="cline-any cline-neutral">&nbsp;</span>
370
358
  <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { randomUUID } from "node:crypto";
371
359
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
360
+ import {
361
+ type CleanReply,
362
+ type CreateTaskReply,
363
+ type PendingReply,
364
+ type PingReply,
365
+ replyChannel,
366
+ TASKS_RPC,
367
+ } from "../rpc/channels.js";
368
+ import { type RpcReply, rpcCall } from "../rpc/cross-extension-rpc.js";
372
369
  import type { TaskStore } from "../task-store.js";
373
370
  import type { LoopEntry } from "../types.js";
371
+ import { NATIVE_TASKS_PROVIDER } from "./native-task-rpc.js";
374
372
  import { emitNativeTaskEvent } from "./task-events.js";
375
373
  &nbsp;
376
374
  export interface TaskRuntimeBridgeOptions {
@@ -380,6 +378,10 @@ export interface TaskRuntimeBridgeOptions {
380
378
  getNativeTaskStore: () =&gt; TaskStore | undefined;
381
379
  onNativeTaskCreated?: (taskStore: TaskStore) =&gt; void;
382
380
  onNativeTasksPruned?: (taskStore: TaskStore) =&gt; Promise&lt;void&gt; | void;
381
+ /** Called when a detection window opens. */
382
+ onDetectionStarted?: () =&gt; void;
383
+ /** Called when a detection window closes (provider found or probe timed out). */
384
+ onDetectionSettled?: () =&gt; void;
383
385
  debug?: (...args: unknown[]) =&gt; void;
384
386
  }
385
387
  &nbsp;
@@ -398,48 +400,51 @@ export function createTaskRuntimeBridge(options: TaskRuntimeBridgeOptions): Task
398
400
  getNativeTaskStore,
399
401
  onNativeTaskCreated,
400
402
  onNativeTasksPruned,
403
+ onDetectionStarted,
404
+ onDetectionSettled,
401
405
  debug,
402
406
  } = options;
407
+ let detectionEpoch = 0;
403
408
  &nbsp;
404
409
  function checkTasksVersion() {
410
+ // Not rpcProbe: pi-loop's own native server also answers this ping, and a
411
+ // first-reply-wins probe would always settle on that self-reply. Keep the
412
+ // listener open for the whole window and skip self-replies so a slower
413
+ // external provider (pi-tasks) is still detected.
414
+ const epoch = ++detectionEpoch;
415
+ onDetectionStarted?.();
416
+ const settleCurrentDetection = () =&gt; {
417
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (epoch !== detectionEpoch) <span class="cstat-no" title="statement not covered" >return;</span>
418
+ onDetectionSettled?.();
419
+ };
405
420
  const requestId = randomUUID();
406
421
  const timer = setTimeout(() =&gt; {
407
422
  unsub();
423
+ settleCurrentDetection();
408
424
  }, 5000);
409
- const unsub = pi.events.on(`tasks:rpc:ping:reply:${requestId}`, (raw: unknown) =&gt; {
425
+ const unsub = pi.events.on(replyChannel(TASKS_RPC.ping, requestId), (raw: unknown) =&gt; {
426
+ const reply = raw as RpcReply&lt;PingReply&gt; | undefined;
427
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (!reply?.success || !reply.data) <span class="cstat-no" title="statement not covered" >return;</span>
428
+ if (reply.data.provider === NATIVE_TASKS_PROVIDER) return;
429
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (reply.data.version === undefined) <span class="cstat-no" title="statement not covered" >return;</span>
410
430
  unsub();
411
431
  clearTimeout(timer);
412
- const remoteVersion = (raw as { data?: { version?: number } })?.data?.version;
413
- <span class="missing-if-branch" title="else path not taken" >E</span>if (remoteVersion !== undefined) setTasksAvailable(true);
432
+ setTasksAvailable(true);
433
+ settleCurrentDetection();
414
434
  });
415
- pi.events.emit("tasks:rpc:ping", { requestId });
435
+ pi.events.emit(TASKS_RPC.ping, { requestId });
416
436
  }
417
437
  &nbsp;
418
438
  async function autoCreateTask(entry: LoopEntry): Promise&lt;string | undefined&gt; {
419
439
  if (!entry.autoTask) return undefined;
420
440
  if (isTasksAvailable()) {
421
441
  try {
422
- const requestId = randomUUID();
423
- const taskId = await new Promise&lt;string | undefined&gt;((resolve) =&gt; {
424
- const timer = <span class="fstat-no" title="function not covered" >setTimeout(() =&gt; {</span>
425
- <span class="cstat-no" title="statement not covered" > unsub();</span>
426
- <span class="cstat-no" title="statement not covered" > resolve(undefined);</span>
427
- }, 5000);
428
- const unsub = pi.events.on(`tasks:rpc:create:reply:${requestId}`, (raw: unknown) =&gt; {
429
- unsub();
430
- clearTimeout(timer);
431
- const reply = raw as { success: boolean; data?: { id: string } };
432
- if (reply.success &amp;&amp; reply.data) resolve(reply.data.id);
433
- else <span class="cstat-no" title="statement not covered" ><span class="missing-if-branch" title="else path not taken" >E</span>resolve(undefined);</span>
434
- });
435
- pi.events.emit("tasks:rpc:create", {
436
- requestId,
437
- subject: entry.prompt.slice(0, 80),
438
- description: `Auto-created from loop #${entry.id}`,
439
- metadata: { loopId: entry.id, trigger: entry.trigger },
440
- });
441
- });
442
- return taskId;
442
+ const reply = await rpcCall&lt;CreateTaskReply&gt;(pi.events, TASKS_RPC.create, {
443
+ subject: entry.prompt.slice(0, 80),
444
+ description: `Auto-created from loop #${entry.id}`,
445
+ metadata: { loopId: entry.id, trigger: entry.trigger },
446
+ }, 5000);
447
+ return reply.id;
443
448
  } catch {
444
449
  <span class="cstat-no" title="statement not covered" > return undefined;</span>
445
450
  }
@@ -459,22 +464,11 @@ export function createTaskRuntimeBridge(options: TaskRuntimeBridgeOptions): Task
459
464
  &nbsp;
460
465
  async function hasPendingTasks(): Promise&lt;number&gt; {
461
466
  if (isTasksAvailable()) {
467
+ // -1 is this bridge's "unknown" sentinel, consumed by notification-runtime;
468
+ // the RPC layer itself rejects on failure.
462
469
  try {
463
- const requestId = randomUUID();
464
- const count = await new Promise&lt;number&gt;((resolve) =&gt; {
465
- const timer = <span class="fstat-no" title="function not covered" >setTimeout(() =&gt; {</span>
466
- <span class="cstat-no" title="statement not covered" > unsub();</span>
467
- <span class="cstat-no" title="statement not covered" > resolve(-1);</span>
468
- }, 3000);
469
- const unsub = pi.events.on(`tasks:rpc:pending:reply:${requestId}`, (raw: unknown) =&gt; {
470
- unsub();
471
- clearTimeout(timer);
472
- const reply = raw as { success: boolean; data?: { pending: number } };
473
- resolve(reply.success &amp;&amp; reply.data ? reply.data.pending : <span class="branch-1 cbranch-no" title="branch not covered" >-1)</span>;
474
- });
475
- pi.events.emit("tasks:rpc:pending", { requestId });
476
- });
477
- return count;
470
+ const reply = await rpcCall&lt;PendingReply&gt;(pi.events, TASKS_RPC.pending, {}, 3000);
471
+ return reply.pending;
478
472
  } catch {
479
473
  <span class="cstat-no" title="statement not covered" > return -1;</span>
480
474
  }
@@ -486,20 +480,8 @@ export function createTaskRuntimeBridge(options: TaskRuntimeBridgeOptions): Task
486
480
  async function cleanDoneTasks(): Promise&lt;void&gt; {
487
481
  if (isTasksAvailable()) {
488
482
  try {
489
- const requestId = randomUUID();
490
- await new Promise&lt;void&gt;((resolve) =&gt; {
491
- const timer = <span class="fstat-no" title="function not covered" >setTimeout(() =&gt; {</span>
492
- <span class="cstat-no" title="statement not covered" > unsub();</span>
493
- <span class="cstat-no" title="statement not covered" > resolve();</span>
494
- }, 3000);
495
- const unsub = pi.events.on(`tasks:rpc:clean:reply:${requestId}`, () =&gt; {
496
- unsub();
497
- clearTimeout(timer);
498
- debug?.("tasks:rpc:clean — done tasks swept");
499
- resolve();
500
- });
501
- pi.events.emit("tasks:rpc:clean", { requestId });
502
- });
483
+ await rpcCall&lt;CleanReply&gt;(pi.events, TASKS_RPC.clean, {}, 3000);
484
+ debug?.(`${TASKS_RPC.clean} done tasks swept`);
503
485
  } catch {
504
486
  // timeout or error, ignore
505
487
  }
@@ -526,7 +508,7 @@ export function createTaskRuntimeBridge(options: TaskRuntimeBridgeOptions): Task
526
508
  <div class='footer quiet pad2 space-top1 center small'>
527
509
  Code coverage generated by
528
510
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
529
- at 2026-06-23T21:49:18.784Z
511
+ at 2026-07-02T19:31:50.067Z
530
512
  </div>
531
513
  <script src="../../prettify.js"></script>
532
514
  <script>
@@ -30,9 +30,9 @@
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
- <span class="strong">85.1% </span>
33
+ <span class="strong">83.67% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>40/47</span>
35
+ <span class='fraction'>41/49</span>
36
36
  </div>
37
37
 
38
38
 
@@ -175,18 +175,18 @@
175
175
  <span class="cline-any cline-yes">5x</span>
176
176
  <span class="cline-any cline-neutral">&nbsp;</span>
177
177
  <span class="cline-any cline-neutral">&nbsp;</span>
178
- <span class="cline-any cline-yes">33x</span>
179
- <span class="cline-any cline-yes">33x</span>
178
+ <span class="cline-any cline-yes">32x</span>
179
+ <span class="cline-any cline-yes">32x</span>
180
180
  <span class="cline-any cline-neutral">&nbsp;</span>
181
181
  <span class="cline-any cline-no">&nbsp;</span>
182
182
  <span class="cline-any cline-neutral">&nbsp;</span>
183
183
  <span class="cline-any cline-neutral">&nbsp;</span>
184
184
  <span class="cline-any cline-neutral">&nbsp;</span>
185
- <span class="cline-any cline-yes">134x</span>
185
+ <span class="cline-any cline-yes">137x</span>
186
186
  <span class="cline-any cline-neutral">&nbsp;</span>
187
187
  <span class="cline-any cline-neutral">&nbsp;</span>
188
- <span class="cline-any cline-yes">134x</span>
189
- <span class="cline-any cline-yes">134x</span>
188
+ <span class="cline-any cline-yes">137x</span>
189
+ <span class="cline-any cline-yes">137x</span>
190
190
  <span class="cline-any cline-neutral">&nbsp;</span>
191
191
  <span class="cline-any cline-neutral">&nbsp;</span>
192
192
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -217,56 +217,56 @@
217
217
  <span class="cline-any cline-neutral">&nbsp;</span>
218
218
  <span class="cline-any cline-neutral">&nbsp;</span>
219
219
  <span class="cline-any cline-neutral">&nbsp;</span>
220
- <span class="cline-any cline-yes">33x</span>
220
+ <span class="cline-any cline-yes">32x</span>
221
221
  <span class="cline-any cline-neutral">&nbsp;</span>
222
- <span class="cline-any cline-yes">33x</span>
223
- <span class="cline-any cline-yes">33x</span>
224
- <span class="cline-any cline-yes">33x</span>
225
- <span class="cline-any cline-yes">33x</span>
226
- <span class="cline-any cline-yes">33x</span>
222
+ <span class="cline-any cline-yes">32x</span>
223
+ <span class="cline-any cline-yes">32x</span>
224
+ <span class="cline-any cline-yes">32x</span>
225
+ <span class="cline-any cline-yes">32x</span>
226
+ <span class="cline-any cline-yes">32x</span>
227
227
  <span class="cline-any cline-neutral">&nbsp;</span>
228
- <span class="cline-any cline-yes">33x</span>
228
+ <span class="cline-any cline-yes">32x</span>
229
229
  <span class="cline-any cline-yes">1x</span>
230
230
  <span class="cline-any cline-yes">1x</span>
231
231
  <span class="cline-any cline-neutral">&nbsp;</span>
232
232
  <span class="cline-any cline-neutral">&nbsp;</span>
233
- <span class="cline-any cline-yes">32x</span>
233
+ <span class="cline-any cline-yes">31x</span>
234
234
  <span class="cline-any cline-neutral">&nbsp;</span>
235
235
  <span class="cline-any cline-neutral">&nbsp;</span>
236
236
  <span class="cline-any cline-neutral">&nbsp;</span>
237
- <span class="cline-any cline-yes">171x</span>
238
- <span class="cline-any cline-yes">72x</span>
237
+ <span class="cline-any cline-yes">172x</span>
238
+ <span class="cline-any cline-yes">76x</span>
239
239
  <span class="cline-any cline-neutral">&nbsp;</span>
240
- <span class="cline-any cline-yes">22x</span>
241
- <span class="cline-any cline-yes">22x</span>
240
+ <span class="cline-any cline-yes">17x</span>
241
+ <span class="cline-any cline-yes">17x</span>
242
242
  <span class="cline-any cline-yes">1x</span>
243
243
  <span class="cline-any cline-yes">1x</span>
244
244
  <span class="cline-any cline-neutral">&nbsp;</span>
245
245
  <span class="cline-any cline-neutral">&nbsp;</span>
246
- <span class="cline-any cline-yes">21x</span>
246
+ <span class="cline-any cline-yes">16x</span>
247
247
  <span class="cline-any cline-neutral">&nbsp;</span>
248
- <span class="cline-any cline-yes">10x</span>
248
+ <span class="cline-any cline-yes">9x</span>
249
249
  <span class="cline-any cline-no">&nbsp;</span>
250
250
  <span class="cline-any cline-no">&nbsp;</span>
251
251
  <span class="cline-any cline-no">&nbsp;</span>
252
252
  <span class="cline-any cline-neutral">&nbsp;</span>
253
253
  <span class="cline-any cline-neutral">&nbsp;</span>
254
- <span class="cline-any cline-yes">10x</span>
254
+ <span class="cline-any cline-yes">9x</span>
255
255
  <span class="cline-any cline-neutral">&nbsp;</span>
256
- <span class="cline-any cline-yes">10x</span>
257
- <span class="cline-any cline-yes">10x</span>
256
+ <span class="cline-any cline-yes">9x</span>
257
+ <span class="cline-any cline-yes">9x</span>
258
258
  <span class="cline-any cline-no">&nbsp;</span>
259
259
  <span class="cline-any cline-no">&nbsp;</span>
260
260
  <span class="cline-any cline-neutral">&nbsp;</span>
261
261
  <span class="cline-any cline-neutral">&nbsp;</span>
262
- <span class="cline-any cline-yes">10x</span>
262
+ <span class="cline-any cline-yes">9x</span>
263
263
  <span class="cline-any cline-yes">1x</span>
264
264
  <span class="cline-any cline-yes">1x</span>
265
265
  <span class="cline-any cline-yes">1x</span>
266
266
  <span class="cline-any cline-neutral">&nbsp;</span>
267
267
  <span class="cline-any cline-neutral">&nbsp;</span>
268
- <span class="cline-any cline-yes">9x</span>
269
- <span class="cline-any cline-yes">7x</span>
268
+ <span class="cline-any cline-yes">8x</span>
269
+ <span class="cline-any cline-yes">6x</span>
270
270
  <span class="cline-any cline-neutral">&nbsp;</span>
271
271
  <span class="cline-any cline-yes">2x</span>
272
272
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -325,7 +325,7 @@ export class CronScheduler {
325
325
  const _scheduleExpr = entry.trigger.type === "hybrid" ? entry.trigger.cron : (entry.trigger as { schedule: string }).schedule;
326
326
  &nbsp;
327
327
  const nextFire = computeNextFire(entry);
328
- const minuteField = _scheduleExpr.trim().split(/\s+/)[0];
328
+ const minuteField = _scheduleExpr.trim().split(/\s+/)[0] ?? <span class="branch-1 cbranch-no" title="branch not covered" >"";</span>
329
329
  const minuteStep = minuteField.startsWith("*/") ? parseInt(minuteField.slice(2), 10) || <span class="branch-1 cbranch-no" title="branch not covered" >30 : 3</span>0;
330
330
  const jitter = computeJitter(entry.id, entry.recurring, minuteStep);
331
331
  const fireTime = nextFire.getTime() + jitter;
@@ -385,7 +385,7 @@ export class CronScheduler {
385
385
  <div class='footer quiet pad2 space-top1 center small'>
386
386
  Code coverage generated by
387
387
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
388
- at 2026-06-23T21:49:18.784Z
388
+ at 2026-07-02T19:31:50.067Z
389
389
  </div>
390
390
  <script src="../prettify.js"></script>
391
391
  <script>
@@ -263,31 +263,31 @@
263
263
  <span class="cline-any cline-neutral">&nbsp;</span>
264
264
  <span class="cline-any cline-neutral">&nbsp;</span>
265
265
  <span class="cline-any cline-neutral">&nbsp;</span>
266
- <span class="cline-any cline-yes">9x</span>
267
- <span class="cline-any cline-yes">9x</span>
266
+ <span class="cline-any cline-yes">10x</span>
267
+ <span class="cline-any cline-yes">10x</span>
268
268
  <span class="cline-any cline-neutral">&nbsp;</span>
269
269
  <span class="cline-any cline-neutral">&nbsp;</span>
270
270
  <span class="cline-any cline-neutral">&nbsp;</span>
271
- <span class="cline-any cline-yes">202x</span>
271
+ <span class="cline-any cline-yes">218x</span>
272
272
  <span class="cline-any cline-neutral">&nbsp;</span>
273
273
  <span class="cline-any cline-neutral">&nbsp;</span>
274
- <span class="cline-any cline-yes">192x</span>
275
- <span class="cline-any cline-yes">192x</span>
276
- <span class="cline-any cline-yes">192x</span>
277
- <span class="cline-any cline-yes">62x</span>
278
- <span class="cline-any cline-yes">38x</span>
274
+ <span class="cline-any cline-yes">200x</span>
275
+ <span class="cline-any cline-yes">200x</span>
276
+ <span class="cline-any cline-yes">200x</span>
277
+ <span class="cline-any cline-yes">61x</span>
278
+ <span class="cline-any cline-yes">37x</span>
279
279
  <span class="cline-any cline-neutral">&nbsp;</span>
280
280
  <span class="cline-any cline-neutral">&nbsp;</span>
281
281
  <span class="cline-any cline-neutral">&nbsp;</span>
282
282
  <span class="cline-any cline-neutral">&nbsp;</span>
283
283
  <span class="cline-any cline-neutral">&nbsp;</span>
284
284
  <span class="cline-any cline-neutral">&nbsp;</span>
285
- <span class="cline-any cline-yes">130x</span>
286
- <span class="cline-any cline-yes">130x</span>
285
+ <span class="cline-any cline-yes">137x</span>
286
+ <span class="cline-any cline-yes">137x</span>
287
287
  <span class="cline-any cline-yes">1x</span>
288
288
  <span class="cline-any cline-neutral">&nbsp;</span>
289
- <span class="cline-any cline-yes">129x</span>
290
- <span class="cline-any cline-yes">129x</span>
289
+ <span class="cline-any cline-yes">136x</span>
290
+ <span class="cline-any cline-yes">136x</span>
291
291
  <span class="cline-any cline-neutral">&nbsp;</span>
292
292
  <span class="cline-any cline-neutral">&nbsp;</span>
293
293
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -302,15 +302,15 @@
302
302
  <span class="cline-any cline-neutral">&nbsp;</span>
303
303
  <span class="cline-any cline-neutral">&nbsp;</span>
304
304
  <span class="cline-any cline-neutral">&nbsp;</span>
305
- <span class="cline-any cline-yes">129x</span>
305
+ <span class="cline-any cline-yes">136x</span>
306
306
  <span class="cline-any cline-neutral">&nbsp;</span>
307
307
  <span class="cline-any cline-neutral">&nbsp;</span>
308
308
  <span class="cline-any cline-neutral">&nbsp;</span>
309
309
  <span class="cline-any cline-neutral">&nbsp;</span>
310
+ <span class="cline-any cline-yes">8x</span>
311
+ <span class="cline-any cline-yes">8x</span>
312
+ <span class="cline-any cline-yes">8x</span>
310
313
  <span class="cline-any cline-yes">7x</span>
311
- <span class="cline-any cline-yes">7x</span>
312
- <span class="cline-any cline-yes">7x</span>
313
- <span class="cline-any cline-yes">6x</span>
314
314
  <span class="cline-any cline-neutral">&nbsp;</span>
315
315
  <span class="cline-any cline-neutral">&nbsp;</span>
316
316
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -318,7 +318,7 @@
318
318
  <span class="cline-any cline-neutral">&nbsp;</span>
319
319
  <span class="cline-any cline-neutral">&nbsp;</span>
320
320
  <span class="cline-any cline-neutral">&nbsp;</span>
321
- <span class="cline-any cline-yes">6x</span>
321
+ <span class="cline-any cline-yes">7x</span>
322
322
  <span class="cline-any cline-neutral">&nbsp;</span>
323
323
  <span class="cline-any cline-neutral">&nbsp;</span>
324
324
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -339,10 +339,10 @@
339
339
  <span class="cline-any cline-neutral">&nbsp;</span>
340
340
  <span class="cline-any cline-neutral">&nbsp;</span>
341
341
  <span class="cline-any cline-neutral">&nbsp;</span>
342
- <span class="cline-any cline-yes">27x</span>
343
- <span class="cline-any cline-yes">27x</span>
344
- <span class="cline-any cline-yes">27x</span>
345
- <span class="cline-any cline-yes">27x</span>
342
+ <span class="cline-any cline-yes">26x</span>
343
+ <span class="cline-any cline-yes">26x</span>
344
+ <span class="cline-any cline-yes">26x</span>
345
+ <span class="cline-any cline-yes">26x</span>
346
346
  <span class="cline-any cline-neutral">&nbsp;</span>
347
347
  <span class="cline-any cline-neutral">&nbsp;</span>
348
348
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -350,7 +350,7 @@
350
350
  <span class="cline-any cline-neutral">&nbsp;</span>
351
351
  <span class="cline-any cline-neutral">&nbsp;</span>
352
352
  <span class="cline-any cline-neutral">&nbsp;</span>
353
- <span class="cline-any cline-yes">27x</span>
353
+ <span class="cline-any cline-yes">26x</span>
354
354
  <span class="cline-any cline-neutral">&nbsp;</span>
355
355
  <span class="cline-any cline-neutral">&nbsp;</span>
356
356
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -379,9 +379,9 @@
379
379
  <span class="cline-any cline-neutral">&nbsp;</span>
380
380
  <span class="cline-any cline-neutral">&nbsp;</span>
381
381
  <span class="cline-any cline-neutral">&nbsp;</span>
382
- <span class="cline-any cline-yes">26x</span>
383
- <span class="cline-any cline-yes">26x</span>
384
- <span class="cline-any cline-yes">23x</span>
382
+ <span class="cline-any cline-yes">27x</span>
383
+ <span class="cline-any cline-yes">27x</span>
384
+ <span class="cline-any cline-yes">24x</span>
385
385
  <span class="cline-any cline-neutral">&nbsp;</span>
386
386
  <span class="cline-any cline-neutral">&nbsp;</span>
387
387
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -389,7 +389,7 @@
389
389
  <span class="cline-any cline-neutral">&nbsp;</span>
390
390
  <span class="cline-any cline-neutral">&nbsp;</span>
391
391
  <span class="cline-any cline-neutral">&nbsp;</span>
392
- <span class="cline-any cline-yes">23x</span>
392
+ <span class="cline-any cline-yes">24x</span>
393
393
  <span class="cline-any cline-neutral">&nbsp;</span>
394
394
  <span class="cline-any cline-neutral">&nbsp;</span>
395
395
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -652,7 +652,7 @@ export class LoopStore extends ReducerBackedStore&lt;LoopEntry, LoopReducerState
652
652
  <div class='footer quiet pad2 space-top1 center small'>
653
653
  Code coverage generated by
654
654
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
655
- at 2026-06-23T21:49:18.784Z
655
+ at 2026-07-02T19:31:50.067Z
656
656
  </div>
657
657
  <script src="../prettify.js"></script>
658
658
  <script>