@ian-pascoe/pi-minimal-subagents 0.8.0 → 0.9.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.
package/package.json
CHANGED
|
@@ -570,7 +570,7 @@ export class MinimalSubagentsCoordinator {
|
|
|
570
570
|
const runtime = this.runtimes.get(agent.agent_id);
|
|
571
571
|
try {
|
|
572
572
|
if (agent.active_turn_id) await this.cancelActiveTurn(agent);
|
|
573
|
-
runtime?.dispose();
|
|
573
|
+
await runtime?.dispose();
|
|
574
574
|
this.runtimes.delete(agent.agent_id);
|
|
575
575
|
if (agent.session_file) {
|
|
576
576
|
await this.dependencies.sessions.trashSession(agent);
|
|
@@ -622,7 +622,7 @@ export class MinimalSubagentsCoordinator {
|
|
|
622
622
|
await Promise.allSettled(
|
|
623
623
|
abandonedRuntimes.map((runtime) => (runtime.isRunning ? runtime.abort() : Promise.resolve())),
|
|
624
624
|
);
|
|
625
|
-
for (const runtime of abandonedRuntimes) runtime.dispose();
|
|
625
|
+
for (const runtime of abandonedRuntimes) await runtime.dispose();
|
|
626
626
|
this.runtimes.clear();
|
|
627
627
|
this.runtimeInitializations.clear();
|
|
628
628
|
this.pendingAgentIds.clear();
|
|
@@ -892,7 +892,7 @@ export class MinimalSubagentsCoordinator {
|
|
|
892
892
|
await Promise.allSettled(this.runtimeInitializations.values());
|
|
893
893
|
await Promise.allSettled(this.backgroundOperations);
|
|
894
894
|
await Promise.allSettled(this.recipientQueues.values());
|
|
895
|
-
for (const runtime of this.runtimes.values()) runtime.dispose();
|
|
895
|
+
for (const runtime of this.runtimes.values()) await runtime.dispose();
|
|
896
896
|
this.runtimes.clear();
|
|
897
897
|
}
|
|
898
898
|
|
|
@@ -910,7 +910,7 @@ export class MinimalSubagentsCoordinator {
|
|
|
910
910
|
const runtime = await this.ensureRuntime(agent);
|
|
911
911
|
if (this.agents.get(agentId) !== agent || agent.active_turn_id !== turnId) {
|
|
912
912
|
if (!this.agents.has(agentId) || !this.acceptingOperations) {
|
|
913
|
-
runtime.dispose();
|
|
913
|
+
await runtime.dispose();
|
|
914
914
|
this.runtimes.delete(agentId);
|
|
915
915
|
}
|
|
916
916
|
return;
|
|
@@ -943,13 +943,13 @@ export class MinimalSubagentsCoordinator {
|
|
|
943
943
|
const openingForTurn = agent.active_turn_id !== undefined;
|
|
944
944
|
const initialization = this.dependencies.sessions
|
|
945
945
|
.openRuntime(agent)
|
|
946
|
-
.then((runtime) => {
|
|
946
|
+
.then(async (runtime) => {
|
|
947
947
|
if (this.agents.get(agent.agent_id) !== agent) {
|
|
948
|
-
runtime.dispose();
|
|
948
|
+
await runtime.dispose();
|
|
949
949
|
throw new Error(`Minimal subagents runtime replaced while opening ${agent.agent_id}`);
|
|
950
950
|
}
|
|
951
951
|
if (!runtime.sessionLeafId) {
|
|
952
|
-
runtime.dispose();
|
|
952
|
+
await runtime.dispose();
|
|
953
953
|
throw new Error(
|
|
954
954
|
`Minimal subagents session restoration: no selected session leaf for ${agent.agent_id}`,
|
|
955
955
|
);
|
|
@@ -80,6 +80,7 @@ import { MinimalSubagentsUiController } from "./minimal-subagents-ui.js";
|
|
|
80
80
|
import type {
|
|
81
81
|
AgentSessionFactory,
|
|
82
82
|
CallerSnapshot,
|
|
83
|
+
ChildSessionObserver,
|
|
83
84
|
CoordinatorNotification,
|
|
84
85
|
ForkSnapshot,
|
|
85
86
|
PersistedAgent,
|
|
@@ -585,6 +586,19 @@ export class MinimalSubagentsLifecycleController {
|
|
|
585
586
|
projectTrusted: context.isProjectTrusted(),
|
|
586
587
|
maxSubagentDepth: minimalSubagentsConfig.maxSubagentDepth,
|
|
587
588
|
onChildSessionActivity: () => activeCoordinator?.scheduleDeliveryReconciliation(),
|
|
589
|
+
observeSession: (session, agentId, resourceInputs) => {
|
|
590
|
+
let observer: ChildSessionObserver | undefined;
|
|
591
|
+
this.pi.events.emit("pi-minimal-subagents:observe-session", {
|
|
592
|
+
rootSessionId,
|
|
593
|
+
agentId,
|
|
594
|
+
session,
|
|
595
|
+
resourceInputs,
|
|
596
|
+
attach: (attached: ChildSessionObserver) => {
|
|
597
|
+
observer = attached;
|
|
598
|
+
},
|
|
599
|
+
});
|
|
600
|
+
return observer;
|
|
601
|
+
},
|
|
588
602
|
getCoordinatorTools: (callerId) => {
|
|
589
603
|
const coordinator = requireActiveCoordinator();
|
|
590
604
|
return createCoordinatorToolDefinitions({
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
SettingsManager,
|
|
18
18
|
sessionEntryToContextMessages,
|
|
19
19
|
type AgentSessionEvent,
|
|
20
|
+
type Extension,
|
|
20
21
|
type SessionEntry,
|
|
21
22
|
type ToolDefinition,
|
|
22
23
|
} from "@earendil-works/pi-coding-agent";
|
|
@@ -53,6 +54,7 @@ import { addMinimalSubagentsUsage } from "./minimal-subagents-usage.js";
|
|
|
53
54
|
import type {
|
|
54
55
|
AgentSessionFactory,
|
|
55
56
|
ChildAgentRuntime,
|
|
57
|
+
ChildSessionObserver,
|
|
56
58
|
ChildAgentTranscriptSnapshot,
|
|
57
59
|
CoordinatorMessage,
|
|
58
60
|
PersistedAgent,
|
|
@@ -180,6 +182,15 @@ export interface PiAgentSessionFactoryOptions {
|
|
|
180
182
|
sessionFileTrash?: SessionFileTrashCapability;
|
|
181
183
|
getCoordinatorTools: (callerId: string) => ToolDefinition[];
|
|
182
184
|
onChildSessionActivity?: () => void;
|
|
185
|
+
observeSession?: (
|
|
186
|
+
session: AgentSession,
|
|
187
|
+
agentId: string,
|
|
188
|
+
resourceInputs: {
|
|
189
|
+
agentDir: string;
|
|
190
|
+
extensions: readonly Pick<Extension, "path" | "resolvedPath" | "sourceInfo" | "hidden">[];
|
|
191
|
+
flagValues: ReadonlyMap<string, boolean | string>;
|
|
192
|
+
},
|
|
193
|
+
) => ChildSessionObserver | undefined;
|
|
183
194
|
}
|
|
184
195
|
|
|
185
196
|
/** Build one child prompt using the active delegation depth rather than persisted launch state. */
|
|
@@ -494,7 +505,7 @@ function collectChildTurnOutcome(
|
|
|
494
505
|
error: "No terminal assistant response",
|
|
495
506
|
};
|
|
496
507
|
}
|
|
497
|
-
if (finalAssistant.stopReason === "aborted") {
|
|
508
|
+
if (aborted || finalAssistant.stopReason === "aborted") {
|
|
498
509
|
return {
|
|
499
510
|
status: "cancelled",
|
|
500
511
|
output: assistantText(finalAssistant),
|
|
@@ -554,6 +565,7 @@ class PiChildAgentRuntime implements ChildAgentRuntime {
|
|
|
554
565
|
private readonly modelRuntime: ModelRuntime,
|
|
555
566
|
private readonly modelById: ReadonlyMap<string, Model<any>>,
|
|
556
567
|
onSessionActivity?: () => void,
|
|
568
|
+
private readonly observer?: ChildSessionObserver,
|
|
557
569
|
) {
|
|
558
570
|
// Keep this child-only; AgentSession.setSteeringMode would overwrite the user's global setting.
|
|
559
571
|
session.agent.steeringMode = "all";
|
|
@@ -613,12 +625,16 @@ class PiChildAgentRuntime implements ChildAgentRuntime {
|
|
|
613
625
|
|
|
614
626
|
async abort(): Promise<void> {
|
|
615
627
|
this.aborted = true;
|
|
616
|
-
await this.session.abort();
|
|
628
|
+
await Promise.all([this.observer?.abort(), this.session.abort()]);
|
|
617
629
|
}
|
|
618
630
|
|
|
619
|
-
dispose(): void {
|
|
631
|
+
async dispose(): Promise<void> {
|
|
620
632
|
this.unsubscribe();
|
|
621
|
-
|
|
633
|
+
try {
|
|
634
|
+
await this.observer?.dispose();
|
|
635
|
+
} finally {
|
|
636
|
+
this.session.dispose();
|
|
637
|
+
}
|
|
622
638
|
}
|
|
623
639
|
|
|
624
640
|
getRuntimeProfile(): RuntimeProfile | undefined {
|
|
@@ -715,7 +731,20 @@ class PiChildAgentRuntime implements ChildAgentRuntime {
|
|
|
715
731
|
|
|
716
732
|
private async captureTurn(operation: () => Promise<void>): Promise<RuntimeTurnOutcome> {
|
|
717
733
|
this.aborted = false;
|
|
718
|
-
|
|
734
|
+
this.observer?.beginTurn();
|
|
735
|
+
return captureChildTurnOutcome(
|
|
736
|
+
this.session,
|
|
737
|
+
async () => {
|
|
738
|
+
try {
|
|
739
|
+
await operation();
|
|
740
|
+
if (!this.aborted) await this.observer?.finishTurn();
|
|
741
|
+
} catch (error) {
|
|
742
|
+
await this.observer?.abort();
|
|
743
|
+
throw error;
|
|
744
|
+
}
|
|
745
|
+
},
|
|
746
|
+
() => this.aborted,
|
|
747
|
+
);
|
|
719
748
|
}
|
|
720
749
|
|
|
721
750
|
private async compactImportedContext(
|
|
@@ -1187,6 +1216,18 @@ export class PiAgentSessionFactory implements AgentSessionFactory {
|
|
|
1187
1216
|
modelRuntime,
|
|
1188
1217
|
this.modelById,
|
|
1189
1218
|
this.options.onChildSessionActivity,
|
|
1219
|
+
this.options.observeSession?.(session, agent.agent_id, {
|
|
1220
|
+
agentDir: this.options.agentDir,
|
|
1221
|
+
extensions: resourceLoader
|
|
1222
|
+
.getExtensions()
|
|
1223
|
+
.extensions.map(({ path, resolvedPath, sourceInfo, hidden }) => ({
|
|
1224
|
+
path,
|
|
1225
|
+
resolvedPath,
|
|
1226
|
+
sourceInfo,
|
|
1227
|
+
hidden,
|
|
1228
|
+
})),
|
|
1229
|
+
flagValues: new Map(resourceLoader.getExtensions().runtime.flagValues),
|
|
1230
|
+
}),
|
|
1190
1231
|
);
|
|
1191
1232
|
}
|
|
1192
1233
|
|
|
@@ -224,6 +224,14 @@ export interface CoordinatorMessage {
|
|
|
224
224
|
};
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
/** Optional review work attached by the root and awaited by the existing child owner. */
|
|
228
|
+
export interface ChildSessionObserver {
|
|
229
|
+
beginTurn(): void;
|
|
230
|
+
finishTurn(): Promise<void>;
|
|
231
|
+
abort(): Promise<void>;
|
|
232
|
+
dispose(): Promise<void>;
|
|
233
|
+
}
|
|
234
|
+
|
|
227
235
|
/** Process-local adapter around one SDK-created Pi child session. */
|
|
228
236
|
export interface ChildAgentRuntime {
|
|
229
237
|
readonly sessionLeafId: string | undefined;
|
|
@@ -238,7 +246,7 @@ export interface ChildAgentRuntime {
|
|
|
238
246
|
/** Queue one typed coordinator message into the child session. */
|
|
239
247
|
queueCoordinatorMessage(message: CoordinatorMessage): Promise<void>;
|
|
240
248
|
abort(): Promise<void>;
|
|
241
|
-
dispose(): void
|
|
249
|
+
dispose(): void | Promise<void>;
|
|
242
250
|
/** Return the live Runtime Profile, or undefined when the SDK session has no model. */
|
|
243
251
|
getRuntimeProfile(): RuntimeProfile | undefined;
|
|
244
252
|
/** Return the effective ordinary tools after child extensions apply runtime adapters. */
|