pi-cohort 7.2.0 → 7.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [7.2.1] - 2026-10-16
4
+
5
+ ### Fixed
6
+
7
+ - A second pi-cohort instance loaded into the same Node process no longer stops the first instance's result watcher or drops its event subscriptions. The extension factory and `registerSubagentNotify` used to call cleanup callbacks a previous instance stored on `globalThis` (`__piSubagentRuntimeCleanup`, `__piSubagentEventUnsubscribes`, `__pi_subagents_notify_unsubscribe__`), which also retired a live session in another SDK runtime; those hooks are gone and each instance now tears down only itself in its own `session_shutdown` handler, which pi emits on reload, new, resume, fork, and quit. (#20)
8
+
3
9
  ## [7.2.0] - 2026-10-11
4
10
 
5
11
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cohort",
3
- "version": "7.2.0",
3
+ "version": "7.2.1",
4
4
  "description": "Delegate Pi work to focused child agents: code review, scouting, implementation, parallel audits, saved chains, and background jobs.",
5
5
  "author": "Jacek Juraszek",
6
6
  "license": "MIT",
@@ -246,15 +246,6 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
246
246
  return;
247
247
  }
248
248
  const globalStore = globalThis as Record<string, unknown>;
249
- const runtimeCleanupStoreKey = "__piSubagentRuntimeCleanup";
250
- const previousRuntimeCleanup = globalStore[runtimeCleanupStoreKey];
251
- if (typeof previousRuntimeCleanup === "function") {
252
- try {
253
- previousRuntimeCleanup();
254
- } catch {
255
- // Best effort cleanup for stale timers from an older reload.
256
- }
257
- }
258
249
 
259
250
  ensureAccessibleDir(RUN_STATE_ROOT_DIR, true);
260
251
  ensureAccessibleDir(RESULTS_DIR);
@@ -296,16 +287,6 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
296
287
  startResultWatcher();
297
288
  primeExistingResults();
298
289
 
299
- const runtimeCleanup = () => {
300
- stopResultWatcher();
301
- clearPendingForegroundControlNotices(state);
302
- if (state.poller) {
303
- clearInterval(state.poller);
304
- state.poller = null;
305
- }
306
- };
307
- globalStore[runtimeCleanupStoreKey] = runtimeCleanup;
308
-
309
290
  const { ensurePoller, handleStarted, handleComplete, resetJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR);
310
291
  const executor = createSubagentExecutor({
311
292
  pi,
@@ -491,20 +472,8 @@ DIAGNOSTICS:
491
472
  pi.registerTool(tool);
492
473
  registerSlashCommands(pi, state);
493
474
 
494
- const eventUnsubscribeStoreKey = "__piSubagentEventUnsubscribes";
495
475
  const controlNoticeSeenStoreKey = "__piSubagentVisibleControlNotices";
496
- const previousEventUnsubscribes = globalStore[eventUnsubscribeStoreKey];
497
- if (Array.isArray(previousEventUnsubscribes)) {
498
- for (const unsubscribe of previousEventUnsubscribes) {
499
- if (typeof unsubscribe !== "function") continue;
500
- try {
501
- unsubscribe();
502
- } catch {
503
- // Best effort cleanup for stale handlers from an older reload.
504
- }
505
- }
506
- }
507
- registerSubagentNotify(pi);
476
+ const unsubscribeNotify = registerSubagentNotify(pi);
508
477
 
509
478
  const existingVisibleControlNotices = globalStore[controlNoticeSeenStoreKey];
510
479
  const visibleControlNotices = existingVisibleControlNotices instanceof Set ? existingVisibleControlNotices as Set<string> : new Set<string>();
@@ -518,6 +487,7 @@ DIAGNOSTICS:
518
487
  });
519
488
  };
520
489
  const eventUnsubscribes = [
490
+ unsubscribeNotify,
521
491
  pi.events.on(SUBAGENT_ASYNC_STARTED_EVENT, handleStarted),
522
492
  pi.events.on(SUBAGENT_ASYNC_COMPLETE_EVENT, handleComplete),
523
493
  pi.events.on(SUBAGENT_CONTROL_EVENT, controlEventHandler),
@@ -527,7 +497,6 @@ DIAGNOSTICS:
527
497
  }
528
498
  }),
529
499
  ];
530
- globalStore[eventUnsubscribeStoreKey] = eventUnsubscribes;
531
500
 
532
501
  pi.on("message_end", (event, ctx) => {
533
502
  if (event.message.role !== "assistant") return;
@@ -591,9 +560,6 @@ DIAGNOSTICS:
591
560
  // Best effort cleanup during shutdown.
592
561
  }
593
562
  }
594
- if (globalStore[eventUnsubscribeStoreKey] === eventUnsubscribes) {
595
- delete globalStore[eventUnsubscribeStoreKey];
596
- }
597
563
  stopResultWatcher();
598
564
  if (state.poller) clearInterval(state.poller);
599
565
  state.poller = null;
@@ -608,9 +574,6 @@ DIAGNOSTICS:
608
574
  slashBridge.dispose();
609
575
  promptTemplateBridge.cancelAll();
610
576
  promptTemplateBridge.dispose();
611
- if (globalStore[runtimeCleanupStoreKey] === runtimeCleanup) {
612
- delete globalStore[runtimeCleanupStoreKey];
613
- }
614
577
  try {
615
578
  if (state.lastUiContext?.hasUI) {
616
579
  state.lastUiContext.ui.setWidget(WIDGET_KEY, undefined);
@@ -40,18 +40,7 @@ interface SubagentResult {
40
40
  totalTasks?: number;
41
41
  }
42
42
 
43
- export default function registerSubagentNotify(pi: ExtensionAPI): void {
44
- const unsubscribeStoreKey = "__pi_subagents_notify_unsubscribe__";
45
- const globalStore = globalThis as Record<string, unknown>;
46
- const previousUnsubscribe = globalStore[unsubscribeStoreKey];
47
- if (typeof previousUnsubscribe === "function") {
48
- try {
49
- previousUnsubscribe();
50
- } catch {
51
- // Best effort cleanup for stale handlers from an older reload.
52
- }
53
- }
54
-
43
+ export default function registerSubagentNotify(pi: ExtensionAPI): () => void {
55
44
  const seen = getGlobalSeenMap("__pi_subagents_notify_seen__");
56
45
  const ttlMs = 10 * 60 * 1000;
57
46
 
@@ -104,5 +93,5 @@ export default function registerSubagentNotify(pi: ExtensionAPI): void {
104
93
  );
105
94
  };
106
95
 
107
- globalStore[unsubscribeStoreKey] = pi.events.on(SUBAGENT_ASYNC_COMPLETE_EVENT, handleComplete);
96
+ return pi.events.on(SUBAGENT_ASYNC_COMPLETE_EVENT, handleComplete);
108
97
  }