pi-subagents 0.28.0 → 0.30.0
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/CHANGELOG.md +31 -0
- package/README.md +26 -62
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +29 -35
- package/src/agents/agent-management.ts +29 -22
- package/src/agents/agent-selection.ts +2 -0
- package/src/agents/agent-serializer.ts +5 -10
- package/src/agents/agents.ts +339 -47
- package/src/agents/chain-serializer.ts +4 -9
- package/src/agents/proactive-skills.ts +191 -0
- package/src/extension/doctor.ts +4 -3
- package/src/extension/fanout-child.ts +1 -3
- package/src/extension/index.ts +6 -9
- package/src/extension/schemas.ts +63 -26
- package/src/intercom/intercom-bridge.ts +11 -1
- package/src/intercom/result-intercom.ts +0 -5
- package/src/runs/background/async-execution.ts +186 -74
- package/src/runs/background/async-resume.ts +53 -5
- package/src/runs/background/async-status.ts +4 -1
- package/src/runs/background/chain-append.ts +282 -0
- package/src/runs/background/chain-root-attachment.ts +161 -0
- package/src/runs/background/run-status.ts +2 -7
- package/src/runs/background/subagent-runner.ts +160 -219
- package/src/runs/foreground/chain-execution.ts +62 -58
- package/src/runs/foreground/execution.ts +39 -343
- package/src/runs/foreground/subagent-executor.ts +316 -111
- package/src/runs/shared/acceptance.ts +605 -22
- package/src/runs/shared/chain-outputs.ts +23 -8
- package/src/runs/shared/completion-guard.ts +3 -26
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/model-fallback.ts +38 -0
- package/src/runs/shared/parallel-utils.ts +13 -10
- package/src/runs/shared/pi-args.ts +3 -2
- package/src/runs/shared/subagent-control.ts +8 -11
- package/src/runs/shared/subagent-prompt-runtime.ts +3 -2
- package/src/runs/shared/workflow-graph.ts +2 -6
- package/src/shared/atomic-json.ts +68 -11
- package/src/shared/settings.ts +1 -0
- package/src/shared/types.ts +20 -49
- package/src/shared/utils.ts +2 -8
- package/src/slash/slash-bridge.ts +3 -1
- package/src/slash/slash-commands.ts +1 -1
- package/src/tui/render.ts +14 -29
- package/src/runs/shared/acceptance-contract.ts +0 -318
- package/src/runs/shared/acceptance-evaluation.ts +0 -221
- package/src/runs/shared/acceptance-finalization.ts +0 -173
- package/src/runs/shared/acceptance-reports.ts +0 -127
|
@@ -59,12 +59,13 @@ import {
|
|
|
59
59
|
MAX_CONCURRENCY,
|
|
60
60
|
resolveChildMaxSubagentDepth,
|
|
61
61
|
} from "../../shared/types.ts";
|
|
62
|
-
import {
|
|
62
|
+
import { resolveSubagentModelOverride } from "../shared/model-fallback.ts";
|
|
63
63
|
import { validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
64
64
|
import { buildWorkflowGraphSnapshot } from "../shared/workflow-graph.ts";
|
|
65
65
|
import { ChainOutputValidationError, outputEntryFromResult, resolveOutputReferences, validateChainOutputBindings } from "../shared/chain-outputs.ts";
|
|
66
66
|
import { createStructuredOutputRuntime } from "../shared/structured-output.ts";
|
|
67
67
|
import { collectDynamicResults, DynamicFanoutError, materializeDynamicParallelStep, validateDynamicCollection, type DynamicCollectedResult } from "../shared/dynamic-fanout.ts";
|
|
68
|
+
import { acceptanceFailureMessage, aggregateAcceptanceReport, evaluateAcceptance, resolveEffectiveAcceptance } from "../shared/acceptance.ts";
|
|
68
69
|
import type { ChainOutputMap } from "../../shared/types.ts";
|
|
69
70
|
|
|
70
71
|
interface ChainExecutionDetailsInput {
|
|
@@ -81,7 +82,7 @@ interface ChainExecutionDetailsInput {
|
|
|
81
82
|
outputs?: ChainOutputMap;
|
|
82
83
|
currentFlatIndex?: number;
|
|
83
84
|
dynamicChildren?: Record<number, Array<{ agent: string; label?: string; flatIndex: number; itemKey: string; outputName?: string; structured?: boolean; error?: string }>>;
|
|
84
|
-
dynamicGroupStatuses?: Record<number, { status: "pending" | "running" | "completed" | "failed" | "paused" | "detached"
|
|
85
|
+
dynamicGroupStatuses?: Record<number, { status: "pending" | "running" | "completed" | "failed" | "paused" | "detached"; error?: string; acceptance?: SingleResult["acceptance"] }>;
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
interface ParallelChainRunInput {
|
|
@@ -103,8 +104,6 @@ interface ParallelChainRunInput {
|
|
|
103
104
|
sessionFileForIndex?: (idx?: number) => string | undefined;
|
|
104
105
|
shareEnabled: boolean;
|
|
105
106
|
artifactConfig: ArtifactConfig;
|
|
106
|
-
timeoutMs?: number;
|
|
107
|
-
timeoutAt?: number;
|
|
108
107
|
artifactsDir: string;
|
|
109
108
|
signal?: AbortSignal;
|
|
110
109
|
onUpdate?: (r: AgentToolResult<Details>) => void;
|
|
@@ -233,9 +232,12 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
233
232
|
taskStr = prefix + taskStr + suffix;
|
|
234
233
|
|
|
235
234
|
const taskAgentConfig = input.agents.find((agent) => agent.name === task.agent);
|
|
236
|
-
const effectiveModel =
|
|
237
|
-
|
|
238
|
-
|
|
235
|
+
const effectiveModel = resolveSubagentModelOverride(
|
|
236
|
+
task.model ?? taskAgentConfig?.model,
|
|
237
|
+
input.ctx.model,
|
|
238
|
+
input.availableModels,
|
|
239
|
+
input.ctx.model?.provider,
|
|
240
|
+
);
|
|
239
241
|
const maxSubagentDepth = resolveChildMaxSubagentDepth(input.maxSubagentDepth, taskAgentConfig?.maxSubagentDepth);
|
|
240
242
|
|
|
241
243
|
const taskCwd = input.worktreeSetup
|
|
@@ -267,7 +269,6 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
267
269
|
cwd: taskCwd,
|
|
268
270
|
signal: input.signal,
|
|
269
271
|
interruptSignal: interruptController.signal,
|
|
270
|
-
...(input.timeoutMs !== undefined && input.timeoutAt !== undefined ? { timeoutMs: input.timeoutMs, timeoutAt: input.timeoutAt } : {}),
|
|
271
272
|
allowIntercomDetach: taskAgentConfig?.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
|
|
272
273
|
intercomEvents: input.intercomEvents,
|
|
273
274
|
runId: input.runId,
|
|
@@ -280,8 +281,6 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
280
281
|
outputPath,
|
|
281
282
|
outputMode: behavior.outputMode,
|
|
282
283
|
maxSubagentDepth,
|
|
283
|
-
maxExecutionTimeMs: taskAgentConfig?.maxExecutionTimeMs,
|
|
284
|
-
maxTokens: taskAgentConfig?.maxTokens,
|
|
285
284
|
controlConfig: input.controlConfig,
|
|
286
285
|
onControlEvent: input.onControlEvent,
|
|
287
286
|
intercomSessionName: input.childIntercomTarget?.(task.agent, input.globalTaskIndex + taskIndex),
|
|
@@ -396,7 +395,6 @@ interface ChainExecutionParams {
|
|
|
396
395
|
nestedRoute?: NestedRouteInfo;
|
|
397
396
|
worktreeSetupHook?: string;
|
|
398
397
|
worktreeSetupHookTimeoutMs?: number;
|
|
399
|
-
timeoutMs?: number;
|
|
400
398
|
}
|
|
401
399
|
|
|
402
400
|
interface ChainExecutionResult {
|
|
@@ -586,7 +584,6 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
586
584
|
tuiBehaviorOverrides = result.behaviorOverrides;
|
|
587
585
|
}
|
|
588
586
|
|
|
589
|
-
const timeoutAt = params.timeoutMs !== undefined ? Date.now() + params.timeoutMs : undefined;
|
|
590
587
|
let prev = "";
|
|
591
588
|
let globalTaskIndex = 0;
|
|
592
589
|
let progressCreated = false;
|
|
@@ -655,7 +652,6 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
655
652
|
shareEnabled,
|
|
656
653
|
artifactConfig,
|
|
657
654
|
artifactsDir,
|
|
658
|
-
...(params.timeoutMs !== undefined && timeoutAt !== undefined ? { timeoutMs: params.timeoutMs, timeoutAt } : {}),
|
|
659
655
|
signal,
|
|
660
656
|
onUpdate,
|
|
661
657
|
results,
|
|
@@ -682,18 +678,6 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
682
678
|
if (result.progress) allProgress.push(result.progress);
|
|
683
679
|
if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
|
|
684
680
|
}
|
|
685
|
-
const timedOutIndexInStep = parallelResults.findIndex((result) => result.timedOut);
|
|
686
|
-
const timedOut = timedOutIndexInStep >= 0 ? parallelResults[timedOutIndexInStep] : undefined;
|
|
687
|
-
if (timedOut) {
|
|
688
|
-
return {
|
|
689
|
-
content: [{ type: "text", text: `Chain timed out at step ${stepIndex + 1} (${timedOut.agent}): ${timedOut.error ?? "timeout expired"}` }],
|
|
690
|
-
isError: true,
|
|
691
|
-
details: buildChainExecutionDetails(makeDetailsInput({
|
|
692
|
-
currentStepIndex: stepIndex,
|
|
693
|
-
currentFlatIndex: globalTaskIndex - step.parallel.length + timedOutIndexInStep,
|
|
694
|
-
})),
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
681
|
const interruptedIndexInStep = parallelResults.findIndex((result) => result.interrupted);
|
|
698
682
|
const interrupted = interruptedIndexInStep >= 0 ? parallelResults[interruptedIndexInStep] : undefined;
|
|
699
683
|
if (interrupted) {
|
|
@@ -770,11 +754,6 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
770
754
|
if (worktreeSetup) cleanupWorktrees(worktreeSetup);
|
|
771
755
|
}
|
|
772
756
|
} else if (isDynamicParallelStep(step)) {
|
|
773
|
-
if (Object.hasOwn(step, "acceptance")) {
|
|
774
|
-
const message = `Dynamic fanout step ${stepIndex + 1} does not support group-level acceptance; set acceptance on the child template instead.`;
|
|
775
|
-
dynamicGroupStatuses[stepIndex] = { status: "failed", error: message };
|
|
776
|
-
return buildChainExecutionErrorResult(message, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
|
|
777
|
-
}
|
|
778
757
|
let materialized: ReturnType<typeof materializeDynamicParallelStep>;
|
|
779
758
|
try {
|
|
780
759
|
materialized = materializeDynamicParallelStep(step, outputs, stepIndex, { maxItems: params.dynamicFanoutMaxItems });
|
|
@@ -808,6 +787,30 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
808
787
|
stepIndex,
|
|
809
788
|
};
|
|
810
789
|
dynamicGroupStatuses[stepIndex] = { status: "completed" };
|
|
790
|
+
if (step.acceptance !== undefined) {
|
|
791
|
+
const effectiveGroupAcceptance = resolveEffectiveAcceptance({
|
|
792
|
+
explicit: step.acceptance,
|
|
793
|
+
agentName: step.parallel.agent,
|
|
794
|
+
task: step.parallel.task ?? originalTask,
|
|
795
|
+
mode: "chain",
|
|
796
|
+
dynamicGroup: true,
|
|
797
|
+
});
|
|
798
|
+
const groupAcceptance = await evaluateAcceptance({
|
|
799
|
+
acceptance: effectiveGroupAcceptance,
|
|
800
|
+
output: "",
|
|
801
|
+
report: aggregateAcceptanceReport({
|
|
802
|
+
results: [],
|
|
803
|
+
notes: "Dynamic fanout produced 0 results.",
|
|
804
|
+
}),
|
|
805
|
+
cwd: cwd ?? ctx.cwd,
|
|
806
|
+
});
|
|
807
|
+
dynamicGroupStatuses[stepIndex].acceptance = groupAcceptance;
|
|
808
|
+
const groupAcceptanceFailure = acceptanceFailureMessage(groupAcceptance);
|
|
809
|
+
if (groupAcceptanceFailure) {
|
|
810
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: groupAcceptanceFailure, acceptance: groupAcceptance };
|
|
811
|
+
return buildChainExecutionErrorResult(groupAcceptanceFailure, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex }));
|
|
812
|
+
}
|
|
813
|
+
}
|
|
811
814
|
prev = "Dynamic fanout produced 0 results.";
|
|
812
815
|
continue;
|
|
813
816
|
}
|
|
@@ -855,7 +858,6 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
855
858
|
shareEnabled,
|
|
856
859
|
artifactConfig,
|
|
857
860
|
artifactsDir,
|
|
858
|
-
...(params.timeoutMs !== undefined && timeoutAt !== undefined ? { timeoutMs: params.timeoutMs, timeoutAt } : {}),
|
|
859
861
|
signal,
|
|
860
862
|
onUpdate,
|
|
861
863
|
results,
|
|
@@ -882,19 +884,6 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
882
884
|
if (result.artifactPaths) allArtifactPaths.push(result.artifactPaths);
|
|
883
885
|
}
|
|
884
886
|
const collected = collectDynamicResults(step, materialized.items, parallelResults);
|
|
885
|
-
const timedOutIndexInStep = parallelResults.findIndex((result) => result.timedOut);
|
|
886
|
-
const timedOut = timedOutIndexInStep >= 0 ? parallelResults[timedOutIndexInStep] : undefined;
|
|
887
|
-
if (timedOut) {
|
|
888
|
-
dynamicGroupStatuses[stepIndex] = { status: "timed-out", error: timedOut.error };
|
|
889
|
-
return {
|
|
890
|
-
content: [{ type: "text", text: `Chain timed out at step ${stepIndex + 1} (${timedOut.agent}): ${timedOut.error ?? "timeout expired"}` }],
|
|
891
|
-
isError: true,
|
|
892
|
-
details: buildChainExecutionDetails(makeDetailsInput({
|
|
893
|
-
currentStepIndex: stepIndex,
|
|
894
|
-
currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length + timedOutIndexInStep,
|
|
895
|
-
})),
|
|
896
|
-
};
|
|
897
|
-
}
|
|
898
887
|
const interruptedIndexInStep = parallelResults.findIndex((result) => result.interrupted);
|
|
899
888
|
const interrupted = interruptedIndexInStep >= 0 ? parallelResults[interruptedIndexInStep] : undefined;
|
|
900
889
|
if (interrupted) {
|
|
@@ -953,6 +942,28 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
953
942
|
stepIndex,
|
|
954
943
|
};
|
|
955
944
|
dynamicGroupStatuses[stepIndex] = { status: "completed" };
|
|
945
|
+
const effectiveGroupAcceptance = resolveEffectiveAcceptance({
|
|
946
|
+
explicit: step.acceptance,
|
|
947
|
+
agentName: step.parallel.agent,
|
|
948
|
+
task: step.parallel.task ?? originalTask,
|
|
949
|
+
mode: "chain",
|
|
950
|
+
dynamicGroup: true,
|
|
951
|
+
});
|
|
952
|
+
const groupAcceptance = await evaluateAcceptance({
|
|
953
|
+
acceptance: effectiveGroupAcceptance,
|
|
954
|
+
output: "",
|
|
955
|
+
report: aggregateAcceptanceReport({
|
|
956
|
+
results: parallelResults,
|
|
957
|
+
notes: `Dynamic fanout collected ${collected.length} result(s) into ${step.collect.as}.`,
|
|
958
|
+
}),
|
|
959
|
+
cwd: cwd ?? ctx.cwd,
|
|
960
|
+
});
|
|
961
|
+
dynamicGroupStatuses[stepIndex].acceptance = groupAcceptance;
|
|
962
|
+
const groupAcceptanceFailure = acceptanceFailureMessage(groupAcceptance);
|
|
963
|
+
if (groupAcceptanceFailure) {
|
|
964
|
+
dynamicGroupStatuses[stepIndex] = { status: "failed", error: groupAcceptanceFailure, acceptance: groupAcceptance };
|
|
965
|
+
return buildChainExecutionErrorResult(groupAcceptanceFailure, makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - dynamicParallelStep.parallel.length }));
|
|
966
|
+
}
|
|
956
967
|
const taskResults: ParallelTaskResult[] = parallelResults.map((result, i) => ({
|
|
957
968
|
agent: result.agent,
|
|
958
969
|
taskIndex: i,
|
|
@@ -1008,10 +1019,13 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1008
1019
|
const cleanTask = stepTask;
|
|
1009
1020
|
stepTask = prefix + stepTask + suffix;
|
|
1010
1021
|
|
|
1011
|
-
const effectiveModel =
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1022
|
+
const effectiveModel = tuiOverride?.model
|
|
1023
|
+
?? resolveSubagentModelOverride(
|
|
1024
|
+
seqStep.model ?? agentConfig.model,
|
|
1025
|
+
ctx.model,
|
|
1026
|
+
availableModels,
|
|
1027
|
+
ctx.model?.provider,
|
|
1028
|
+
);
|
|
1015
1029
|
|
|
1016
1030
|
const outputPath = typeof behavior.output === "string"
|
|
1017
1031
|
? (path.isAbsolute(behavior.output) ? behavior.output : path.join(chainDir, behavior.output))
|
|
@@ -1043,7 +1057,6 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1043
1057
|
cwd: resolveChildCwd(cwd ?? ctx.cwd, seqStep.cwd),
|
|
1044
1058
|
signal,
|
|
1045
1059
|
interruptSignal: interruptController.signal,
|
|
1046
|
-
...(params.timeoutMs !== undefined && timeoutAt !== undefined ? { timeoutMs: params.timeoutMs, timeoutAt } : {}),
|
|
1047
1060
|
allowIntercomDetach: agentConfig.systemPrompt?.includes(INTERCOM_BRIDGE_MARKER) === true,
|
|
1048
1061
|
intercomEvents,
|
|
1049
1062
|
runId,
|
|
@@ -1056,8 +1069,6 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1056
1069
|
outputPath,
|
|
1057
1070
|
outputMode: behavior.outputMode,
|
|
1058
1071
|
maxSubagentDepth,
|
|
1059
|
-
maxExecutionTimeMs: agentConfig.maxExecutionTimeMs,
|
|
1060
|
-
maxTokens: agentConfig.maxTokens,
|
|
1061
1072
|
controlConfig,
|
|
1062
1073
|
onControlEvent,
|
|
1063
1074
|
intercomSessionName: childIntercomTarget?.(seqStep.agent, globalTaskIndex),
|
|
@@ -1125,13 +1136,6 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1125
1136
|
if (r.progress) allProgress.push(r.progress);
|
|
1126
1137
|
if (r.artifactPaths) allArtifactPaths.push(r.artifactPaths);
|
|
1127
1138
|
|
|
1128
|
-
if (r.timedOut) {
|
|
1129
|
-
return {
|
|
1130
|
-
content: [{ type: "text", text: `Chain timed out at step ${stepIndex + 1} (${r.agent}): ${r.error ?? "timeout expired"}` }],
|
|
1131
|
-
isError: true,
|
|
1132
|
-
details: buildChainExecutionDetails(makeDetailsInput({ currentStepIndex: stepIndex, currentFlatIndex: globalTaskIndex - 1 })),
|
|
1133
|
-
};
|
|
1134
|
-
}
|
|
1135
1139
|
if (r.interrupted) {
|
|
1136
1140
|
return {
|
|
1137
1141
|
content: [{ type: "text", text: `Chain paused after interrupt at step ${stepIndex + 1} (${r.agent}). Waiting for explicit next action.` }],
|