@ian-pascoe/pi-minimal-subagents 0.2.0 → 0.2.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/package.json +1 -1
- package/src/minimal-subagents-capabilities.ts +1 -12
- package/src/minimal-subagents-config.ts +5 -16
- package/src/minimal-subagents-coordinator.ts +7 -29
- package/src/minimal-subagents-delivery-ledger.ts +13 -21
- package/src/minimal-subagents-registry.ts +1 -24
- package/src/minimal-subagents-render-contract.ts +10 -32
- package/src/minimal-subagents-rendering.ts +28 -123
- package/src/minimal-subagents-sessions.ts +20 -38
- package/src/minimal-subagents-tools.ts +4 -25
- package/src/minimal-subagents-types.ts +2 -13
package/package.json
CHANGED
|
@@ -36,18 +36,7 @@ export function buildEligibleModelIds(input: {
|
|
|
36
36
|
const source = scopeConfigured
|
|
37
37
|
? input.scopedModels.map((entry) => entry.model)
|
|
38
38
|
: input.availableModels;
|
|
39
|
-
|
|
40
|
-
const result: string[] = [];
|
|
41
|
-
|
|
42
|
-
for (const model of source) {
|
|
43
|
-
const canonicalId = `${model.provider}/${model.id}`;
|
|
44
|
-
if (!seen.has(canonicalId)) {
|
|
45
|
-
seen.add(canonicalId);
|
|
46
|
-
result.push(canonicalId);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return result;
|
|
39
|
+
return [...new Set(source.map(({ provider, id }) => `${provider}/${id}`))];
|
|
51
40
|
}
|
|
52
41
|
|
|
53
42
|
/** Supplies inherited tools, the ancestor ceiling, and runtime availability for exact tool resolution. */
|
|
@@ -51,8 +51,8 @@ interface MinimalSubagentsSettingsDocument {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
interface MinimalSubagentsConfigInput {
|
|
54
|
-
globalSettings:
|
|
55
|
-
projectSettings:
|
|
54
|
+
globalSettings: MinimalSubagentsSettingsDocumentInput;
|
|
55
|
+
projectSettings: MinimalSubagentsSettingsDocumentInput;
|
|
56
56
|
eligibleModelIds: readonly string[];
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -126,19 +126,8 @@ function parseModelRolesWireValue(value: JsonValue): ModelRolesWireValue {
|
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
function parsePiSettingsDocument(
|
|
130
|
-
settings: MinimalSubagentsSettingsDocumentInput,
|
|
131
|
-
): MinimalSubagentsSettingsDocument {
|
|
132
|
-
if (!Value.Check(SettingsDocumentSchema, settings)) return {};
|
|
133
|
-
const parsed: MinimalSubagentsSettingsDocument = {};
|
|
134
|
-
if (settings.minimalSubagents !== undefined) {
|
|
135
|
-
parsed.minimalSubagents = settings.minimalSubagents;
|
|
136
|
-
}
|
|
137
|
-
return parsed;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
129
|
function readMinimalSubagentsSettings(
|
|
141
|
-
settings:
|
|
130
|
+
settings: MinimalSubagentsSettingsDocumentInput,
|
|
142
131
|
scope: SettingsScope,
|
|
143
132
|
warnings: string[],
|
|
144
133
|
): ParsedMinimalSubagentsSettings {
|
|
@@ -359,8 +348,8 @@ export function resolveMinimalSubagentsSettings(
|
|
|
359
348
|
eligibleModelIds: readonly string[],
|
|
360
349
|
): ResolvedMinimalSubagentsConfig {
|
|
361
350
|
return resolveMinimalSubagentsConfig({
|
|
362
|
-
globalSettings:
|
|
363
|
-
projectSettings:
|
|
351
|
+
globalSettings: settings.getGlobalSettings(),
|
|
352
|
+
projectSettings: settings.getProjectSettings(),
|
|
364
353
|
eligibleModelIds,
|
|
365
354
|
});
|
|
366
355
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
3
2
|
import { assembleImportedContext, contextContainsImages } from "./minimal-subagents-context.js";
|
|
4
3
|
import {
|
|
5
4
|
canAgentContractSpawn,
|
|
@@ -119,7 +118,6 @@ export class MinimalSubagentsCoordinator {
|
|
|
119
118
|
private readonly agents = new Map<string, PersistedAgent>();
|
|
120
119
|
private readonly runtimes = new Map<string, ChildAgentRuntime>();
|
|
121
120
|
private readonly runtimeInitializations = new Map<string, Promise<ChildAgentRuntime>>();
|
|
122
|
-
private readonly importedMessages = new Map<string, AgentMessage[]>();
|
|
123
121
|
private readonly tombstones = new Set<string>();
|
|
124
122
|
private readonly pendingAgentIds = new Set<string>();
|
|
125
123
|
private deliveryLedger: DeliveryLedger = createDeliveryLedger();
|
|
@@ -252,7 +250,6 @@ export class MinimalSubagentsCoordinator {
|
|
|
252
250
|
agent.session_id = identity.sessionId;
|
|
253
251
|
agent.session_leaf_id = identity.sessionLeafId;
|
|
254
252
|
this.agents.set(agentId, agent);
|
|
255
|
-
this.importedMessages.set(agentId, imported.messages);
|
|
256
253
|
this.dependencies.registry.append(
|
|
257
254
|
createRegistryEvent(this.dependencies.registry.rootSessionId, "agent-created", { agent }),
|
|
258
255
|
);
|
|
@@ -505,7 +502,6 @@ export class MinimalSubagentsCoordinator {
|
|
|
505
502
|
result.trashed_session_files.push(agent.session_file);
|
|
506
503
|
}
|
|
507
504
|
this.agents.delete(agent.agent_id);
|
|
508
|
-
this.importedMessages.delete(agent.agent_id);
|
|
509
505
|
this.pruneDeliveryStateForDeletedAgent(agent.agent_id);
|
|
510
506
|
this.pruneRecentMessageProjectionsForDeletedAgent(agent.agent_id);
|
|
511
507
|
this.tombstones.add(agent.agent_id);
|
|
@@ -520,10 +516,7 @@ export class MinimalSubagentsCoordinator {
|
|
|
520
516
|
failedAncestors.add(agent.agent_id);
|
|
521
517
|
if (runtime && agent.session_file) {
|
|
522
518
|
try {
|
|
523
|
-
this.runtimes.set(
|
|
524
|
-
agent.agent_id,
|
|
525
|
-
await this.dependencies.sessions.restoreRuntime(agent),
|
|
526
|
-
);
|
|
519
|
+
this.runtimes.set(agent.agent_id, await this.dependencies.sessions.openRuntime(agent));
|
|
527
520
|
} catch (restoreError) {
|
|
528
521
|
agent.availability = "unavailable";
|
|
529
522
|
agent.unavailable_reason = `Deletion recovery failed: ${
|
|
@@ -559,7 +552,6 @@ export class MinimalSubagentsCoordinator {
|
|
|
559
552
|
for (const runtime of abandonedRuntimes) runtime.dispose();
|
|
560
553
|
this.runtimes.clear();
|
|
561
554
|
this.runtimeInitializations.clear();
|
|
562
|
-
this.importedMessages.clear();
|
|
563
555
|
this.pendingAgentIds.clear();
|
|
564
556
|
this.tombstones.clear();
|
|
565
557
|
this.deliveryLedger = createDeliveryLedger();
|
|
@@ -623,7 +615,7 @@ export class MinimalSubagentsCoordinator {
|
|
|
623
615
|
});
|
|
624
616
|
continue;
|
|
625
617
|
}
|
|
626
|
-
const runtime = await this.dependencies.sessions.
|
|
618
|
+
const runtime = await this.dependencies.sessions.openRuntime(agent);
|
|
627
619
|
if (restoreEpoch !== this.lifecycleEpoch) {
|
|
628
620
|
runtime.dispose();
|
|
629
621
|
return;
|
|
@@ -902,19 +894,14 @@ export class MinimalSubagentsCoordinator {
|
|
|
902
894
|
new Error(agent.clone_error ?? `No persistent session exists for ${agent.agent_id}`),
|
|
903
895
|
);
|
|
904
896
|
}
|
|
905
|
-
const
|
|
906
|
-
|
|
907
|
-
importedMessages
|
|
908
|
-
? this.dependencies.sessions.createRuntime({ agent, importedMessages })
|
|
909
|
-
: this.dependencies.sessions.restoreRuntime(agent)
|
|
910
|
-
)
|
|
897
|
+
const initialization = this.dependencies.sessions
|
|
898
|
+
.openRuntime(agent)
|
|
911
899
|
.then((runtime) => {
|
|
912
900
|
if (this.agents.get(agent.agent_id) !== agent) {
|
|
913
901
|
runtime.dispose();
|
|
914
902
|
throw new Error(`Minimal subagents runtime replaced while opening ${agent.agent_id}`);
|
|
915
903
|
}
|
|
916
904
|
this.runtimes.set(agent.agent_id, runtime);
|
|
917
|
-
this.importedMessages.delete(agent.agent_id);
|
|
918
905
|
return runtime;
|
|
919
906
|
})
|
|
920
907
|
.finally(() => {
|
|
@@ -1791,10 +1778,7 @@ export class MinimalSubagentsCoordinator {
|
|
|
1791
1778
|
message.details.source_agent_id,
|
|
1792
1779
|
message.details.source_turn_id,
|
|
1793
1780
|
);
|
|
1794
|
-
|
|
1795
|
-
const claimPromise = new Promise<void>((resolve) => {
|
|
1796
|
-
releaseClaim = resolve;
|
|
1797
|
-
});
|
|
1781
|
+
const { promise: claimPromise, resolve: releaseClaim } = Promise.withResolvers<void>();
|
|
1798
1782
|
const pending: PendingParentMessage = {
|
|
1799
1783
|
deliveryId: delivery.delivery_id,
|
|
1800
1784
|
message,
|
|
@@ -1914,10 +1898,7 @@ export class MinimalSubagentsCoordinator {
|
|
|
1914
1898
|
}
|
|
1915
1899
|
|
|
1916
1900
|
private createRecipientIdleWait(agentId: string): CancelableWait {
|
|
1917
|
-
|
|
1918
|
-
const promise = new Promise<void>((resolve) => {
|
|
1919
|
-
release = resolve;
|
|
1920
|
-
});
|
|
1901
|
+
const { promise, resolve: release } = Promise.withResolvers<void>();
|
|
1921
1902
|
const waiters = this.recipientIdleWaiters.get(agentId) ?? new Set();
|
|
1922
1903
|
waiters.add(release);
|
|
1923
1904
|
this.recipientIdleWaiters.set(agentId, waiters);
|
|
@@ -1932,10 +1913,7 @@ export class MinimalSubagentsCoordinator {
|
|
|
1932
1913
|
}
|
|
1933
1914
|
|
|
1934
1915
|
private createAutomaticDeliveryClaimWait(deliveryKey: string): CancelableWait {
|
|
1935
|
-
|
|
1936
|
-
const promise = new Promise<void>((resolve) => {
|
|
1937
|
-
release = resolve;
|
|
1938
|
-
});
|
|
1916
|
+
const { promise, resolve: release } = Promise.withResolvers<void>();
|
|
1939
1917
|
const waiters = this.automaticDeliveryClaimWaiters.get(deliveryKey) ?? new Set();
|
|
1940
1918
|
waiters.add(release);
|
|
1941
1919
|
this.automaticDeliveryClaimWaiters.set(deliveryKey, waiters);
|
|
@@ -58,23 +58,13 @@ function deliveryTurnKey(sourceAgentId: string, sourceTurnId: string): string {
|
|
|
58
58
|
return `${sourceAgentId}\u0000${sourceTurnId}`;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
function cloneTerminalDelivery(delivery: PersistedDelivery): PersistedDelivery {
|
|
62
|
-
return structuredClone(delivery);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function cloneCoordinationDelivery(
|
|
66
|
-
delivery: PersistedCoordinationDelivery,
|
|
67
|
-
): PersistedCoordinationDelivery {
|
|
68
|
-
return structuredClone(delivery);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
61
|
function createTransition(
|
|
72
62
|
ledger: DeliveryLedger,
|
|
73
63
|
prunedTerminalDeliveries: readonly PersistedDelivery[] = [],
|
|
74
64
|
): DeliveryLedgerTransition {
|
|
75
65
|
return {
|
|
76
66
|
ledger,
|
|
77
|
-
prunedTerminalDeliveries: prunedTerminalDeliveries.map(
|
|
67
|
+
prunedTerminalDeliveries: prunedTerminalDeliveries.map((delivery) => structuredClone(delivery)),
|
|
78
68
|
};
|
|
79
69
|
}
|
|
80
70
|
|
|
@@ -134,7 +124,7 @@ export function createDeliveryLedger(
|
|
|
134
124
|
): DeliveryLedger {
|
|
135
125
|
const coordinationDeliveries = (snapshot.coordination_deliveries ?? [])
|
|
136
126
|
.filter((delivery) => !delivery.settled)
|
|
137
|
-
.map(
|
|
127
|
+
.map((delivery) => structuredClone(delivery));
|
|
138
128
|
let nextSequence = Math.max(
|
|
139
129
|
1,
|
|
140
130
|
snapshot.next_delivery_sequence ?? 1,
|
|
@@ -146,7 +136,7 @@ export function createDeliveryLedger(
|
|
|
146
136
|
const terminalDeliveries = (snapshot.deliveries ?? [])
|
|
147
137
|
.filter((delivery) => !delivery.settled)
|
|
148
138
|
.map((delivery) => {
|
|
149
|
-
const restored =
|
|
139
|
+
const restored = structuredClone(delivery);
|
|
150
140
|
if (restored.sequence === undefined) restored.sequence = nextSequence++;
|
|
151
141
|
return restored;
|
|
152
142
|
});
|
|
@@ -161,8 +151,10 @@ export function createDeliveryLedger(
|
|
|
161
151
|
/** Serialize pending Delivery Ledger state without exposing mutable internal references. */
|
|
162
152
|
export function deliveryLedgerSnapshot(ledger: DeliveryLedger): DeliveryLedgerSnapshot {
|
|
163
153
|
return {
|
|
164
|
-
deliveries: ledger.terminalDeliveries.map(
|
|
165
|
-
coordination_deliveries: ledger.coordinationDeliveries.map(
|
|
154
|
+
deliveries: ledger.terminalDeliveries.map((delivery) => structuredClone(delivery)),
|
|
155
|
+
coordination_deliveries: ledger.coordinationDeliveries.map((delivery) =>
|
|
156
|
+
structuredClone(delivery),
|
|
157
|
+
),
|
|
166
158
|
wait_claimed_turns: [...ledger.waitClaimedTurns],
|
|
167
159
|
next_delivery_sequence: ledger.nextSequence,
|
|
168
160
|
};
|
|
@@ -194,7 +186,7 @@ export function addTerminalDelivery(
|
|
|
194
186
|
],
|
|
195
187
|
nextSequence: ledger.nextSequence + 1,
|
|
196
188
|
});
|
|
197
|
-
return { ...transition, delivery:
|
|
189
|
+
return { ...transition, delivery: structuredClone(delivery) };
|
|
198
190
|
}
|
|
199
191
|
|
|
200
192
|
/** Replay or update one already-sequenced terminal delivery. */
|
|
@@ -202,7 +194,7 @@ export function upsertTerminalDelivery(
|
|
|
202
194
|
ledger: DeliveryLedger,
|
|
203
195
|
delivery: PersistedDelivery,
|
|
204
196
|
): DeliveryLedgerTransition {
|
|
205
|
-
const restored =
|
|
197
|
+
const restored = structuredClone(delivery);
|
|
206
198
|
const existing = ledger.terminalDeliveries.find(
|
|
207
199
|
(current) =>
|
|
208
200
|
deliveryTurnKey(current.source_agent_id, current.source_turn_id) ===
|
|
@@ -251,7 +243,7 @@ export function addCoordinationDelivery(
|
|
|
251
243
|
],
|
|
252
244
|
nextSequence: ledger.nextSequence + 1,
|
|
253
245
|
},
|
|
254
|
-
delivery:
|
|
246
|
+
delivery: structuredClone(delivery),
|
|
255
247
|
prunedTerminalDeliveries: [],
|
|
256
248
|
};
|
|
257
249
|
}
|
|
@@ -267,7 +259,7 @@ export function upsertCoordinationDelivery(
|
|
|
267
259
|
...ledger.coordinationDeliveries.filter(
|
|
268
260
|
(current) => current.delivery_id !== delivery.delivery_id,
|
|
269
261
|
),
|
|
270
|
-
|
|
262
|
+
structuredClone(delivery),
|
|
271
263
|
],
|
|
272
264
|
nextSequence: Math.max(ledger.nextSequence, delivery.sequence + 1),
|
|
273
265
|
});
|
|
@@ -505,7 +497,7 @@ export function findTerminalDelivery(
|
|
|
505
497
|
const delivery = ledger.terminalDeliveries.find(
|
|
506
498
|
(candidate) => deliveryTurnKey(candidate.source_agent_id, candidate.source_turn_id) === key,
|
|
507
499
|
);
|
|
508
|
-
return delivery ?
|
|
500
|
+
return delivery ? structuredClone(delivery) : undefined;
|
|
509
501
|
}
|
|
510
502
|
|
|
511
503
|
/** Return one pending Coordination Message by stable delivery identity. */
|
|
@@ -516,7 +508,7 @@ export function findCoordinationDelivery(
|
|
|
516
508
|
const delivery = ledger.coordinationDeliveries.find(
|
|
517
509
|
(candidate) => candidate.delivery_id === deliveryId,
|
|
518
510
|
);
|
|
519
|
-
return delivery ?
|
|
511
|
+
return delivery ? structuredClone(delivery) : undefined;
|
|
520
512
|
}
|
|
521
513
|
|
|
522
514
|
/** Report whether one source turn currently has a durable wait claim. */
|
|
@@ -740,12 +740,6 @@ function validateRegistrySnapshot(
|
|
|
740
740
|
}
|
|
741
741
|
const identityError = validateDeliveryIdentity(delivery, version);
|
|
742
742
|
if (identityError) return invalidRegistrySnapshot("invalid-delivery-identity", identityError);
|
|
743
|
-
if (!isOwnedTurnId(delivery.source_agent_id, delivery.source_turn_id)) {
|
|
744
|
-
return invalidRegistrySnapshot(
|
|
745
|
-
"invalid-delivery-identity",
|
|
746
|
-
"terminal source_turn_id must belong to source_agent_id",
|
|
747
|
-
);
|
|
748
|
-
}
|
|
749
743
|
const source = agentsById.get(delivery.source_agent_id);
|
|
750
744
|
if (!source) {
|
|
751
745
|
return invalidRegistrySnapshot(
|
|
@@ -763,17 +757,6 @@ function validateRegistrySnapshot(
|
|
|
763
757
|
for (const delivery of coordinationDeliveries) {
|
|
764
758
|
const identityError = validateCoordinationIdentity(delivery);
|
|
765
759
|
if (identityError) return invalidRegistrySnapshot("invalid-delivery-identity", identityError);
|
|
766
|
-
if (
|
|
767
|
-
!isOwnedTurnId(
|
|
768
|
-
delivery.message.details.source_agent_id,
|
|
769
|
-
delivery.message.details.source_turn_id,
|
|
770
|
-
)
|
|
771
|
-
) {
|
|
772
|
-
return invalidRegistrySnapshot(
|
|
773
|
-
"invalid-delivery-identity",
|
|
774
|
-
"Coordination Message source_turn_id must belong to source_agent_id",
|
|
775
|
-
);
|
|
776
|
-
}
|
|
777
760
|
if (
|
|
778
761
|
!areAdjacentAgents(
|
|
779
762
|
delivery.message.details.source_agent_id,
|
|
@@ -1228,13 +1211,7 @@ export function replayRegistryEntries(
|
|
|
1228
1211
|
}
|
|
1229
1212
|
});
|
|
1230
1213
|
|
|
1231
|
-
|
|
1232
|
-
for (let index = parsedEvents.length - 1; index >= 0; index--) {
|
|
1233
|
-
if (parsedEvents[index]?.event.event === "checkpoint") {
|
|
1234
|
-
checkpointIndex = index;
|
|
1235
|
-
break;
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1214
|
+
const checkpointIndex = parsedEvents.findLastIndex(({ event }) => event.event === "checkpoint");
|
|
1238
1215
|
const checkpointEvent = checkpointIndex >= 0 ? parsedEvents[checkpointIndex]?.event : undefined;
|
|
1239
1216
|
const checkpoint = checkpointEvent?.event === "checkpoint" ? checkpointEvent.snapshot : undefined;
|
|
1240
1217
|
const agents = new Map(
|
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
MessageRenderer,
|
|
4
4
|
ToolDefinition,
|
|
5
5
|
} from "@earendil-works/pi-coding-agent";
|
|
6
|
-
import { Type, type Static
|
|
6
|
+
import { Type, type Static } from "typebox";
|
|
7
7
|
import { Value } from "typebox/value";
|
|
8
8
|
|
|
9
9
|
/** Names of the six coordinator tools with custom transcript renderers. */
|
|
@@ -107,16 +107,6 @@ const ManagementCallArgumentsSchema = Type.Object({
|
|
|
107
107
|
recursive: Type.Optional(Type.Boolean()),
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
-
/** Tool-call contracts keep each coordinator name correlated with its own arguments. */
|
|
111
|
-
export const coordinatorToolCallSchemas = {
|
|
112
|
-
subagent: SpawnCallArgumentsSchema,
|
|
113
|
-
agent_message: MessageCallArgumentsSchema,
|
|
114
|
-
subagent_wait: WaitCallArgumentsSchema,
|
|
115
|
-
subagent_status: StatusCallArgumentsSchema,
|
|
116
|
-
subagent_cancel: ManagementCallArgumentsSchema,
|
|
117
|
-
subagent_delete: ManagementCallArgumentsSchema,
|
|
118
|
-
} as const satisfies { readonly [Name in CoordinatorToolName]: TSchema };
|
|
119
|
-
|
|
120
110
|
const SpawnDetailsSchema = Type.Object({
|
|
121
111
|
agent_id: Type.String(),
|
|
122
112
|
turn_id: Type.String(),
|
|
@@ -183,15 +173,11 @@ const DeleteDetailsSchema = Type.Object({
|
|
|
183
173
|
),
|
|
184
174
|
});
|
|
185
175
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
subagent_status: StatusDetailsSchema,
|
|
192
|
-
subagent_cancel: CancelDetailsSchema,
|
|
193
|
-
subagent_delete: DeleteDetailsSchema,
|
|
194
|
-
} as const satisfies { readonly [Name in CoordinatorToolName]: TSchema };
|
|
176
|
+
const MessageRenderDetailsSchema = Type.Union([
|
|
177
|
+
CurrentMessageDetailsSchema,
|
|
178
|
+
LegacyMessageDetailsSchema,
|
|
179
|
+
]);
|
|
180
|
+
const WaitRenderDetailsSchema = Type.Union([WaitMessageDetailsSchema, WaitTurnDetailsSchema]);
|
|
195
181
|
|
|
196
182
|
export type SpawnCallArguments = Static<typeof SpawnCallArgumentsSchema>;
|
|
197
183
|
export type MessageCallArguments = Static<typeof MessageCallArgumentsSchema>;
|
|
@@ -199,12 +185,8 @@ export type WaitCallArguments = Static<typeof WaitCallArgumentsSchema>;
|
|
|
199
185
|
export type StatusCallArguments = Static<typeof StatusCallArgumentsSchema>;
|
|
200
186
|
export type ManagementCallArguments = Static<typeof ManagementCallArgumentsSchema>;
|
|
201
187
|
export type SpawnRenderDetails = Static<typeof SpawnDetailsSchema>;
|
|
202
|
-
export type MessageRenderDetails = Static<
|
|
203
|
-
|
|
204
|
-
>;
|
|
205
|
-
export type WaitRenderDetails = Static<
|
|
206
|
-
typeof WaitMessageDetailsSchema | typeof WaitTurnDetailsSchema
|
|
207
|
-
>;
|
|
188
|
+
export type MessageRenderDetails = Static<typeof MessageRenderDetailsSchema>;
|
|
189
|
+
export type WaitRenderDetails = Static<typeof WaitRenderDetailsSchema>;
|
|
208
190
|
export type StatusRenderDetails = Static<typeof StatusDetailsSchema>;
|
|
209
191
|
export type CancelRenderDetails = Static<typeof CancelDetailsSchema>;
|
|
210
192
|
export type DeleteRenderDetails = Static<typeof DeleteDetailsSchema>;
|
|
@@ -263,13 +245,9 @@ export function parseCoordinatorToolResult(
|
|
|
263
245
|
case "subagent":
|
|
264
246
|
return Value.Check(SpawnDetailsSchema, details) ? { toolName, details } : undefined;
|
|
265
247
|
case "agent_message":
|
|
266
|
-
return Value.Check(
|
|
267
|
-
? { toolName, details }
|
|
268
|
-
: undefined;
|
|
248
|
+
return Value.Check(MessageRenderDetailsSchema, details) ? { toolName, details } : undefined;
|
|
269
249
|
case "subagent_wait":
|
|
270
|
-
return Value.Check(
|
|
271
|
-
? { toolName, details }
|
|
272
|
-
: undefined;
|
|
250
|
+
return Value.Check(WaitRenderDetailsSchema, details) ? { toolName, details } : undefined;
|
|
273
251
|
case "subagent_status":
|
|
274
252
|
return Value.Check(StatusDetailsSchema, details) ? { toolName, details } : undefined;
|
|
275
253
|
case "subagent_cancel":
|
|
@@ -34,7 +34,6 @@ import {
|
|
|
34
34
|
type RenderStatusAgent,
|
|
35
35
|
type SpawnCallArguments,
|
|
36
36
|
type SpawnRenderDetails,
|
|
37
|
-
type StatusCallArguments,
|
|
38
37
|
type StatusRenderDetails,
|
|
39
38
|
type WaitCallArguments,
|
|
40
39
|
type WaitRenderDetails,
|
|
@@ -259,59 +258,6 @@ function renderManagementToolCall(
|
|
|
259
258
|
);
|
|
260
259
|
}
|
|
261
260
|
|
|
262
|
-
type CoordinatorToolCallRenderers = {
|
|
263
|
-
readonly subagent: (args: SpawnCallArguments, theme: MinimalSubagentsRenderTheme) => Component;
|
|
264
|
-
readonly agent_message: (
|
|
265
|
-
args: MessageCallArguments,
|
|
266
|
-
theme: MinimalSubagentsRenderTheme,
|
|
267
|
-
) => Component;
|
|
268
|
-
readonly subagent_wait: (
|
|
269
|
-
args: WaitCallArguments,
|
|
270
|
-
theme: MinimalSubagentsRenderTheme,
|
|
271
|
-
) => Component;
|
|
272
|
-
readonly subagent_status: (
|
|
273
|
-
args: StatusCallArguments,
|
|
274
|
-
theme: MinimalSubagentsRenderTheme,
|
|
275
|
-
) => Component;
|
|
276
|
-
readonly subagent_cancel: (
|
|
277
|
-
args: ManagementCallArguments,
|
|
278
|
-
theme: MinimalSubagentsRenderTheme,
|
|
279
|
-
) => Component;
|
|
280
|
-
readonly subagent_delete: (
|
|
281
|
-
args: ManagementCallArguments,
|
|
282
|
-
theme: MinimalSubagentsRenderTheme,
|
|
283
|
-
) => Component;
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
const COORDINATOR_TOOL_CALL_RENDERERS = {
|
|
287
|
-
subagent: (args, theme) =>
|
|
288
|
-
new Text(
|
|
289
|
-
`${coordinatorToolCallTitle(theme, "Subagent")} ${theme.fg("accent", args.agent_id ?? "generated")}${coordinatorToolCallPreview(theme, args.task)}`,
|
|
290
|
-
0,
|
|
291
|
-
0,
|
|
292
|
-
),
|
|
293
|
-
agent_message: (args, theme) =>
|
|
294
|
-
new Text(
|
|
295
|
-
`${coordinatorToolCallTitle(theme, "Message")} ${theme.fg("accent", args.agent_id ?? "parent")}${coordinatorToolCallPreview(theme, args.message)}`,
|
|
296
|
-
0,
|
|
297
|
-
0,
|
|
298
|
-
),
|
|
299
|
-
subagent_wait: (args, theme) =>
|
|
300
|
-
new Text(
|
|
301
|
-
`${coordinatorToolCallTitle(theme, "Wait")} ${theme.fg("accent", args.agent_id ?? "agent")}`,
|
|
302
|
-
0,
|
|
303
|
-
0,
|
|
304
|
-
),
|
|
305
|
-
subagent_status: (args, theme) =>
|
|
306
|
-
new Text(
|
|
307
|
-
`${coordinatorToolCallTitle(theme, "Status")} ${theme.fg("accent", args.agent_id ?? "children")}`,
|
|
308
|
-
0,
|
|
309
|
-
0,
|
|
310
|
-
),
|
|
311
|
-
subagent_cancel: (args, theme) => renderManagementToolCall("Cancel", args, theme),
|
|
312
|
-
subagent_delete: (args, theme) => renderManagementToolCall("Delete", args, theme),
|
|
313
|
-
} satisfies CoordinatorToolCallRenderers;
|
|
314
|
-
|
|
315
261
|
function renderSpawnResult(
|
|
316
262
|
details: SpawnRenderDetails,
|
|
317
263
|
options: ToolRenderResultOptions,
|
|
@@ -660,51 +606,6 @@ function renderDeleteResult(
|
|
|
660
606
|
return container;
|
|
661
607
|
}
|
|
662
608
|
|
|
663
|
-
type CoordinatorToolResultRenderers = {
|
|
664
|
-
readonly subagent: (
|
|
665
|
-
details: SpawnRenderDetails,
|
|
666
|
-
options: ToolRenderResultOptions,
|
|
667
|
-
theme: MinimalSubagentsRenderTheme,
|
|
668
|
-
args: SpawnCallArguments,
|
|
669
|
-
) => Component;
|
|
670
|
-
readonly agent_message: (
|
|
671
|
-
details: MessageRenderDetails,
|
|
672
|
-
options: ToolRenderResultOptions,
|
|
673
|
-
theme: MinimalSubagentsRenderTheme,
|
|
674
|
-
args: MessageCallArguments,
|
|
675
|
-
) => Component;
|
|
676
|
-
readonly subagent_wait: (
|
|
677
|
-
details: WaitRenderDetails,
|
|
678
|
-
options: ToolRenderResultOptions,
|
|
679
|
-
theme: MinimalSubagentsRenderTheme,
|
|
680
|
-
args: WaitCallArguments,
|
|
681
|
-
) => Component;
|
|
682
|
-
readonly subagent_status: (
|
|
683
|
-
details: StatusRenderDetails,
|
|
684
|
-
options: ToolRenderResultOptions,
|
|
685
|
-
theme: MinimalSubagentsRenderTheme,
|
|
686
|
-
) => Component;
|
|
687
|
-
readonly subagent_cancel: (
|
|
688
|
-
details: CancelRenderDetails,
|
|
689
|
-
options: ToolRenderResultOptions,
|
|
690
|
-
theme: MinimalSubagentsRenderTheme,
|
|
691
|
-
) => Component;
|
|
692
|
-
readonly subagent_delete: (
|
|
693
|
-
details: DeleteRenderDetails,
|
|
694
|
-
options: ToolRenderResultOptions,
|
|
695
|
-
theme: MinimalSubagentsRenderTheme,
|
|
696
|
-
) => Component;
|
|
697
|
-
};
|
|
698
|
-
|
|
699
|
-
const COORDINATOR_TOOL_RESULT_RENDERERS = {
|
|
700
|
-
subagent: renderSpawnResult,
|
|
701
|
-
agent_message: renderMessageResult,
|
|
702
|
-
subagent_wait: renderWaitResult,
|
|
703
|
-
subagent_status: renderStatusResult,
|
|
704
|
-
subagent_cancel: renderCancelResult,
|
|
705
|
-
subagent_delete: renderDeleteResult,
|
|
706
|
-
} satisfies CoordinatorToolResultRenderers;
|
|
707
|
-
|
|
708
609
|
/** Render one of the six coordinator tool calls with a shared native Pi grammar. */
|
|
709
610
|
export function renderCoordinatorToolCall(
|
|
710
611
|
toolName: CoordinatorToolName,
|
|
@@ -715,17 +616,33 @@ export function renderCoordinatorToolCall(
|
|
|
715
616
|
if (parsed === undefined) return new Text(coordinatorToolCallTitle(theme, toolName), 0, 0);
|
|
716
617
|
switch (parsed.toolName) {
|
|
717
618
|
case "subagent":
|
|
718
|
-
return
|
|
619
|
+
return new Text(
|
|
620
|
+
`${coordinatorToolCallTitle(theme, "Subagent")} ${theme.fg("accent", parsed.args.agent_id ?? "generated")}${coordinatorToolCallPreview(theme, parsed.args.task)}`,
|
|
621
|
+
0,
|
|
622
|
+
0,
|
|
623
|
+
);
|
|
719
624
|
case "agent_message":
|
|
720
|
-
return
|
|
625
|
+
return new Text(
|
|
626
|
+
`${coordinatorToolCallTitle(theme, "Message")} ${theme.fg("accent", parsed.args.agent_id ?? "parent")}${coordinatorToolCallPreview(theme, parsed.args.message)}`,
|
|
627
|
+
0,
|
|
628
|
+
0,
|
|
629
|
+
);
|
|
721
630
|
case "subagent_wait":
|
|
722
|
-
return
|
|
631
|
+
return new Text(
|
|
632
|
+
`${coordinatorToolCallTitle(theme, "Wait")} ${theme.fg("accent", parsed.args.agent_id ?? "agent")}`,
|
|
633
|
+
0,
|
|
634
|
+
0,
|
|
635
|
+
);
|
|
723
636
|
case "subagent_status":
|
|
724
|
-
return
|
|
637
|
+
return new Text(
|
|
638
|
+
`${coordinatorToolCallTitle(theme, "Status")} ${theme.fg("accent", parsed.args.agent_id ?? "children")}`,
|
|
639
|
+
0,
|
|
640
|
+
0,
|
|
641
|
+
);
|
|
725
642
|
case "subagent_cancel":
|
|
726
|
-
return
|
|
643
|
+
return renderManagementToolCall("Cancel", parsed.args, theme);
|
|
727
644
|
case "subagent_delete":
|
|
728
|
-
return
|
|
645
|
+
return renderManagementToolCall("Delete", parsed.args, theme);
|
|
729
646
|
}
|
|
730
647
|
}
|
|
731
648
|
|
|
@@ -743,44 +660,32 @@ export function renderCoordinatorToolResult(
|
|
|
743
660
|
const parsedCall = parseCoordinatorToolCall(toolName, args);
|
|
744
661
|
switch (parsedResult.toolName) {
|
|
745
662
|
case "subagent":
|
|
746
|
-
return
|
|
663
|
+
return renderSpawnResult(
|
|
747
664
|
parsedResult.details,
|
|
748
665
|
options,
|
|
749
666
|
theme,
|
|
750
667
|
parsedCall?.toolName === "subagent" ? parsedCall.args : {},
|
|
751
668
|
);
|
|
752
669
|
case "agent_message":
|
|
753
|
-
return
|
|
670
|
+
return renderMessageResult(
|
|
754
671
|
parsedResult.details,
|
|
755
672
|
options,
|
|
756
673
|
theme,
|
|
757
674
|
parsedCall?.toolName === "agent_message" ? parsedCall.args : {},
|
|
758
675
|
);
|
|
759
676
|
case "subagent_wait":
|
|
760
|
-
return
|
|
677
|
+
return renderWaitResult(
|
|
761
678
|
parsedResult.details,
|
|
762
679
|
options,
|
|
763
680
|
theme,
|
|
764
681
|
parsedCall?.toolName === "subagent_wait" ? parsedCall.args : {},
|
|
765
682
|
);
|
|
766
683
|
case "subagent_status":
|
|
767
|
-
return
|
|
768
|
-
parsedResult.details,
|
|
769
|
-
options,
|
|
770
|
-
theme,
|
|
771
|
-
);
|
|
684
|
+
return renderStatusResult(parsedResult.details, options, theme);
|
|
772
685
|
case "subagent_cancel":
|
|
773
|
-
return
|
|
774
|
-
parsedResult.details,
|
|
775
|
-
options,
|
|
776
|
-
theme,
|
|
777
|
-
);
|
|
686
|
+
return renderCancelResult(parsedResult.details, options, theme);
|
|
778
687
|
case "subagent_delete":
|
|
779
|
-
return
|
|
780
|
-
parsedResult.details,
|
|
781
|
-
options,
|
|
782
|
-
theme,
|
|
783
|
-
);
|
|
688
|
+
return renderDeleteResult(parsedResult.details, options, theme);
|
|
784
689
|
}
|
|
785
690
|
}
|
|
786
691
|
|
|
@@ -53,7 +53,6 @@ import type {
|
|
|
53
53
|
PersistedAgent,
|
|
54
54
|
PersistedSessionIdentity,
|
|
55
55
|
ProjectContextMode,
|
|
56
|
-
RuntimeCreationRequest,
|
|
57
56
|
RuntimeProfile,
|
|
58
57
|
RuntimeTurnOutcome,
|
|
59
58
|
} from "./minimal-subagents-types.js";
|
|
@@ -203,12 +202,14 @@ function findLatestChildSessionRecord<TRecordSchema extends TSchema>(
|
|
|
203
202
|
customType: string,
|
|
204
203
|
schema: TRecordSchema,
|
|
205
204
|
): Static<TRecordSchema> | undefined {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
205
|
+
return entries.findLast(
|
|
206
|
+
(
|
|
207
|
+
entry,
|
|
208
|
+
): entry is Extract<SessionEntry, { type: "custom" }> & {
|
|
209
|
+
data: Static<TRecordSchema>;
|
|
210
|
+
} =>
|
|
211
|
+
entry.type === "custom" && entry.customType === customType && Value.Check(schema, entry.data),
|
|
212
|
+
)?.data;
|
|
212
213
|
}
|
|
213
214
|
|
|
214
215
|
function findLatestForkGeneration(
|
|
@@ -247,18 +248,17 @@ function findCurrentForkOwnership(
|
|
|
247
248
|
entries: ReturnType<SessionManager["getBranch"]>,
|
|
248
249
|
cloneSessionId: string,
|
|
249
250
|
): ForkOwnershipRecord | undefined {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
251
|
+
return entries.findLast(
|
|
252
|
+
(
|
|
253
|
+
entry,
|
|
254
|
+
): entry is Extract<SessionEntry, { type: "custom" }> & {
|
|
255
|
+
data: ForkOwnershipRecord;
|
|
256
|
+
} =>
|
|
257
|
+
entry.type === "custom" &&
|
|
258
|
+
entry.customType === FORK_OWNERSHIP_ENTRY_TYPE &&
|
|
255
259
|
Value.Check(ForkOwnershipRecordSchema, entry.data) &&
|
|
256
|
-
entry.data.clone_session_id === cloneSessionId
|
|
257
|
-
|
|
258
|
-
return entry.data;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
return undefined;
|
|
260
|
+
entry.data.clone_session_id === cloneSessionId,
|
|
261
|
+
)?.data;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
function verifyForkCloneProvenance(
|
|
@@ -527,17 +527,6 @@ class PiChildAgentRuntime implements ChildAgentRuntime {
|
|
|
527
527
|
});
|
|
528
528
|
}
|
|
529
529
|
|
|
530
|
-
get sessionFile(): string {
|
|
531
|
-
const sessionFile = this.session.sessionFile;
|
|
532
|
-
if (!sessionFile)
|
|
533
|
-
throw new Error("Minimal subagents child runtime lost its persistent session file");
|
|
534
|
-
return sessionFile;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
get sessionId(): string {
|
|
538
|
-
return this.session.sessionId;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
530
|
get sessionLeafId(): string | undefined {
|
|
542
531
|
return this.session.sessionManager.getLeafId() ?? undefined;
|
|
543
532
|
}
|
|
@@ -720,14 +709,6 @@ export class PiAgentSessionFactory implements AgentSessionFactory {
|
|
|
720
709
|
});
|
|
721
710
|
}
|
|
722
711
|
|
|
723
|
-
createRuntime(request: RuntimeCreationRequest): Promise<ChildAgentRuntime> {
|
|
724
|
-
return this.openRuntime(request.agent);
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
restoreRuntime(agent: PersistedAgent): Promise<ChildAgentRuntime> {
|
|
728
|
-
return this.openRuntime(agent);
|
|
729
|
-
}
|
|
730
|
-
|
|
731
712
|
resolveLaunchMissingDependencies(agent: PersistedAgent): Promise<string[]> {
|
|
732
713
|
return this.findMissingDependencies(agent, false);
|
|
733
714
|
}
|
|
@@ -967,7 +948,8 @@ export class PiAgentSessionFactory implements AgentSessionFactory {
|
|
|
967
948
|
return discovery;
|
|
968
949
|
}
|
|
969
950
|
|
|
970
|
-
|
|
951
|
+
/** Open one verified persisted Child Agent runtime for launch or restoration. */
|
|
952
|
+
async openRuntime(agent: PersistedAgent): Promise<ChildAgentRuntime> {
|
|
971
953
|
if (!agent.session_file)
|
|
972
954
|
throw new Error(`Minimal subagents restore: ${agent.agent_id} has no session file`);
|
|
973
955
|
const model = this.modelById.get(agent.launch_contract.model);
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
type ToolDefinition,
|
|
10
10
|
type ToolRenderResultOptions,
|
|
11
11
|
} from "@earendil-works/pi-coding-agent";
|
|
12
|
-
import type { Static } from "typebox";
|
|
13
12
|
import type { MinimalSubagentsCoordinator } from "./minimal-subagents-coordinator.js";
|
|
14
13
|
import type { MinimalSubagentsModelRole } from "./minimal-subagents-config.js";
|
|
15
14
|
import {
|
|
@@ -53,27 +52,6 @@ export interface CoordinatorToolDefinitionOptions {
|
|
|
53
52
|
onAttention?: (message: string) => void;
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
/** Arguments consumed by the wait tool's narrow coordinator execution seam. */
|
|
57
|
-
export type CoordinatorWaitToolParameters = Static<
|
|
58
|
-
ReturnType<typeof createCoordinatorToolSchemas>["subagent_wait"]
|
|
59
|
-
>;
|
|
60
|
-
|
|
61
|
-
/** Forward one typed wait-tool request without requiring an unrelated Pi execution context. */
|
|
62
|
-
export function executeCoordinatorWaitTool(
|
|
63
|
-
coordinator: Pick<CoordinatorToolOperations, "wait">,
|
|
64
|
-
callerId: string,
|
|
65
|
-
parameters: CoordinatorWaitToolParameters,
|
|
66
|
-
signal: AbortSignal | undefined,
|
|
67
|
-
): Promise<WaitResult> {
|
|
68
|
-
return coordinator.wait(
|
|
69
|
-
callerId,
|
|
70
|
-
parameters.agent_id,
|
|
71
|
-
parameters.timeout_ms,
|
|
72
|
-
signal,
|
|
73
|
-
parameters.turn_id,
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
55
|
function buildModelRolePromptGuidelines(
|
|
78
56
|
modelRoles: readonly MinimalSubagentsModelRole[],
|
|
79
57
|
): string[] | undefined {
|
|
@@ -243,11 +221,12 @@ export function createCoordinatorToolDefinitions(
|
|
|
243
221
|
waitingInterval.unref?.();
|
|
244
222
|
try {
|
|
245
223
|
return await runCoordinatorToolActivity(options, async () => {
|
|
246
|
-
const result = await
|
|
247
|
-
options.coordinator,
|
|
224
|
+
const result = await options.coordinator.wait(
|
|
248
225
|
options.callerId,
|
|
249
|
-
parameters,
|
|
226
|
+
parameters.agent_id,
|
|
227
|
+
parameters.timeout_ms,
|
|
250
228
|
signal,
|
|
229
|
+
parameters.turn_id,
|
|
251
230
|
);
|
|
252
231
|
return {
|
|
253
232
|
...structuredToolResult(result),
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import type { AgentMessage, ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
2
2
|
import type { Usage } from "@earendil-works/pi-ai";
|
|
3
3
|
|
|
4
|
-
/** Canonical path-like identity for one persistent subagent. */
|
|
5
|
-
export type AgentId = string & { readonly __agentId: unique symbol };
|
|
6
|
-
|
|
7
4
|
/** Stable identity for one prompt and its complete assistant/tool loop. */
|
|
8
5
|
export type TurnId = string & { readonly __turnId: unique symbol };
|
|
9
6
|
|
|
@@ -190,8 +187,6 @@ export interface CoordinatorMessage {
|
|
|
190
187
|
|
|
191
188
|
/** Process-local adapter around one SDK-created Pi child session. */
|
|
192
189
|
export interface ChildAgentRuntime {
|
|
193
|
-
readonly sessionFile: string;
|
|
194
|
-
readonly sessionId: string;
|
|
195
190
|
readonly sessionLeafId: string | undefined;
|
|
196
191
|
readonly isRunning: boolean;
|
|
197
192
|
runPrompt(
|
|
@@ -219,17 +214,11 @@ export interface PersistedSessionIdentity {
|
|
|
219
214
|
sessionLeafId?: string;
|
|
220
215
|
}
|
|
221
216
|
|
|
222
|
-
/** Combines a persisted agent record with first-launch imported context. */
|
|
223
|
-
export interface RuntimeCreationRequest {
|
|
224
|
-
agent: PersistedAgent;
|
|
225
|
-
importedMessages: AgentMessage[];
|
|
226
|
-
}
|
|
227
|
-
|
|
228
217
|
/** Pi-specific session operations injected into the pure coordinator. */
|
|
229
218
|
export interface AgentSessionFactory {
|
|
230
219
|
createIdentity(agent: PersistedAgent, importedMessages: AgentMessage[]): PersistedSessionIdentity;
|
|
231
|
-
|
|
232
|
-
|
|
220
|
+
/** Open one verified persisted Child Agent runtime for launch or restoration. */
|
|
221
|
+
openRuntime(agent: PersistedAgent): Promise<ChildAgentRuntime>;
|
|
233
222
|
resolveLaunchMissingDependencies(agent: PersistedAgent): Promise<string[]>;
|
|
234
223
|
resolveRestorationMissingDependencies(agent: PersistedAgent): Promise<string[]>;
|
|
235
224
|
resolveThinkingLevel(modelId: string, requested: ThinkingLevel): ThinkingLevel;
|