pi-subagents 0.50.0 → 0.52.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 +103 -0
- package/agents/oracle.md +3 -1
- package/agents/reviewer.md +1 -0
- package/agents/scout.md +2 -2
- package/agents/worker.md +2 -1
- package/async-retention-discovery-worker.mjs +180 -0
- package/docs/agents.md +40 -2
- package/docs/configuration.md +30 -12
- package/docs/extension-api.md +42 -1
- package/docs/models.md +2 -0
- package/docs/observability.md +41 -5
- package/docs/tool-reference.md +38 -39
- package/docs/workflows.md +171 -3
- package/package.json +4 -2
- package/skills/pi-subagents/SKILL.md +6 -4
- package/skills/pi-subagents/references/constraints-and-recipes.md +12 -6
- package/skills/pi-subagents/references/execution-controls.md +29 -15
- package/skills/pi-subagents/references/management-authoring-rpc.md +3 -3
- package/skills/pi-subagents/references/prompting-and-roles.md +4 -2
- package/src/agents/agent-management.ts +124 -351
- package/src/agents/agents.ts +157 -31
- package/src/agents/skills.ts +1 -1
- package/src/api/external-job-provider.ts +185 -0
- package/src/api/preflight.ts +36 -8
- package/src/api/shared-types.ts +2 -0
- package/src/extension/config.ts +3 -3
- package/src/extension/doctor.ts +3 -6
- package/src/extension/fanout-child.ts +2 -2
- package/src/extension/index.ts +166 -88
- package/src/extension/public-execution.ts +31 -2
- package/src/extension/schemas.ts +12 -35
- package/src/extension/tool-description.ts +35 -24
- package/src/inspectors/herdr/actions.ts +2 -2
- package/src/inspectors/herdr/inspector-runner.ts +2 -1
- package/src/inspectors/herdr/project-panes.ts +2 -2
- package/src/intercom/native-supervisor-channel.ts +32 -10
- package/src/missions/lifecycle.ts +6 -1
- package/src/missions/store.ts +4 -9
- package/src/missions/workflow-state.ts +2 -2
- package/src/profiles/profiles.ts +3 -1
- package/src/runs/background/active-run-index.ts +31 -8
- package/src/runs/background/async-execution.ts +68 -40
- package/src/runs/background/async-job-tracker.ts +20 -4
- package/src/runs/background/async-resume.ts +47 -15
- package/src/runs/background/async-retention.ts +886 -0
- package/src/runs/background/async-status.ts +39 -53
- package/src/runs/background/chain-append.ts +3 -33
- package/src/runs/background/completion-dedupe.ts +5 -1
- package/src/runs/background/completion-replay.ts +22 -12
- package/src/runs/background/control-channel.ts +14 -68
- package/src/runs/background/fleet-view.ts +68 -19
- package/src/runs/background/index-segment.ts +59 -0
- package/src/runs/background/inspect-rpc.ts +443 -0
- package/src/runs/background/notify.ts +31 -5
- package/src/runs/background/result-files.ts +158 -90
- package/src/runs/background/result-watcher.ts +116 -20
- package/src/runs/background/resume-guidance.ts +8 -5
- package/src/runs/background/retained-children.ts +13 -3
- package/src/runs/background/run-id-query.ts +7 -0
- package/src/runs/background/run-id-resolver.ts +11 -9
- package/src/runs/background/run-status.ts +27 -18
- package/src/runs/background/scheduled-runs.ts +24 -4
- package/src/runs/background/stale-run-reconciler.ts +6 -3
- package/src/runs/background/steering.ts +11 -1
- package/src/runs/background/subagent-runner.ts +253 -140
- package/src/runs/background/subagent-wait.ts +8 -8
- package/src/runs/background/terminal-run-index.ts +129 -0
- package/src/runs/background/wait-completions.ts +21 -4
- package/src/runs/background/wait-subscriptions.ts +81 -2
- package/src/runs/foreground/async-steering-action.ts +21 -14
- package/src/runs/foreground/execution.ts +3 -1
- package/src/runs/foreground/subagent-executor.ts +666 -1579
- package/src/runs/foreground/workflow-detach-reconcile.ts +194 -0
- package/src/runs/foreground/workflow-foreground-steering.ts +6 -5
- package/src/runs/shared/acceptance.ts +4 -4
- package/src/runs/shared/chain-outputs.ts +1 -3
- package/src/runs/shared/external-job-bridge.ts +444 -0
- package/src/runs/shared/external-job-runner.ts +286 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +14 -0
- package/src/runs/shared/model-fallback.ts +98 -38
- package/src/runs/shared/orca-progress-tabs.ts +84 -22
- package/src/runs/shared/parallel-handoff.ts +46 -4
- package/src/runs/shared/parallel-utils.ts +4 -15
- package/src/runs/shared/permissions.ts +5 -1
- package/src/runs/shared/pi-args.ts +8 -1
- package/src/runs/shared/session-lease.ts +0 -6
- package/src/runs/shared/subagent-control.ts +26 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +12 -6
- package/src/runs/shared/workflow-graph.ts +1 -23
- package/src/runs/shared/worktree.ts +14 -2
- package/src/shared/atomic-json.ts +22 -2
- package/src/shared/capacity-resilient-json.ts +102 -0
- package/src/shared/completion-owner.ts +14 -0
- package/src/shared/file-system-retry.ts +49 -1
- package/src/shared/fork-context.ts +42 -0
- package/src/shared/prompt-resources.ts +0 -40
- package/src/shared/settings.ts +3 -27
- package/src/shared/types.ts +71 -26
- package/src/shared/utils.ts +8 -0
- package/src/shared/watch-strategy.ts +10 -0
- package/src/slash/slash-bridge.ts +2 -1
- package/src/slash/slash-commands.ts +29 -3
- package/src/tui/fleet.ts +63 -15
- package/src/watchdog/change-signature.ts +1 -1
- package/src/watchdog/lsp-diagnostics.ts +1 -0
- package/src/workflows/chat-progress.ts +2 -2
- package/src/workflows/scripted-workflow.ts +220 -67
- package/src/runs/foreground/chain-clarify.ts +0 -1354
- package/src/runs/foreground/chain-execution.ts +0 -1581
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
4
|
+
import { isStorageCapacityError } from "../../shared/file-system-retry.ts";
|
|
5
|
+
import { getSingleResultOutput, readStatus } from "../../shared/utils.ts";
|
|
6
|
+
import {
|
|
7
|
+
DIRS,
|
|
8
|
+
SUBAGENT_ASYNC_COMPLETE_EVENT,
|
|
9
|
+
type AsyncStatus,
|
|
10
|
+
type IntercomEventBus,
|
|
11
|
+
type SingleResult,
|
|
12
|
+
type SubagentState,
|
|
13
|
+
} from "../../shared/types.ts";
|
|
14
|
+
import { updateActiveRunIndex } from "../background/active-run-index.ts";
|
|
15
|
+
import { resultFilePath, writeAsyncResultFile } from "../background/result-files.ts";
|
|
16
|
+
|
|
17
|
+
function cloneWorkflowStatus(status: AsyncStatus): AsyncStatus {
|
|
18
|
+
return {
|
|
19
|
+
...status,
|
|
20
|
+
steps: status.steps?.map((step) => ({ ...step })),
|
|
21
|
+
workflow: status.workflow ? { ...status.workflow, trace: [...(status.workflow.trace ?? [])] } : status.workflow,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function childSucceeded(result: Pick<SingleResult, "exitCode" | "error">): boolean {
|
|
26
|
+
return result.exitCode === 0 && !result.error;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function applyDetachedChildToPausedWorkflow(
|
|
30
|
+
status: AsyncStatus,
|
|
31
|
+
input: { childRunId: string; result: Pick<SingleResult, "exitCode" | "error" | "sessionFile">; workflowKey?: string },
|
|
32
|
+
): AsyncStatus | undefined {
|
|
33
|
+
if (status.mode !== "workflow" || status.state !== "paused") return undefined;
|
|
34
|
+
const next = cloneWorkflowStatus(status);
|
|
35
|
+
const step = next.steps?.find((candidate) => candidate.runId === input.childRunId)
|
|
36
|
+
?? (input.workflowKey ? next.steps?.find((candidate) => candidate.workflowKey === input.workflowKey) : undefined);
|
|
37
|
+
if (!step) return undefined;
|
|
38
|
+
const succeeded = childSucceeded(input.result);
|
|
39
|
+
const updatedAt = Date.now();
|
|
40
|
+
step.status = succeeded ? "completed" : "failed";
|
|
41
|
+
step.endedAt = updatedAt;
|
|
42
|
+
delete step.activityState;
|
|
43
|
+
delete step.currentTool;
|
|
44
|
+
delete step.currentToolStartedAt;
|
|
45
|
+
if (input.result.sessionFile) step.sessionFile = input.result.sessionFile;
|
|
46
|
+
if (succeeded) delete step.error;
|
|
47
|
+
else if (input.result.error) step.error = input.result.error;
|
|
48
|
+
next.lastUpdate = updatedAt;
|
|
49
|
+
const promoted = promotePausedWorkflowIfSettled(next);
|
|
50
|
+
if (promoted?.state === "failed" && input.result.error) promoted.error = input.result.error;
|
|
51
|
+
return promoted ?? next;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function promotePausedWorkflowIfSettled(status: AsyncStatus): AsyncStatus | undefined {
|
|
55
|
+
if (status.mode !== "workflow" || status.state !== "paused") return undefined;
|
|
56
|
+
const next = cloneWorkflowStatus(status);
|
|
57
|
+
const stillOpen = next.steps?.some((candidate) =>
|
|
58
|
+
candidate.status === "running"
|
|
59
|
+
|| (candidate.status === "paused" && candidate.activityState === "needs_attention")
|
|
60
|
+
) === true;
|
|
61
|
+
if (stillOpen || !next.steps?.length) return undefined;
|
|
62
|
+
const failed = next.steps.some((candidate) => candidate.status === "failed");
|
|
63
|
+
const updatedAt = Date.now();
|
|
64
|
+
next.lastUpdate = updatedAt;
|
|
65
|
+
next.state = failed ? "failed" : "complete";
|
|
66
|
+
next.endedAt = updatedAt;
|
|
67
|
+
delete next.activityState;
|
|
68
|
+
if (!failed) delete next.error;
|
|
69
|
+
return next;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function workflowResultChildren(status: AsyncStatus, childRunId: string, result: SingleResult, existingResults: unknown): unknown {
|
|
73
|
+
const output = getSingleResultOutput(result);
|
|
74
|
+
if (Array.isArray(existingResults)) {
|
|
75
|
+
return existingResults.map((entry) => {
|
|
76
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return entry;
|
|
77
|
+
const child = entry as Record<string, unknown>;
|
|
78
|
+
if (child.runId !== childRunId) return child;
|
|
79
|
+
return {
|
|
80
|
+
...child,
|
|
81
|
+
success: childSucceeded(result),
|
|
82
|
+
output,
|
|
83
|
+
outputState: output.trim() ? "present" : "absent",
|
|
84
|
+
detached: undefined,
|
|
85
|
+
...(result.error ? { error: result.error } : {}),
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return status.steps?.map((step) => ({
|
|
90
|
+
workflowKey: step.workflowKey,
|
|
91
|
+
agent: step.agent,
|
|
92
|
+
runId: step.runId,
|
|
93
|
+
success: step.status === "completed" || step.status === "complete",
|
|
94
|
+
output: step.runId === childRunId ? output : "",
|
|
95
|
+
outputState: step.runId === childRunId && output.trim() ? "present" : "absent",
|
|
96
|
+
...(step.error ? { error: step.error } : {}),
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function publishedWorkflowResult(status: AsyncStatus, childRunId: string, result: SingleResult, asyncDir: string, existing?: Record<string, unknown>): Record<string, unknown> {
|
|
101
|
+
const sessionId = status.sessionId ?? (typeof existing?.sessionId === "string" ? existing.sessionId : undefined);
|
|
102
|
+
const summary = status.state === "complete"
|
|
103
|
+
? `Workflow completed after detached child ${childRunId} finished.`
|
|
104
|
+
: status.error ?? (typeof existing?.summary === "string" ? existing.summary : undefined);
|
|
105
|
+
return {
|
|
106
|
+
...(existing ?? {}),
|
|
107
|
+
id: status.runId,
|
|
108
|
+
runId: status.runId,
|
|
109
|
+
toolCallId: status.toolCallId,
|
|
110
|
+
agent: "workflow",
|
|
111
|
+
mode: "workflow",
|
|
112
|
+
success: status.state === "complete",
|
|
113
|
+
state: status.state,
|
|
114
|
+
summary,
|
|
115
|
+
error: status.error,
|
|
116
|
+
activityState: status.activityState,
|
|
117
|
+
endedAt: status.endedAt,
|
|
118
|
+
timestamp: Date.now(),
|
|
119
|
+
results: workflowResultChildren(status, childRunId, result, existing?.results),
|
|
120
|
+
workflow: status.workflow,
|
|
121
|
+
asyncDir,
|
|
122
|
+
cwd: status.cwd,
|
|
123
|
+
sessionId,
|
|
124
|
+
completionOwnerId: status.completionOwnerId,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function reconcileDetachedWorkflowChildCompletion(input: {
|
|
129
|
+
state: SubagentState;
|
|
130
|
+
workflowRunId: string;
|
|
131
|
+
childRunId: string;
|
|
132
|
+
result: SingleResult;
|
|
133
|
+
events?: IntercomEventBus;
|
|
134
|
+
workflowKey?: string;
|
|
135
|
+
}): boolean {
|
|
136
|
+
const job = input.state.asyncJobs.get(input.workflowRunId);
|
|
137
|
+
const asyncDir = job?.asyncDir ?? path.join(DIRS.async, input.workflowRunId);
|
|
138
|
+
const status = readStatus(asyncDir);
|
|
139
|
+
if (!status) return false;
|
|
140
|
+
const next = applyDetachedChildToPausedWorkflow(status, {
|
|
141
|
+
childRunId: input.childRunId,
|
|
142
|
+
result: input.result,
|
|
143
|
+
workflowKey: input.workflowKey,
|
|
144
|
+
});
|
|
145
|
+
if (!next) return false;
|
|
146
|
+
writeAtomicJson(path.join(asyncDir, "status.json"), next);
|
|
147
|
+
updateActiveRunIndex(asyncDir, next.state, next.toolCallId);
|
|
148
|
+
if (job) {
|
|
149
|
+
job.status = next.state;
|
|
150
|
+
job.updatedAt = next.lastUpdate;
|
|
151
|
+
job.activityState = next.activityState;
|
|
152
|
+
job.steps = next.steps?.map((step, index) => ({ ...step, index }));
|
|
153
|
+
job.workflow = next.workflow;
|
|
154
|
+
}
|
|
155
|
+
const resultPath = resultFilePath(DIRS.results, input.workflowRunId);
|
|
156
|
+
let existing: Record<string, unknown> | undefined;
|
|
157
|
+
try {
|
|
158
|
+
const parsed: unknown = JSON.parse(fs.readFileSync(resultPath, "utf-8"));
|
|
159
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) existing = parsed as Record<string, unknown>;
|
|
160
|
+
} catch (error) {
|
|
161
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
162
|
+
}
|
|
163
|
+
const published = publishedWorkflowResult(next, input.childRunId, input.result, asyncDir, existing);
|
|
164
|
+
writeAsyncResultFile(resultPath, published);
|
|
165
|
+
if (next.state === "complete" || next.state === "failed") {
|
|
166
|
+
try {
|
|
167
|
+
fs.appendFileSync(path.join(asyncDir, "events.jsonl"), `${JSON.stringify({
|
|
168
|
+
ts: Date.now(),
|
|
169
|
+
runId: input.workflowRunId,
|
|
170
|
+
type: "subagent.workflow.completed",
|
|
171
|
+
state: next.state,
|
|
172
|
+
...(next.error ? { error: next.error } : {}),
|
|
173
|
+
reconciledFromDetachedChild: input.childRunId,
|
|
174
|
+
})}\n`, "utf-8");
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (!isStorageCapacityError(error)) throw error;
|
|
177
|
+
}
|
|
178
|
+
input.events?.emit(SUBAGENT_ASYNC_COMPLETE_EVENT, {
|
|
179
|
+
id: input.workflowRunId,
|
|
180
|
+
runId: input.workflowRunId,
|
|
181
|
+
source: "async",
|
|
182
|
+
mode: "workflow",
|
|
183
|
+
agent: "workflow",
|
|
184
|
+
success: next.state === "complete",
|
|
185
|
+
state: next.state,
|
|
186
|
+
summary: typeof published.summary === "string" ? published.summary : (next.state === "complete" ? "Workflow completed." : next.error),
|
|
187
|
+
sessionId: next.sessionId,
|
|
188
|
+
completionOwnerId: next.completionOwnerId,
|
|
189
|
+
timestamp: Date.now(),
|
|
190
|
+
triggerTurn: true,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
@@ -4,6 +4,7 @@ import * as path from "node:path";
|
|
|
4
4
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
5
5
|
import type { Details, ForegroundRunControl, SubagentState } from "../../shared/types.ts";
|
|
6
6
|
import { readStatus } from "../../shared/utils.ts";
|
|
7
|
+
import { steeringReceipt } from "../background/steering.ts";
|
|
7
8
|
import {
|
|
8
9
|
consumeSteerAckFromDir,
|
|
9
10
|
readSteerCapability,
|
|
@@ -146,18 +147,18 @@ export async function steerWorkflowForegroundTarget(input: {
|
|
|
146
147
|
targets: [target],
|
|
147
148
|
};
|
|
148
149
|
if (input.signal?.aborted) {
|
|
149
|
-
return { content: [{ type: "text", text: `Steering pending for foreground run ${control.runId} (request ${request.id}); caller aborted before acknowledgment.` }], details: { mode: "management", results: [], steering } };
|
|
150
|
+
return { content: [{ type: "text", text: steeringReceipt(request.message, `Steering pending for foreground run ${control.runId} (request ${request.id}); caller aborted before acknowledgment.`) }], details: { mode: "management", results: [], steering } };
|
|
150
151
|
}
|
|
151
152
|
if (ack?.state === "delivered") {
|
|
152
|
-
return { content: [{ type: "text", text: `Steering delivered for foreground run ${control.runId} (request ${request.id}).` }], details: { mode: "management", results: [], steering } };
|
|
153
|
+
return { content: [{ type: "text", text: steeringReceipt(request.message, `Steering delivered for foreground run ${control.runId} (request ${request.id}).`) }], details: { mode: "management", results: [], steering } };
|
|
153
154
|
}
|
|
154
155
|
if (ack?.state === "queued") {
|
|
155
|
-
return { content: [{ type: "text", text: `Steering queued for foreground run ${control.runId} (request ${request.id}).` }], details: { mode: "management", results: [], steering } };
|
|
156
|
+
return { content: [{ type: "text", text: steeringReceipt(request.message, `Steering queued for foreground run ${control.runId} (request ${request.id}).`) }], details: { mode: "management", results: [], steering } };
|
|
156
157
|
}
|
|
157
158
|
if (ack?.state === "failed") {
|
|
158
|
-
return { content: [{ type: "text", text: `Steering failed for foreground run ${control.runId} (request ${request.id}): ${ack.message}` }], isError: true, details: { mode: "management", results: [], steering } };
|
|
159
|
+
return { content: [{ type: "text", text: steeringReceipt(request.message, `Steering failed for foreground run ${control.runId} (request ${request.id}): ${ack.message}`) }], isError: true, details: { mode: "management", results: [], steering } };
|
|
159
160
|
}
|
|
160
|
-
return { content: [{ type: "text", text: `Steering pending for foreground run ${control.runId} (request ${request.id}); no acknowledgment was received.` }], details: { mode: "management", results: [], steering } };
|
|
161
|
+
return { content: [{ type: "text", text: steeringReceipt(request.message, `Steering pending for foreground run ${control.runId} (request ${request.id}); no acknowledgment was received.`) }], details: { mode: "management", results: [], steering } };
|
|
161
162
|
}
|
|
162
163
|
|
|
163
164
|
export function workflowForegroundSteeringDir(asyncDirRoot: string, workflowRunId: string, childRunId: string): string {
|
|
@@ -946,7 +946,7 @@ function reportEvidenceStatus(report: AcceptanceReport, kind: AcceptanceEvidence
|
|
|
946
946
|
}
|
|
947
947
|
|
|
948
948
|
function checkNoStagedFiles(cwd: string): AcceptanceRuntimeCheck {
|
|
949
|
-
const result = spawnSync("git", ["status", "--short"], { cwd, encoding: "utf-8" });
|
|
949
|
+
const result = spawnSync("git", ["status", "--short"], { cwd, encoding: "utf-8", windowsHide: true });
|
|
950
950
|
if (result.status !== 0) {
|
|
951
951
|
return { id: "no-staged-files", status: "not-applicable", message: "git status unavailable; no staged-files check skipped" };
|
|
952
952
|
}
|
|
@@ -1053,11 +1053,11 @@ interface VerifyWorkspaceState {
|
|
|
1053
1053
|
}
|
|
1054
1054
|
|
|
1055
1055
|
function readVerifyWorkspaceState(cwd: string): VerifyWorkspaceState | undefined {
|
|
1056
|
-
const repo = spawnSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf-8" });
|
|
1056
|
+
const repo = spawnSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf-8", windowsHide: true });
|
|
1057
1057
|
if (repo.status !== 0 || !repo.stdout.trim()) return undefined;
|
|
1058
1058
|
const repoRoot = fs.realpathSync(repo.stdout.trim());
|
|
1059
|
-
const head = spawnSync("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf-8" });
|
|
1060
|
-
const diff = spawnSync("git", ["diff", "--binary", "--full-index", "HEAD", "--"], { cwd: repoRoot, encoding: "utf-8", maxBuffer: 50 * 1024 * 1024 });
|
|
1059
|
+
const head = spawnSync("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf-8", windowsHide: true });
|
|
1060
|
+
const diff = spawnSync("git", ["diff", "--binary", "--full-index", "HEAD", "--"], { cwd: repoRoot, encoding: "utf-8", maxBuffer: 50 * 1024 * 1024, windowsHide: true });
|
|
1061
1061
|
if (head.status !== 0 || diff.status !== 0 || !head.stdout.trim()) return undefined;
|
|
1062
1062
|
return {
|
|
1063
1063
|
kind: "git-tracked",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isDynamicParallelStep, isParallelStep, type ChainStep, type SequentialStep } from "../../shared/settings.ts";
|
|
2
2
|
import type { ChainOutputMap, ChainOutputMapEntry, SingleResult } from "../../shared/types.ts";
|
|
3
3
|
import { getSingleResultOutput } from "../../shared/utils.ts";
|
|
4
4
|
import { DynamicFanoutError, hasDynamicFanoutFields, type DynamicFanoutConfig, validateDynamicStepShape } from "./dynamic-fanout.ts";
|
|
@@ -14,7 +14,6 @@ export interface ChainOutputValidationContext {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function outputNamesForStep(step: ChainStep): string[] {
|
|
17
|
-
if (isCheckpointStep(step)) return [];
|
|
18
17
|
if (isParallelStep(step)) return step.parallel.map((task) => task.as).filter((name): name is string => Boolean(name));
|
|
19
18
|
if (isDynamicParallelStep(step)) return [step.collect.as];
|
|
20
19
|
const name = (step as SequentialStep).as;
|
|
@@ -22,7 +21,6 @@ function outputNamesForStep(step: ChainStep): string[] {
|
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
function taskTemplatesForStep(step: ChainStep): string[] {
|
|
25
|
-
if (isCheckpointStep(step)) return [];
|
|
26
24
|
if (isParallelStep(step)) return step.parallel.map((task) => task.task ?? "{previous}");
|
|
27
25
|
if (isDynamicParallelStep(step)) return [step.parallel.task ?? "{previous}", step.parallel.label ?? ""].filter(Boolean);
|
|
28
26
|
return [(step as SequentialStep).task ?? "{previous}"];
|