pi-crew 0.2.4 → 0.2.6
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-crew",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Pi extension for coordinated AI teams, workflows, worktrees, and async task orchestration",
|
|
5
5
|
"author": "baphuongna",
|
|
6
6
|
"license": "MIT",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"cli-highlight": "^2.1.11",
|
|
81
81
|
"diff": "^5.2.0",
|
|
82
82
|
"jiti": "^2.6.1",
|
|
83
|
-
"@sinclair/typebox": "^
|
|
83
|
+
"@sinclair/typebox": "^0.34.49"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@mariozechner/pi-agent-core": "^0.65.0",
|
|
@@ -319,7 +319,16 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
319
319
|
// immediate cache invalidate via renderScheduler.schedule. Falls back to
|
|
320
320
|
// poll-only behavior on systems where fs.watch errors.
|
|
321
321
|
let crewWatcher: import("node:fs").FSWatcher | undefined;
|
|
322
|
+
// Separate map for foreground team-run AbortControllers (distinct from subagent controllers).
|
|
323
|
+
// P0 fix: stopSessionBoundSubagents must NOT abort foreground team runs on session switch.
|
|
324
|
+
// Foreground team runs run in the same process as the session; they naturally clean up
|
|
325
|
+
// when the session context is torn down. Only subagents need explicit abort on switch.
|
|
326
|
+
const foregroundTeamRunControllers = new Map<string | symbol, AbortController>();
|
|
327
|
+
|
|
322
328
|
const stopSessionBoundSubagents = (): void => {
|
|
329
|
+
// Only abort subagent controllers — NOT foreground team runs.
|
|
330
|
+
// Foreground team runs are bound to the session lifecycle; they will be aborted
|
|
331
|
+
// by cleanupRuntime during session_shutdown.
|
|
323
332
|
for (const controller of foregroundControllers.values()) controller.abort();
|
|
324
333
|
foregroundControllers.clear();
|
|
325
334
|
subagentManager.abortAll("Session switching — foreground subagents cancelled.");
|
|
@@ -361,7 +370,7 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
361
370
|
const ownerGeneration = captureSessionGeneration();
|
|
362
371
|
const controller = new AbortController();
|
|
363
372
|
const key = runId ?? Symbol();
|
|
364
|
-
|
|
373
|
+
foregroundTeamRunControllers.set(key, controller);
|
|
365
374
|
if (ctx.hasUI) {
|
|
366
375
|
setWorkingIndicator(ctx, { frames: ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"], intervalMs: 80 });
|
|
367
376
|
ctx.ui.setWorkingMessage(runId ? `pi-crew foreground run ${runId}...` : "pi-crew foreground run...");
|
|
@@ -382,7 +391,7 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
382
391
|
else logInternalError("register.foreground-run-failure", error, `runId=${runId} context disposed`);
|
|
383
392
|
})
|
|
384
393
|
.finally(() => {
|
|
385
|
-
|
|
394
|
+
foregroundTeamRunControllers.delete(key);
|
|
386
395
|
const ownerCurrent = isContextCurrent(ctx, ownerGeneration);
|
|
387
396
|
if (ctx.hasUI) {
|
|
388
397
|
// Always clear working message/spinner — stale spinners for completed runs are confusing.
|
|
@@ -439,11 +448,15 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
439
448
|
if (preloadTimer) { clearTimeout(preloadTimer); preloadTimer = undefined; }
|
|
440
449
|
closeWatcher(crewWatcher); crewWatcher = undefined;
|
|
441
450
|
stopSessionBoundSubagents();
|
|
451
|
+
// P0 fix: also abort foreground team runs on session shutdown (not on session switch).
|
|
452
|
+
// This is the only place where foreground team run controllers should be aborted.
|
|
453
|
+
for (const controller of foregroundTeamRunControllers.values()) controller.abort();
|
|
454
|
+
foregroundTeamRunControllers.clear();
|
|
442
455
|
crewScheduler?.stop();
|
|
443
456
|
stopAsyncRunNotifier(notifierState);
|
|
444
457
|
|
|
445
458
|
// Best-effort: kill any async background runners that are still alive.
|
|
446
|
-
// Foreground child processes are
|
|
459
|
+
// Foreground child processes (team run tasks) are handled above.
|
|
447
460
|
try {
|
|
448
461
|
for (const manifest of manifestCache.list(50)) {
|
|
449
462
|
if (manifest.async?.pid !== undefined && checkProcessLiveness(manifest.async.pid).alive) {
|
|
@@ -815,12 +828,12 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
815
828
|
} catch { /* older Pi without resources_discover */ }
|
|
816
829
|
|
|
817
830
|
const abortForegroundRun = (runId: string): boolean => {
|
|
818
|
-
const controller =
|
|
831
|
+
const controller = foregroundTeamRunControllers.get(runId);
|
|
819
832
|
if (!controller) return false;
|
|
820
833
|
controller.abort();
|
|
821
834
|
return true;
|
|
822
835
|
};
|
|
823
|
-
registerCompactionGuard(pi, { foregroundControllers });
|
|
836
|
+
registerCompactionGuard(pi, { foregroundControllers, foregroundTeamRunControllers });
|
|
824
837
|
|
|
825
838
|
// Phase 1.4: Permission gate for destructive team actions.
|
|
826
839
|
// AGENTS.md requires confirm=true for management deletes.
|
|
@@ -4,6 +4,7 @@ import type { ArtifactDescriptor, TeamRunManifest } from "../../state/types.ts";
|
|
|
4
4
|
|
|
5
5
|
export interface RegisterCompactionGuardOptions {
|
|
6
6
|
foregroundControllers: Map<string | symbol, AbortController>;
|
|
7
|
+
foregroundTeamRunControllers: Map<string | symbol, AbortController>;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
const TRIGGER_RATIO = 0.75;
|
|
@@ -95,7 +96,7 @@ export function registerCompactionGuard(pi: ExtensionAPI, options: RegisterCompa
|
|
|
95
96
|
|
|
96
97
|
// Phase 1.2: Defer compaction during foreground runs unless context is critically full.
|
|
97
98
|
pi.on("session_before_compact", async (_event, ctx) => {
|
|
98
|
-
if (options.foregroundControllers.size === 0) return;
|
|
99
|
+
if (options.foregroundControllers.size === 0 && options.foregroundTeamRunControllers.size === 0) return;
|
|
99
100
|
const ratio = usageRatio(ctx);
|
|
100
101
|
if (ratio !== undefined && ratio >= HARD_RATIO) {
|
|
101
102
|
ctx.ui.notify("Compaction allowed despite foreground run: context is critically full", "warning");
|
|
@@ -109,14 +110,15 @@ export function registerCompactionGuard(pi: ExtensionAPI, options: RegisterCompa
|
|
|
109
110
|
// Phase 2.1: Proactive compaction with dynamic threshold based on model context window.
|
|
110
111
|
pi.on("turn_end", (_event, ctx) => {
|
|
111
112
|
if (compactionInProgress) return;
|
|
112
|
-
|
|
113
|
+
const hasActiveForeground = options.foregroundControllers.size > 0 || options.foregroundTeamRunControllers.size > 0;
|
|
114
|
+
if (!hasActiveForeground && pendingCompactReason) {
|
|
113
115
|
pendingCompactReason = null;
|
|
114
116
|
startCompact(ctx, "deferred");
|
|
115
117
|
return;
|
|
116
118
|
}
|
|
117
119
|
const ratio = usageRatio(ctx);
|
|
118
120
|
if (ratio === undefined || ratio < TRIGGER_RATIO) return;
|
|
119
|
-
if (
|
|
121
|
+
if (hasActiveForeground) {
|
|
120
122
|
pendingCompactReason = "threshold-during-foreground-run";
|
|
121
123
|
return;
|
|
122
124
|
}
|