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

@@ -58,6 +58,52 @@ function apply(
}
describe('Pi event projector', () => {
it('projects only subagent.v1 details and never exposes unknown raw details', async () => {
const projector = new PiEventProjector({ createId: () => 'unused' });
let snapshot = emptySnapshot();
snapshot.nodes.push({
kind: 'tool', id: 'tool-subagent', toolCallId: 'call-subagent', toolName: 'subagent',
title: 'subagent', inputText: '{}', status: 'running', output: [],
});
snapshot = apply(snapshot, await projector.project(snapshot, {
type: 'tool_execution_update',
toolCallId: 'call-subagent',
partialResult: {
content: [],
details: {
schema: 'subagent.v1', dispatchId: 'dispatch-a', mode: 'parallel',
tasks: [{
taskId: 'task-a', agentId: 'agent-a', toolProfile: 'read-only',
status: 'complete', summary: 'done', usage: { inputTokens: 3, outputTokens: 5 },
}],
},
},
}));
expect(snapshot.nodes).toContainEqual(expect.objectContaining({
kind: 'subagent', id: 'subagent:dispatch-a', runId: 'run-a',
details: expect.objectContaining({ schema: 'subagent.v1' }),
}));
expect(snapshot.nodes).toContainEqual(expect.objectContaining({
kind: 'tool', id: 'tool-subagent',
details: expect.objectContaining({ dispatchId: 'dispatch-a' }),
}));
snapshot.nodes.push({
kind: 'tool', id: 'tool-unknown', toolCallId: 'call-unknown', toolName: 'subagent',
title: 'subagent', inputText: '{}', status: 'running', output: [],
});
snapshot = apply(snapshot, await projector.project(snapshot, {
type: 'tool_execution_end',
toolCallId: 'call-unknown', isError: false,
result: { details: { schema: 'subagent.v2', raw: 'RAW_SECRET_DETAILS' } },
}));
expect(JSON.stringify(snapshot)).not.toContain('RAW_SECRET_DETAILS');
expect(snapshot.nodes).toContainEqual(expect.objectContaining({
kind: 'tool', id: 'tool-unknown',
output: [expect.objectContaining({ text: 'Subagent details are unavailable for this version.' })],
}));
});
it('keeps one assistant UI identity while content-index deltas become an authoritative message', async () => {
const projector = new PiEventProjector({
createId: () => 'assistant-ui-a',