@ian-pascoe/pi-minimal-subagents 0.7.1 → 0.7.2
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
CHANGED
|
@@ -226,8 +226,14 @@ Deleting a child first verifies its session header and persistent identity,
|
|
|
226
226
|
then uses the optional `trash` command when available and falls back to
|
|
227
227
|
unlinking its session file. Deletion prunes pending delivery state and retained
|
|
228
228
|
recent-message projections sourced from the complete deleted subtree. Restore
|
|
229
|
-
and clone perform the same ownership check
|
|
230
|
-
leaf.
|
|
229
|
+
and clone perform the same ownership check against the recorded child-session
|
|
230
|
+
leaf. Restoration loads metadata and validates saved sessions without starting
|
|
231
|
+
child runtimes or their extension services (including MCP Servers). Status,
|
|
232
|
+
saved transcripts, settled-result waits, cancellation of idle children, and
|
|
233
|
+
session management do not start runtimes. A message or an undelivered child-bound
|
|
234
|
+
result opens only its recipient's runtime on demand; saved Delivery Evidence
|
|
235
|
+
prevents already-delivered work from reopening it. Runtime initialization errors
|
|
236
|
+
are reported when that child is first needed.
|
|
231
237
|
|
|
232
238
|
Registry replay and Delivery Evidence are scoped to the Root Agent's active
|
|
233
239
|
session-tree branch. Registry writes use V2 records with complete field,
|
package/package.json
CHANGED
|
@@ -126,10 +126,11 @@ export function reconcileCoordinatorToolAccess(
|
|
|
126
126
|
activeToolNames: readonly string[],
|
|
127
127
|
enabled: boolean,
|
|
128
128
|
): string[] {
|
|
129
|
-
const
|
|
130
|
-
|
|
129
|
+
const missing = new Set<string>(COORDINATOR_TOOL_NAMES);
|
|
130
|
+
const retained = activeToolNames.filter(
|
|
131
|
+
(toolName) => !COORDINATOR_TOOL_NAME_SET.has(toolName) || (enabled && missing.delete(toolName)),
|
|
131
132
|
);
|
|
132
|
-
return enabled ? [...
|
|
133
|
+
return enabled ? [...retained, ...missing] : retained;
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
/** Resolve branch state over settings and include read-only Coordinator Tool activation. */
|
|
@@ -655,19 +655,6 @@ export class MinimalSubagentsCoordinator {
|
|
|
655
655
|
});
|
|
656
656
|
continue;
|
|
657
657
|
}
|
|
658
|
-
const runtime = await this.dependencies.sessions.openRuntime(agent);
|
|
659
|
-
if (restoreEpoch !== this.lifecycleEpoch) {
|
|
660
|
-
runtime.dispose();
|
|
661
|
-
return;
|
|
662
|
-
}
|
|
663
|
-
if (!runtime.sessionLeafId) {
|
|
664
|
-
runtime.dispose();
|
|
665
|
-
throw new Error(
|
|
666
|
-
`Minimal subagents session restoration: no selected session leaf for ${agent.agent_id}`,
|
|
667
|
-
);
|
|
668
|
-
}
|
|
669
|
-
this.runtimes.set(agent.agent_id, runtime);
|
|
670
|
-
agent.session_leaf_id = runtime.sessionLeafId;
|
|
671
658
|
agent.availability = "available";
|
|
672
659
|
if (previousAvailability !== "available")
|
|
673
660
|
agent.latest_activity_at = this.now().toISOString();
|
|
@@ -926,6 +913,7 @@ export class MinimalSubagentsCoordinator {
|
|
|
926
913
|
new Error(agent.clone_error ?? `No persistent session exists for ${agent.agent_id}`),
|
|
927
914
|
);
|
|
928
915
|
}
|
|
916
|
+
const openingForTurn = agent.active_turn_id !== undefined;
|
|
929
917
|
const initialization = this.dependencies.sessions
|
|
930
918
|
.openRuntime(agent)
|
|
931
919
|
.then((runtime) => {
|
|
@@ -933,9 +921,29 @@ export class MinimalSubagentsCoordinator {
|
|
|
933
921
|
runtime.dispose();
|
|
934
922
|
throw new Error(`Minimal subagents runtime replaced while opening ${agent.agent_id}`);
|
|
935
923
|
}
|
|
924
|
+
if (!runtime.sessionLeafId) {
|
|
925
|
+
runtime.dispose();
|
|
926
|
+
throw new Error(
|
|
927
|
+
`Minimal subagents session restoration: no selected session leaf for ${agent.agent_id}`,
|
|
928
|
+
);
|
|
929
|
+
}
|
|
936
930
|
this.runtimes.set(agent.agent_id, runtime);
|
|
931
|
+
agent.session_leaf_id = runtime.sessionLeafId;
|
|
937
932
|
return runtime;
|
|
938
933
|
})
|
|
934
|
+
.catch((error) => {
|
|
935
|
+
if (!openingForTurn && this.agents.get(agent.agent_id) === agent) {
|
|
936
|
+
agent.availability = "unavailable";
|
|
937
|
+
agent.latest_activity_at = this.now().toISOString();
|
|
938
|
+
agent.unavailable_reason = error instanceof Error ? error.message : String(error);
|
|
939
|
+
this.dependencies.notify?.({
|
|
940
|
+
type: "unavailable",
|
|
941
|
+
agentId: agent.agent_id,
|
|
942
|
+
message: `${agent.agent_id} unavailable: ${agent.unavailable_reason}`,
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
throw error;
|
|
946
|
+
})
|
|
939
947
|
.finally(() => {
|
|
940
948
|
if (this.runtimeInitializations.get(agent.agent_id) === initialization) {
|
|
941
949
|
this.runtimeInitializations.delete(agent.agent_id);
|
|
@@ -1307,6 +1315,7 @@ export class MinimalSubagentsCoordinator {
|
|
|
1307
1315
|
}
|
|
1308
1316
|
const target = this.requireUsableAgent(targetId, "message");
|
|
1309
1317
|
const runtime = this.runtimes.get(targetId) ?? (await this.ensureRuntime(target));
|
|
1318
|
+
this.assertAccepting();
|
|
1310
1319
|
if (!isCurrentDelivery()) {
|
|
1311
1320
|
throw new Error("Minimal subagents delivery abandoned after session branch change");
|
|
1312
1321
|
}
|
|
@@ -1430,34 +1439,45 @@ export class MinimalSubagentsCoordinator {
|
|
|
1430
1439
|
}
|
|
1431
1440
|
|
|
1432
1441
|
private hasDeliveryEvidence(delivery: PersistedDelivery): boolean {
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
);
|
|
1438
|
-
}
|
|
1439
|
-
return (
|
|
1440
|
-
this.runtimes
|
|
1441
|
-
.get(delivery.destination_agent_id)
|
|
1442
|
-
?.hasDeliveryEvidence(delivery.source_agent_id, delivery.source_turn_id) ?? false
|
|
1442
|
+
return this.hasRecipientDeliveryEvidence(
|
|
1443
|
+
delivery.destination_agent_id,
|
|
1444
|
+
delivery.source_agent_id,
|
|
1445
|
+
delivery.source_turn_id,
|
|
1443
1446
|
);
|
|
1444
1447
|
}
|
|
1445
1448
|
|
|
1446
1449
|
private hasCoordinationDeliveryEvidence(delivery: PersistedCoordinationDelivery): boolean {
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1450
|
+
return this.hasRecipientDeliveryEvidence(
|
|
1451
|
+
delivery.destination_agent_id,
|
|
1452
|
+
delivery.message.details.source_agent_id,
|
|
1453
|
+
delivery.message.details.source_turn_id,
|
|
1454
|
+
delivery.delivery_id,
|
|
1455
|
+
);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
private hasRecipientDeliveryEvidence(
|
|
1459
|
+
targetId: string,
|
|
1460
|
+
sourceAgentId: string,
|
|
1461
|
+
sourceTurnId: string,
|
|
1462
|
+
deliveryId?: string,
|
|
1463
|
+
): boolean {
|
|
1464
|
+
const endpoint = targetId === "root" ? this.dependencies.root : this.runtimes.get(targetId);
|
|
1465
|
+
if (endpoint) return endpoint.hasDeliveryEvidence(sourceAgentId, sourceTurnId, deliveryId);
|
|
1466
|
+
const agent = this.agents.get(targetId);
|
|
1467
|
+
if (!agent) return false;
|
|
1468
|
+
try {
|
|
1469
|
+
return (
|
|
1470
|
+
this.dependencies.sessions.hasDeliveryEvidence?.(
|
|
1471
|
+
agent,
|
|
1472
|
+
sourceAgentId,
|
|
1473
|
+
sourceTurnId,
|
|
1474
|
+
deliveryId,
|
|
1475
|
+
) ?? false
|
|
1454
1476
|
);
|
|
1477
|
+
} catch {
|
|
1478
|
+
// Unverified sessions cannot prove delivery; runtime opening reports their failure.
|
|
1479
|
+
return false;
|
|
1455
1480
|
}
|
|
1456
|
-
return (
|
|
1457
|
-
this.runtimes
|
|
1458
|
-
.get(delivery.destination_agent_id)
|
|
1459
|
-
?.hasDeliveryEvidence(sourceAgentId, sourceTurnId, delivery.delivery_id) ?? false
|
|
1460
|
-
);
|
|
1461
1481
|
}
|
|
1462
1482
|
|
|
1463
1483
|
private settleDelivery(delivery: PersistedDelivery): void {
|
|
@@ -1518,7 +1538,9 @@ export class MinimalSubagentsCoordinator {
|
|
|
1518
1538
|
capability_ceiling: [...agent.capability_ceiling],
|
|
1519
1539
|
spawn_entry_id: agent.spawn_entry_id,
|
|
1520
1540
|
recent_messages: structuredClone(agent.recent_messages),
|
|
1521
|
-
recent_activity: buildRecentAgentActivity(
|
|
1541
|
+
recent_activity: buildRecentAgentActivity(
|
|
1542
|
+
runtime?.snapshotActivityMessages() ?? this.inspectTranscript(agent.agent_id).messages,
|
|
1543
|
+
),
|
|
1522
1544
|
latest_result: agent.latest_result ? structuredClone(agent.latest_result) : undefined,
|
|
1523
1545
|
missing_dependencies: [...agent.missing_dependencies],
|
|
1524
1546
|
unavailable_reason: agent.unavailable_reason,
|
|
@@ -826,24 +826,49 @@ export class PiAgentSessionFactory implements AgentSessionFactory {
|
|
|
826
826
|
stat.ctimeMs,
|
|
827
827
|
]);
|
|
828
828
|
if (this.savedTranscript?.key === key) return this.savedTranscript.snapshot;
|
|
829
|
+
const manager = this.readSession(agent);
|
|
830
|
+
const snapshot = selectChildAgentTranscript(
|
|
831
|
+
manager.getBranch(agent.session_leaf_id).flatMap(sessionEntryToContextMessages),
|
|
832
|
+
);
|
|
833
|
+
this.savedTranscript = { key, snapshot };
|
|
834
|
+
return snapshot;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/** Inspect durable evidence without starting child extensions or changing the saved branch. */
|
|
838
|
+
hasDeliveryEvidence(
|
|
839
|
+
agent: PersistedAgent,
|
|
840
|
+
sourceAgentId: string,
|
|
841
|
+
sourceTurnId: string,
|
|
842
|
+
deliveryId?: string,
|
|
843
|
+
): boolean {
|
|
844
|
+
return findDeliveryEvidence(
|
|
845
|
+
this.readSession(agent).getBranch(agent.session_leaf_id),
|
|
846
|
+
sourceAgentId,
|
|
847
|
+
sourceTurnId,
|
|
848
|
+
deliveryId,
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
private readSession(agent: PersistedAgent): SessionManager {
|
|
853
|
+
if (!agent.session_file || !agent.session_id || !agent.session_leaf_id) {
|
|
854
|
+
throw new Error(`Child Session Position is unavailable for ${agent.agent_id}.`);
|
|
855
|
+
}
|
|
856
|
+
const sessionFile = canonicalPath(agent.session_file);
|
|
829
857
|
const entries = parseSessionEntries(readFileSync(sessionFile, "utf8"));
|
|
830
858
|
if (entries[0]?.type !== "session")
|
|
831
859
|
throw new Error(`Invalid child session file: ${sessionFile}`);
|
|
832
860
|
// SessionManager.open can migrate/rewrite files; an in-memory reader cannot write them.
|
|
833
861
|
const manager = SessionManager.inMemory(this.options.cwd, undefined, entries);
|
|
834
862
|
verifyChildSessionIdentity(manager, agent, this.options.rootSessionId);
|
|
835
|
-
|
|
836
|
-
manager.getBranch(agent.session_leaf_id).flatMap(sessionEntryToContextMessages),
|
|
837
|
-
);
|
|
838
|
-
this.savedTranscript = { key, snapshot };
|
|
839
|
-
return snapshot;
|
|
863
|
+
return manager;
|
|
840
864
|
}
|
|
841
865
|
|
|
842
866
|
resolveLaunchMissingDependencies(agent: PersistedAgent): Promise<string[]> {
|
|
843
867
|
return this.findMissingDependencies(agent, false);
|
|
844
868
|
}
|
|
845
869
|
|
|
846
|
-
resolveRestorationMissingDependencies(agent: PersistedAgent): Promise<string[]> {
|
|
870
|
+
async resolveRestorationMissingDependencies(agent: PersistedAgent): Promise<string[]> {
|
|
871
|
+
this.readSession(agent);
|
|
847
872
|
return this.findMissingDependencies(agent, this.options.modelScopeRestricted);
|
|
848
873
|
}
|
|
849
874
|
|
|
@@ -261,6 +261,13 @@ export interface AgentSessionFactory {
|
|
|
261
261
|
openRuntime(agent: PersistedAgent): Promise<ChildAgentRuntime>;
|
|
262
262
|
/** Read verified saved history independently of runtime restoration dependencies. */
|
|
263
263
|
readTranscript?(agent: PersistedAgent): ChildAgentTranscriptSnapshot;
|
|
264
|
+
/** Read verified selected-branch evidence without opening a runtime. */
|
|
265
|
+
hasDeliveryEvidence?(
|
|
266
|
+
agent: PersistedAgent,
|
|
267
|
+
sourceAgentId: string,
|
|
268
|
+
sourceTurnId: string,
|
|
269
|
+
deliveryId?: string,
|
|
270
|
+
): boolean;
|
|
264
271
|
resolveLaunchMissingDependencies(agent: PersistedAgent): Promise<string[]>;
|
|
265
272
|
resolveRestorationMissingDependencies(agent: PersistedAgent): Promise<string[]>;
|
|
266
273
|
resolveThinkingLevel(modelId: string, requested: ThinkingLevel): ThinkingLevel;
|