@yusukeshib/pi-babysit 0.6.0 → 0.6.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 (3) hide show
  1. package/README.md +8 -0
  2. package/index.ts +36 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -127,6 +127,14 @@ suite at the end. Background subagents must likewise be collected before the
127
127
  parent task finishes so their answer and nested usage are not lost.
128
128
  Set `PI_BABYSIT_ALLOW_BASH=1` only as an explicit emergency escape hatch.
129
129
 
130
+ ## Pi shutdown and reload
131
+
132
+ A real Pi quit terminates every running process and subagent in the current Pi
133
+ session namespace. The workers are killed concurrently, so shutdown time does
134
+ not grow linearly when many sessions are active. Lifecycle transitions that keep
135
+ Pi running—`/reload`, `/new`, `/resume`, and `/fork`—leave detached workers
136
+ alone. Reloading or resuming the same Pi session reconnects to its namespace.
137
+
130
138
  ## Unexpected worker loss
131
139
 
132
140
  If the babysit supervisor disappears without recording an exit, pi-babysit
package/index.ts CHANGED
@@ -367,6 +367,35 @@ async function awaitConfirmedTermination(id: string): Promise<BsSession | null>
367
367
  return statusOf(id);
368
368
  }
369
369
 
370
+ export function shouldTerminateSessionsOnShutdown(reason: string | undefined): boolean {
371
+ return reason === "quit";
372
+ }
373
+
374
+ async function terminateRunningSessions(): Promise<void> {
375
+ let listed: Awaited<ReturnType<typeof listSessions>>;
376
+ try {
377
+ listed = await listSessions();
378
+ } catch {
379
+ return;
380
+ }
381
+ if (listed.error) return;
382
+
383
+ // A namespace may contain many independent commands and agents. Terminate
384
+ // them concurrently so quitting Pi does not wait for serial kill timeouts.
385
+ // allSettled ensures one unreachable worker cannot skip cleanup for others.
386
+ await Promise.allSettled(
387
+ listed.sessions
388
+ .filter((session) => session.state === "running")
389
+ .map(async (session) => {
390
+ const result = await bs(["kill", "-s", session.id, "--json"]);
391
+ if (result.code !== 0 || validateKillResponse(result.stdout)) return;
392
+ // A later resume of this Pi session must not turn intentional shutdown
393
+ // cleanup into a process-completion notification.
394
+ suppressNotify(session.id, "kill");
395
+ }),
396
+ );
397
+ }
398
+
370
399
  // ---------------------------------------------------------------------------
371
400
  // per-session metadata
372
401
  // ---------------------------------------------------------------------------
@@ -3338,9 +3367,15 @@ export default function (pi: ExtensionAPI) {
3338
3367
  }, POLL_MS);
3339
3368
  });
3340
3369
 
3341
- pi.on("session_shutdown", async () => {
3370
+ pi.on("session_shutdown", async (event) => {
3342
3371
  if (pollTimer) clearInterval(pollTimer);
3343
3372
  pollTimer = undefined;
3373
+ // Pi also emits session_shutdown while hot-reloading and switching sessions.
3374
+ // Preserve detached work for those lifecycle transitions; only a real Pi
3375
+ // quit owns and terminates every worker in the current session namespace.
3376
+ if (shouldTerminateSessionsOnShutdown(event?.reason)) {
3377
+ await terminateRunningSessions();
3378
+ }
3344
3379
  releaseRootLease(rootLeasePath);
3345
3380
  rootLeasePath = undefined;
3346
3381
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yusukeshib/pi-babysit",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Run any shell command and pi subagents under babysit, with context-safe captured output.",
5
5
  "keywords": [
6
6
  "pi-package",