@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.
- package/README.md +8 -0
- package/index.ts +36 -1
- 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
|
});
|