feat: add Pi subagent scheduler and child runtime

This commit is contained in:
2026-08-23 12:40:22 +08:00
parent b806c78139
commit f1c7cd8ad4
21 changed files with 1986 additions and 53 deletions

View File

@@ -7,6 +7,7 @@ import type {
PublicUsage,
} from '../contracts';
import type { PiRpcEvent } from './rpc-client';
import { subagentDetailsOfResult } from '../subagent-protocol';
export interface PiEventProjectorOptions {
createId(): string;
@@ -89,6 +90,49 @@ function outputBlocks(toolId: string, value: unknown): ConversationContentBlock[
});
}
function projectToolResult(
snapshot: ConversationSnapshot,
tool: ConversationToolNode,
status: ConversationToolNode['status'],
value: unknown,
): ConversationPatch[] {
const details = subagentDetailsOfResult(value);
const result = asRecord(value);
const unknownSubagentDetails = tool.toolName === 'subagent'
&& result?.details !== undefined
&& !details;
const output = unknownSubagentDetails
? [{
kind: 'text' as const,
id: `${tool.id}:output:unavailable`,
text: 'Subagent details are unavailable for this version.',
status: 'complete' as const,
}]
: outputBlocks(tool.id, value);
const patches: ConversationPatch[] = [{
op: 'tool.upsert',
node: {
...tool,
status,
output,
...(details ? { details } : {}),
},
}];
const runId = snapshot.run.runId;
if (details && runId) {
patches.push({
op: 'subagent.upsert',
node: {
kind: 'subagent',
id: `subagent:${details.dispatchId}`,
runId,
details,
},
});
}
return patches;
}
function currentTool(snapshot: ConversationSnapshot, toolCallId: string): ConversationToolNode | undefined {
return snapshot.nodes.find(
(node): node is ConversationToolNode => node.kind === 'tool'
@@ -613,28 +657,19 @@ export class PiEventProjector {
if (typeof event.toolCallId !== 'string') return [];
const tool = currentTool(snapshot, event.toolCallId);
if (!tool) return [];
return [{
op: 'tool.upsert',
node: {
...tool,
status: 'running',
output: outputBlocks(tool.id, event.partialResult),
},
}];
return projectToolResult(snapshot, tool, 'running', event.partialResult);
}
if (event.type === 'tool_execution_end') {
if (typeof event.toolCallId !== 'string') return [];
const tool = currentTool(snapshot, event.toolCallId);
if (!tool) return [];
return [{
op: 'tool.upsert',
node: {
...tool,
status: event.isError === true ? 'error' : 'complete',
output: outputBlocks(tool.id, event.result),
},
}];
return projectToolResult(
snapshot,
tool,
event.isError === true ? 'error' : 'complete',
event.result,
);
}
if (event.type === 'message_end') {
@@ -642,14 +677,12 @@ export class PiEventProjector {
if (message?.role === 'toolResult' && typeof message.toolCallId === 'string') {
const tool = currentTool(snapshot, message.toolCallId);
if (!tool) return [];
return [{
op: 'tool.upsert',
node: {
...tool,
status: message.isError === true ? 'error' : 'complete',
output: outputBlocks(tool.id, message),
},
}];
return projectToolResult(
snapshot,
tool,
message.isError === true ? 'error' : 'complete',
message,
);
}
if (message?.role !== 'assistant') return [];
const current = currentAssistant(snapshot);