feat: add Pi subagent scheduler and child runtime
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user