pi-subagents 0.80.0 → 0.81.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 +28 -0
- package/bundle/agents.js +18 -0
- package/bundle/background-work.js +16 -0
- package/bundle/capability-ceiling.js +22 -0
- package/bundle/child-tool-plan.js +11 -0
- package/bundle/chunk-26C3WLYQ.js +8388 -0
- package/bundle/chunk-2XYWN7QJ.js +128 -0
- package/bundle/chunk-2ZSNH26H.js +4164 -0
- package/bundle/chunk-3DC5KBVP.js +235 -0
- package/bundle/chunk-3IF2PGW7.js +380 -0
- package/bundle/chunk-4QFZZV3O.js +57 -0
- package/bundle/chunk-563BHAZ5.js +1365 -0
- package/bundle/chunk-5LFLVSTM.js +260 -0
- package/bundle/chunk-62OCY2S4.js +14 -0
- package/bundle/chunk-74RT5YQE.js +326 -0
- package/bundle/chunk-7F6RKT6W.js +132 -0
- package/bundle/chunk-7J4ZXMY7.js +18 -0
- package/bundle/chunk-ARRKBB2B.js +179 -0
- package/bundle/chunk-BJN5LUQY.js +458 -0
- package/bundle/chunk-BPS2AGBZ.js +5421 -0
- package/bundle/chunk-CEKS3TFO.js +1337 -0
- package/bundle/chunk-CTPAE36Y.js +94 -0
- package/bundle/chunk-D4EHT5SJ.js +14 -0
- package/bundle/chunk-EYNJ6VL6.js +526 -0
- package/bundle/chunk-G5DKFLLI.js +1185 -0
- package/bundle/chunk-KSWQVGDW.js +1027 -0
- package/bundle/chunk-L5ZHE4L4.js +946 -0
- package/bundle/chunk-LSRUTHT7.js +57 -0
- package/bundle/chunk-N2BN7BF5.js +159 -0
- package/bundle/chunk-NF7IEKBH.js +490 -0
- package/bundle/chunk-NIXWDZZV.js +9 -0
- package/bundle/chunk-NNLASTQE.js +4082 -0
- package/bundle/chunk-TUWLXTAM.js +935 -0
- package/bundle/chunk-UKG4V45O.js +228 -0
- package/bundle/chunk-V2JWDB5U.js +753 -0
- package/bundle/chunk-V3BIUPLU.js +9163 -0
- package/bundle/chunk-V6MR5TOQ.js +270 -0
- package/bundle/chunk-VJHOELQP.js +8245 -0
- package/bundle/chunk-WSBNENFS.js +732 -0
- package/bundle/chunk-XMFLCKMQ.js +16664 -0
- package/bundle/chunk-YFCK7H2S.js +129 -0
- package/bundle/chunk-YYREO77V.js +137 -0
- package/bundle/chunk-Z7KVHQHL.js +173 -0
- package/bundle/chunk-ZBC44JJ3.js +8606 -0
- package/bundle/chunk-ZP3HODFH.js +175 -0
- package/bundle/control-channel.js +11 -0
- package/bundle/delegation.js +14 -0
- package/bundle/external-job-provider.js +20 -0
- package/bundle/external-runs.js +21 -0
- package/bundle/index.js +30 -0
- package/bundle/inspectors.js +8 -0
- package/bundle/intercom-bridge.js +11 -0
- package/bundle/preflight.js +425 -0
- package/bundle/project-panes.js +28 -0
- package/bundle/required-child-extensions.js +6 -0
- package/bundle/shared-types.js +8 -0
- package/bundle/workflow-resources.js +7 -0
- package/docs/extension-api.md +27 -0
- package/docs/observability.md +6 -2
- package/package.json +18 -18
- package/src/agents/frontmatter.d.ts +2 -0
- package/src/agents/frontmatter.js +9 -2
- package/src/api/delegation.d.ts +20 -0
- package/src/extension/index.js +27 -7
- package/src/runs/background/run-child-session.js +18 -0
- package/src/runs/background/stale-run-reconciler.js +2 -0
- package/src/runs/foreground/execution.js +39 -2
- package/src/runs/shared/structured-output.d.ts +1 -0
- package/src/runs/shared/structured-output.js +1 -0
- package/src/shared/accessible-dir.js +4 -1
- package/src/shared/provider-diagnostics.d.ts +9 -0
- package/src/shared/provider-diagnostics.js +75 -0
- package/src/shared/types.d.ts +3 -0
- package/src/slash/delegation-adapters.d.ts +3 -1
- package/src/slash/delegation-adapters.js +7 -0
- package/src/slash/prompt-template-bridge.js +3 -1
- package/src/slash/subagents-admin.d.ts +1 -0
- package/src/slash/subagents-admin.js +1 -1
- package/src/tui/fleet-layout.d.ts +20 -0
- package/src/tui/fleet-layout.js +17 -0
- package/src/tui/fleet-viewport.d.ts +12 -0
- package/src/tui/fleet-viewport.js +35 -0
- package/src/tui/fleet.d.ts +5 -6
- package/src/tui/fleet.js +69 -51
- package/src/workflows/structured-workflow-scripts.d.ts +1 -0
- package/src/workflows/structured-workflow-scripts.js +5 -4
- package/src/workflows/workflow-resources.js +2 -2
package/src/tui/fleet.js
CHANGED
|
@@ -2,6 +2,8 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
5
|
+
import { fleetContains, fleetLayout } from "./fleet-layout.js";
|
|
6
|
+
import { FleetViewport } from "./fleet-viewport.js";
|
|
5
7
|
import { snapshotExternalRuns } from "../api/external-runs.js";
|
|
6
8
|
import { getArtifactPaths, getArtifactsDir } from "../shared/artifacts.js";
|
|
7
9
|
import { formatDuration, formatModelThinking, formatTokens, formatTokenUsage, shortenPath } from "../shared/formatters.js";
|
|
@@ -746,11 +748,8 @@ export class SubagentFleetComponent {
|
|
|
746
748
|
snapshot = { items: [] };
|
|
747
749
|
selected = 0;
|
|
748
750
|
selectedKey;
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
detailLineCount = 0;
|
|
752
|
-
detailViewportHeight = 8;
|
|
753
|
-
bodyHeight = 8;
|
|
751
|
+
viewport;
|
|
752
|
+
renderedLayout;
|
|
754
753
|
expandedTools = false;
|
|
755
754
|
promptAuditOpen = false;
|
|
756
755
|
promptAuditView = "authored";
|
|
@@ -773,6 +772,7 @@ export class SubagentFleetComponent {
|
|
|
773
772
|
keybindings;
|
|
774
773
|
constructor(tui, theme, state, done, options = {}) {
|
|
775
774
|
this.tui = tui;
|
|
775
|
+
this.viewport = new FleetViewport(() => this.tui.requestRender());
|
|
776
776
|
this.theme = theme;
|
|
777
777
|
this.markdownTheme = options.markdownTheme ?? getMarkdownTheme();
|
|
778
778
|
this.state = state;
|
|
@@ -820,11 +820,14 @@ export class SubagentFleetComponent {
|
|
|
820
820
|
this.selectedKey = this.snapshot.items[this.selected]?.key;
|
|
821
821
|
}
|
|
822
822
|
moveSelection(delta) {
|
|
823
|
-
|
|
823
|
+
this.selectItem(Math.max(0, Math.min(this.snapshot.items.length - 1, this.selected + delta)));
|
|
824
|
+
}
|
|
825
|
+
selectItem(index) {
|
|
826
|
+
if (!this.snapshot.items[index])
|
|
824
827
|
return;
|
|
825
|
-
this.selected =
|
|
828
|
+
this.selected = index;
|
|
826
829
|
this.selectedKey = this.snapshot.items[this.selected]?.key;
|
|
827
|
-
this.
|
|
830
|
+
this.viewport.follow();
|
|
828
831
|
this.resetActionInput();
|
|
829
832
|
this.tui.requestRender();
|
|
830
833
|
}
|
|
@@ -862,8 +865,7 @@ export class SubagentFleetComponent {
|
|
|
862
865
|
return;
|
|
863
866
|
this.selected = this.snapshot.items.findIndex((item) => item.key === target.key);
|
|
864
867
|
this.selectedKey = target.key;
|
|
865
|
-
this.
|
|
866
|
-
this.detailAutoFollow = false;
|
|
868
|
+
this.viewport.resetTop();
|
|
867
869
|
this.tui.requestRender();
|
|
868
870
|
}
|
|
869
871
|
resetActionInput() {
|
|
@@ -951,8 +953,7 @@ export class SubagentFleetComponent {
|
|
|
951
953
|
setActionNotice(result) {
|
|
952
954
|
this.actionNotice = result;
|
|
953
955
|
this.resetActionInput();
|
|
954
|
-
this.
|
|
955
|
-
this.detailScroll = 0;
|
|
956
|
+
this.viewport.resetTop();
|
|
956
957
|
this.refresh();
|
|
957
958
|
this.tui.requestRender();
|
|
958
959
|
}
|
|
@@ -972,10 +973,35 @@ export class SubagentFleetComponent {
|
|
|
972
973
|
});
|
|
973
974
|
}
|
|
974
975
|
scrollDetail(delta) {
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
976
|
+
this.viewport.scroll.scrollBy(delta);
|
|
977
|
+
}
|
|
978
|
+
handleMouse(event) {
|
|
979
|
+
// Block pointer fallback as well as inspector actions while a text/confirmation modal owns input.
|
|
980
|
+
if (this.steerDraft !== undefined || this.redoGuidanceDraft !== undefined || this.stopConfirming || this.actionBusy) {
|
|
981
|
+
return { handled: true, render: false };
|
|
982
|
+
}
|
|
983
|
+
const layout = this.renderedLayout;
|
|
984
|
+
if (!layout || layout.width !== event.width || layout.rows !== (this.tui.terminal?.rows ?? 32)) {
|
|
985
|
+
// Never use hit regions from a different size; wheel must not escape to the transcript.
|
|
986
|
+
return event.type === "wheel" ? { handled: true, render: false } : undefined;
|
|
987
|
+
}
|
|
988
|
+
if (event.type === "wheel" && fleetContains(layout.output, event.x, event.y)) {
|
|
989
|
+
this.scrollDetail(event.wheelDelta ?? 0);
|
|
990
|
+
return { handled: true, render: false };
|
|
991
|
+
}
|
|
992
|
+
if (this.promptAuditOpen)
|
|
993
|
+
return { handled: true, render: false };
|
|
994
|
+
if (event.type === "click" && event.button === "left" && !event.shift && !event.alt && !event.ctrl
|
|
995
|
+
&& fleetContains(layout.roster, event.x, event.y)) {
|
|
996
|
+
const key = layout.rosterKeys[event.y - layout.roster.y];
|
|
997
|
+
const index = key === undefined ? -1 : this.snapshot.items.findIndex((item) => item.key === key);
|
|
998
|
+
if (index < 0)
|
|
999
|
+
return undefined;
|
|
1000
|
+
this.selectItem(index);
|
|
1001
|
+
return { handled: true, render: false };
|
|
1002
|
+
}
|
|
1003
|
+
// Unowned wheel inside the overlay would otherwise scroll the transcript behind it.
|
|
1004
|
+
return event.type === "wheel" ? { handled: true, render: false } : undefined;
|
|
979
1005
|
}
|
|
980
1006
|
handleInput(data) {
|
|
981
1007
|
if (this.promptAuditOpen && this.redoGuidanceDraft !== undefined) {
|
|
@@ -1012,7 +1038,7 @@ export class SubagentFleetComponent {
|
|
|
1012
1038
|
if (this.promptAuditOpen) {
|
|
1013
1039
|
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
1014
1040
|
this.promptAuditOpen = false;
|
|
1015
|
-
this.
|
|
1041
|
+
this.viewport.follow();
|
|
1016
1042
|
this.tui.requestRender();
|
|
1017
1043
|
return;
|
|
1018
1044
|
}
|
|
@@ -1022,7 +1048,7 @@ export class SubagentFleetComponent {
|
|
|
1022
1048
|
return this.movePromptSelection(-1);
|
|
1023
1049
|
if (data === "1" || data === "2" || data === "3") {
|
|
1024
1050
|
this.promptAuditView = data === "1" ? "authored" : data === "2" ? "runtime" : "effective";
|
|
1025
|
-
this.
|
|
1051
|
+
this.viewport.resetTop();
|
|
1026
1052
|
this.tui.requestRender();
|
|
1027
1053
|
return;
|
|
1028
1054
|
}
|
|
@@ -1037,8 +1063,7 @@ export class SubagentFleetComponent {
|
|
|
1037
1063
|
else {
|
|
1038
1064
|
this.actionNotice = undefined;
|
|
1039
1065
|
this.redoGuidanceDraft = "";
|
|
1040
|
-
this.
|
|
1041
|
-
this.detailScroll = 0;
|
|
1066
|
+
this.viewport.resetTop();
|
|
1042
1067
|
this.tui.requestRender();
|
|
1043
1068
|
}
|
|
1044
1069
|
return;
|
|
@@ -1061,9 +1086,9 @@ export class SubagentFleetComponent {
|
|
|
1061
1086
|
if (matchesFleetAction(data, this.keybindings, "scrollDown"))
|
|
1062
1087
|
return this.scrollDetail(1);
|
|
1063
1088
|
if (matchesFleetAction(data, this.keybindings, "pageUp"))
|
|
1064
|
-
return this.scrollDetail(-this.
|
|
1089
|
+
return this.scrollDetail(-this.viewport.scroll.viewportHeight);
|
|
1065
1090
|
if (matchesFleetAction(data, this.keybindings, "pageDown"))
|
|
1066
|
-
return this.scrollDetail(this.
|
|
1091
|
+
return this.scrollDetail(this.viewport.scroll.viewportHeight);
|
|
1067
1092
|
return;
|
|
1068
1093
|
}
|
|
1069
1094
|
if (this.steerDraft !== undefined) {
|
|
@@ -1137,9 +1162,9 @@ export class SubagentFleetComponent {
|
|
|
1137
1162
|
if (matchesFleetAction(data, this.keybindings, "selectLast"))
|
|
1138
1163
|
return this.moveSelection(this.snapshot.items.length);
|
|
1139
1164
|
if (matchesFleetAction(data, this.keybindings, "pageUp"))
|
|
1140
|
-
return this.scrollDetail(-this.
|
|
1165
|
+
return this.scrollDetail(-this.viewport.scroll.viewportHeight);
|
|
1141
1166
|
if (matchesFleetAction(data, this.keybindings, "pageDown"))
|
|
1142
|
-
return this.scrollDetail(this.
|
|
1167
|
+
return this.scrollDetail(this.viewport.scroll.viewportHeight);
|
|
1143
1168
|
if (matchesFleetAction(data, this.keybindings, "refresh")) {
|
|
1144
1169
|
this.transcriptCache = undefined;
|
|
1145
1170
|
this.refresh();
|
|
@@ -1152,8 +1177,7 @@ export class SubagentFleetComponent {
|
|
|
1152
1177
|
else {
|
|
1153
1178
|
this.promptAuditOpen = true;
|
|
1154
1179
|
this.promptAuditView = "authored";
|
|
1155
|
-
this.
|
|
1156
|
-
this.detailAutoFollow = false;
|
|
1180
|
+
this.viewport.resetTop();
|
|
1157
1181
|
this.actionNotice = undefined;
|
|
1158
1182
|
this.tui.requestRender();
|
|
1159
1183
|
}
|
|
@@ -1166,8 +1190,7 @@ export class SubagentFleetComponent {
|
|
|
1166
1190
|
else {
|
|
1167
1191
|
this.actionNotice = undefined;
|
|
1168
1192
|
this.steerDraft = "";
|
|
1169
|
-
this.
|
|
1170
|
-
this.detailScroll = 0;
|
|
1193
|
+
this.viewport.resetTop();
|
|
1171
1194
|
this.tui.requestRender();
|
|
1172
1195
|
}
|
|
1173
1196
|
return;
|
|
@@ -1179,8 +1202,7 @@ export class SubagentFleetComponent {
|
|
|
1179
1202
|
else {
|
|
1180
1203
|
this.actionNotice = undefined;
|
|
1181
1204
|
this.stopConfirming = true;
|
|
1182
|
-
this.
|
|
1183
|
-
this.detailScroll = 0;
|
|
1205
|
+
this.viewport.resetTop();
|
|
1184
1206
|
this.tui.requestRender();
|
|
1185
1207
|
}
|
|
1186
1208
|
return;
|
|
@@ -1196,18 +1218,18 @@ export class SubagentFleetComponent {
|
|
|
1196
1218
|
return;
|
|
1197
1219
|
}
|
|
1198
1220
|
}
|
|
1199
|
-
rosterLines(
|
|
1221
|
+
rosterLines(layout) {
|
|
1200
1222
|
if (this.snapshot.items.length === 0)
|
|
1201
1223
|
return [this.theme.fg("dim", "No tracked children")];
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
const
|
|
1224
|
+
return layout.rosterKeys.map((_key, offset) => {
|
|
1225
|
+
const index = layout.rosterStart + offset;
|
|
1226
|
+
const item = this.snapshot.items[index];
|
|
1205
1227
|
const marker = index === this.selected ? this.theme.fg("accent", "›") : " ";
|
|
1206
1228
|
const context = item.kind === "async" ? contextModeBadge(this.theme, item.step?.context ?? item.run.context) : item.kind === "foreground-recent" ? contextModeBadge(this.theme, item.child.context) : "";
|
|
1207
1229
|
const agent = index === this.selected ? this.theme.bold(item.agent) : item.agent;
|
|
1208
1230
|
const identity = item.runId.slice(0, 8);
|
|
1209
1231
|
const left = `${marker} ${statusGlyph(item, this.theme)} ${agent}${context} ${this.theme.fg("dim", `· ${identity}`)}`;
|
|
1210
|
-
return rightAligned(left, this.theme.fg("dim", item.state), width);
|
|
1232
|
+
return rightAligned(left, this.theme.fg("dim", item.state), layout.roster.width);
|
|
1211
1233
|
});
|
|
1212
1234
|
}
|
|
1213
1235
|
renderedTranscript(target, width) {
|
|
@@ -1305,26 +1327,21 @@ export class SubagentFleetComponent {
|
|
|
1305
1327
|
return { header: [], body: this.withActionLines(lines) };
|
|
1306
1328
|
}
|
|
1307
1329
|
render(width) {
|
|
1308
|
-
|
|
1330
|
+
const layout = fleetLayout(width, this.tui.terminal?.rows ?? 32, this.selected, this.snapshot.items);
|
|
1331
|
+
this.renderedLayout = layout;
|
|
1332
|
+
if (!layout)
|
|
1309
1333
|
return [truncateToWidth("Subagent fleet needs at least 36 columns. Esc closes.", width)];
|
|
1310
1334
|
const innerWidth = width - 2;
|
|
1311
|
-
const
|
|
1312
|
-
|
|
1313
|
-
const
|
|
1314
|
-
const
|
|
1315
|
-
const roster = this.rosterLines(rosterWidth);
|
|
1335
|
+
const { bodyHeight } = layout;
|
|
1336
|
+
const rosterWidth = layout.roster.width;
|
|
1337
|
+
const detailWidth = layout.output.width;
|
|
1338
|
+
const roster = this.rosterLines(layout);
|
|
1316
1339
|
const detail = this.wrappedDetail(detailWidth);
|
|
1317
|
-
const detailHeader = detail.header.slice(0, Math.max(0,
|
|
1318
|
-
|
|
1319
|
-
this.detailLineCount = detail.body.length;
|
|
1320
|
-
const maxDetailScroll = Math.max(0, detail.body.length - this.detailViewportHeight);
|
|
1321
|
-
if (this.detailAutoFollow)
|
|
1322
|
-
this.detailScroll = maxDetailScroll;
|
|
1323
|
-
else if (this.detailScroll > maxDetailScroll)
|
|
1324
|
-
this.detailScroll = maxDetailScroll;
|
|
1340
|
+
const detailHeader = detail.header.slice(0, Math.max(0, bodyHeight - 1));
|
|
1341
|
+
const outputBodyHeight = bodyHeight - detailHeader.length;
|
|
1325
1342
|
const visibleDetails = [
|
|
1326
1343
|
...detailHeader,
|
|
1327
|
-
...
|
|
1344
|
+
...this.viewport.render(detail.body, detailWidth, outputBodyHeight),
|
|
1328
1345
|
];
|
|
1329
1346
|
const lines = [this.theme.fg("border", `╭${"─".repeat(innerWidth)}╮`)];
|
|
1330
1347
|
const selected = this.snapshot.items[this.selected];
|
|
@@ -1336,7 +1353,7 @@ export class SubagentFleetComponent {
|
|
|
1336
1353
|
: this.theme.fg("dim", "no children ");
|
|
1337
1354
|
lines.push(this.theme.fg("border", "│") + rightAligned(title, selectedStatus, innerWidth) + this.theme.fg("border", "│"));
|
|
1338
1355
|
lines.push(this.theme.fg("border", `├${"─".repeat(rosterWidth)}┬${"─".repeat(detailWidth)}┤`));
|
|
1339
|
-
for (let index = 0; index <
|
|
1356
|
+
for (let index = 0; index < bodyHeight; index++) {
|
|
1340
1357
|
lines.push(this.theme.fg("border", "│")
|
|
1341
1358
|
+ fit(roster[index] ?? "", rosterWidth)
|
|
1342
1359
|
+ this.theme.fg("border", "│")
|
|
@@ -1355,6 +1372,7 @@ export class SubagentFleetComponent {
|
|
|
1355
1372
|
return lines.map((line) => truncateToWidth(line, width));
|
|
1356
1373
|
}
|
|
1357
1374
|
invalidate() {
|
|
1375
|
+
this.renderedLayout = undefined;
|
|
1358
1376
|
this.transcriptCache = undefined;
|
|
1359
1377
|
this.refresh();
|
|
1360
1378
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* and never becomes code. Scripts run on the ordinary workflow runtime.
|
|
5
5
|
*/
|
|
6
6
|
export type StructuredWorkflowKind = "tasks" | "chain";
|
|
7
|
+
export declare const TOP_LEVEL_CONTROLS_HINT = " Put worktree and other child controls on the subagent call itself; they apply to every child.";
|
|
7
8
|
export declare function isPlainRecord(value: unknown): value is Record<string, unknown>;
|
|
8
9
|
/** Validate exact data shapes and expand them into a package-owned workflow script. */
|
|
9
10
|
export declare function buildStructuredWorkflowScript(kind: StructuredWorkflowKind, steps: unknown, originalTask: string | undefined): string;
|
|
@@ -7,6 +7,7 @@ const OUTPUT_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
|
7
7
|
const PLACEHOLDER_PATTERN = /\{(?:task|previous|outputs\.([A-Za-z_][A-Za-z0-9_]*))\}/g;
|
|
8
8
|
/** Optional per-child launch fields copied verbatim onto the generated runs.all item. */
|
|
9
9
|
const CHILD_FIELDS = ["cwd", "model"];
|
|
10
|
+
export const TOP_LEVEL_CONTROLS_HINT = " Put worktree and other child controls on the subagent call itself; they apply to every child.";
|
|
10
11
|
export function isPlainRecord(value) {
|
|
11
12
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
12
13
|
return false;
|
|
@@ -97,7 +98,7 @@ function buildTasksScript(steps, originalTask) {
|
|
|
97
98
|
throw new Error("tasks must be a non-empty array of { agent, task } items.");
|
|
98
99
|
const children = steps.map((item, index) => {
|
|
99
100
|
const path = `tasks[${index}]`;
|
|
100
|
-
const record = exactFields(item, path, ["agent", "task", ...CHILD_FIELDS],
|
|
101
|
+
const record = exactFields(item, path, ["agent", "task", ...CHILD_FIELDS], `Each tasks item accepts agent and task, plus optional cwd and model.${TOP_LEVEL_CONTROLS_HINT}`);
|
|
101
102
|
const agent = nonEmptyString(record.agent, `${path}.agent`);
|
|
102
103
|
const task = nonEmptyString(record.task, `${path}.task`);
|
|
103
104
|
const taskExpression = compileTemplate(task, {
|
|
@@ -131,20 +132,20 @@ function buildChainScript(steps, originalTask) {
|
|
|
131
132
|
let stepChildren;
|
|
132
133
|
let outputName;
|
|
133
134
|
if (isPlainRecord(step) && Object.hasOwn(step, "parallel")) {
|
|
134
|
-
const record = exactFields(step, path, ["parallel"],
|
|
135
|
+
const record = exactFields(step, path, ["parallel"], `A parallel step accepts only parallel; as is supported on sequential steps only.${TOP_LEVEL_CONTROLS_HINT}`);
|
|
135
136
|
const items = record.parallel;
|
|
136
137
|
if (!Array.isArray(items) || items.length === 0)
|
|
137
138
|
throw new Error(`${path}.parallel must be a non-empty array of { agent, task } items.`);
|
|
138
139
|
stepChildren = items.map((item, itemIndex) => {
|
|
139
140
|
const itemPath = `${path}.parallel[${itemIndex}]`;
|
|
140
|
-
const itemRecord = exactFields(item, itemPath, ["agent", "task", ...CHILD_FIELDS],
|
|
141
|
+
const itemRecord = exactFields(item, itemPath, ["agent", "task", ...CHILD_FIELDS], `Each parallel item accepts agent and task, plus optional cwd and model.${TOP_LEVEL_CONTROLS_HINT}`);
|
|
141
142
|
const agent = nonEmptyString(itemRecord.agent, `${itemPath}.agent`);
|
|
142
143
|
const task = nonEmptyString(itemRecord.task, `${itemPath}.task`);
|
|
143
144
|
return { key: `step-${number}-${itemIndex + 1}`, agent, fields: childFields(itemRecord, itemPath), taskExpression: compileTemplate(task, { path: `${itemPath}.task`, hasOriginalTask, previous, outputs }) };
|
|
144
145
|
});
|
|
145
146
|
}
|
|
146
147
|
else {
|
|
147
|
-
const record = exactFields(step, path, ["agent", "task", "as", ...CHILD_FIELDS],
|
|
148
|
+
const record = exactFields(step, path, ["agent", "task", "as", ...CHILD_FIELDS], `A sequential step accepts agent, task, as, cwd and model; a parallel step accepts only parallel.${TOP_LEVEL_CONTROLS_HINT}`);
|
|
148
149
|
const agent = nonEmptyString(record.agent, `${path}.agent`);
|
|
149
150
|
let task;
|
|
150
151
|
if (record.task === undefined) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { stableJsonDigest } from "../shared/launch-contract.js";
|
|
3
3
|
import { createWorkflowResourcePermit, } from "../shared/workflow-child-permit.js";
|
|
4
|
-
import { buildStructuredWorkflowScript, isPlainRecord } from "./structured-workflow-scripts.js";
|
|
4
|
+
import { buildStructuredWorkflowScript, isPlainRecord, TOP_LEVEL_CONTROLS_HINT } from "./structured-workflow-scripts.js";
|
|
5
5
|
const RESOURCE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
6
6
|
export const MAX_ARGS_BYTES = 16 * 1024;
|
|
7
7
|
export const MAX_ARGS_FIELDS = 16;
|
|
@@ -152,7 +152,7 @@ function resolveRunCi(args) {
|
|
|
152
152
|
function resolveParallel(args) {
|
|
153
153
|
const unsupported = Object.keys(args).filter((key) => key !== "tasks");
|
|
154
154
|
if (unsupported.length > 0)
|
|
155
|
-
return { error: `workflow 'parallel' args contain unsupported fields: ${unsupported.join(", ")}. Pass args.tasks: [{ agent, task, cwd?, model? }, ...]
|
|
155
|
+
return { error: `workflow 'parallel' args contain unsupported fields: ${unsupported.join(", ")}. Pass args.tasks: [{ agent, task, cwd?, model? }, ...].${TOP_LEVEL_CONTROLS_HINT}` };
|
|
156
156
|
try {
|
|
157
157
|
return { script: buildStructuredWorkflowScript("tasks", args.tasks, undefined) };
|
|
158
158
|
}
|