@agwab/pi-workflow 0.2.0 → 0.2.1
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/README.md +2 -0
- package/dist/compiler.d.ts +4 -6
- package/dist/compiler.js +64 -31
- package/dist/dynamic-generated-task-runtime.d.ts +2 -0
- package/dist/dynamic-generated-task-runtime.js +21 -8
- package/dist/engine.d.ts +5 -6
- package/dist/engine.js +36 -52
- package/dist/extension.js +11 -9
- package/dist/store.d.ts +3 -1
- package/dist/store.js +134 -38
- package/dist/subagent-backend.d.ts +4 -0
- package/dist/subagent-backend.js +128 -4
- package/dist/types.d.ts +3 -1
- package/dist/workflow-runtime.d.ts +2 -0
- package/dist/workflow-runtime.js +30 -0
- package/docs/usage.md +11 -0
- package/package.json +1 -1
- package/src/compiler.ts +113 -57
- package/src/dynamic-generated-task-runtime.ts +47 -12
- package/src/engine.ts +49 -85
- package/src/extension.ts +18 -14
- package/src/store.ts +179 -44
- package/src/subagent-backend.ts +170 -6
- package/src/types.ts +7 -1
- package/src/workflow-runtime.ts +35 -0
package/src/compiler.ts
CHANGED
|
@@ -33,7 +33,10 @@ import {
|
|
|
33
33
|
} from "./types.js";
|
|
34
34
|
import {
|
|
35
35
|
resolveWorkflowRuntime,
|
|
36
|
+
selectWorkflowRuntime,
|
|
36
37
|
type WorkflowModelInfo,
|
|
38
|
+
type WorkflowRuntimeDefaults,
|
|
39
|
+
type WorkflowRuntimeResolutionInput,
|
|
37
40
|
} from "./workflow-runtime.js";
|
|
38
41
|
|
|
39
42
|
const DELEGATION_TOOLS = new Set([
|
|
@@ -549,7 +552,8 @@ export async function compileWorkflow(
|
|
|
549
552
|
spec: ArtifactGraphWorkflowSpec,
|
|
550
553
|
options: CompileOptions & {
|
|
551
554
|
task?: string;
|
|
552
|
-
|
|
555
|
+
runtimeOverrides?: WorkflowRuntimeDefaults;
|
|
556
|
+
runtimeDefaults?: WorkflowRuntimeDefaults;
|
|
553
557
|
},
|
|
554
558
|
): Promise<any> {
|
|
555
559
|
const compilePlan = buildArtifactGraphCompilePlan(spec, options);
|
|
@@ -615,11 +619,25 @@ async function collectForeachPathWarnings(
|
|
|
615
619
|
return warnings;
|
|
616
620
|
}
|
|
617
621
|
|
|
622
|
+
function runtimeSettings(value: unknown): WorkflowRuntimeDefaults | undefined {
|
|
623
|
+
if (!isPlainRecord(value)) return undefined;
|
|
624
|
+
const model =
|
|
625
|
+
typeof value.model === "string" && value.model.trim()
|
|
626
|
+
? value.model.trim()
|
|
627
|
+
: undefined;
|
|
628
|
+
const thinking =
|
|
629
|
+
typeof value.thinking === "string" && value.thinking.trim()
|
|
630
|
+
? (value.thinking.trim() as ThinkingLevel)
|
|
631
|
+
: undefined;
|
|
632
|
+
return model || thinking ? { model, thinking } : undefined;
|
|
633
|
+
}
|
|
634
|
+
|
|
618
635
|
async function compileArtifactGraphPlan(
|
|
619
636
|
spec: any,
|
|
620
637
|
options: CompileOptions & {
|
|
621
638
|
task?: string;
|
|
622
|
-
|
|
639
|
+
runtimeOverrides?: WorkflowRuntimeDefaults;
|
|
640
|
+
runtimeDefaults?: WorkflowRuntimeDefaults;
|
|
623
641
|
},
|
|
624
642
|
): Promise<any> {
|
|
625
643
|
const stages = spec.stages;
|
|
@@ -665,9 +683,9 @@ async function compileArtifactGraphPlan(
|
|
|
665
683
|
Object.keys(workflowInput).length > 0
|
|
666
684
|
? `# Workflow Input\n\n${JSON.stringify(workflowInput, null, 2)}`
|
|
667
685
|
: "";
|
|
668
|
-
const
|
|
669
|
-
const
|
|
670
|
-
|
|
686
|
+
const runtimeOverrides = options.runtimeOverrides;
|
|
687
|
+
const runtimeDefaults = options.runtimeDefaults;
|
|
688
|
+
const specRuntimeDefaults = runtimeSettings(spec.defaults);
|
|
671
689
|
const tasks: any[] = [];
|
|
672
690
|
const stageRecords: any[] = [];
|
|
673
691
|
const issues: ValidationIssue[] = [];
|
|
@@ -719,6 +737,22 @@ async function compileArtifactGraphPlan(
|
|
|
719
737
|
dynamicToolPath,
|
|
720
738
|
);
|
|
721
739
|
const dynamicToolSelection = filterToolSelection(rawDynamicToolSelection);
|
|
740
|
+
const requestedRuntime = selectWorkflowRuntime(
|
|
741
|
+
runtimeOverrides,
|
|
742
|
+
runtimeSettings(stage),
|
|
743
|
+
runtimeDefaults,
|
|
744
|
+
specRuntimeDefaults,
|
|
745
|
+
);
|
|
746
|
+
const resolvedDynamicRuntime = await resolveWorkflowRuntime(
|
|
747
|
+
requestedRuntime,
|
|
748
|
+
{
|
|
749
|
+
taskKey: key,
|
|
750
|
+
stageId: stage.id,
|
|
751
|
+
taskId,
|
|
752
|
+
agent: "dynamic",
|
|
753
|
+
},
|
|
754
|
+
{ availableModels: options.availableModels },
|
|
755
|
+
);
|
|
722
756
|
const dynamicTask = buildDynamicTask(
|
|
723
757
|
stage,
|
|
724
758
|
taskId,
|
|
@@ -729,24 +763,22 @@ async function compileArtifactGraphPlan(
|
|
|
729
763
|
specDir,
|
|
730
764
|
workflowInputText,
|
|
731
765
|
options.task,
|
|
732
|
-
|
|
733
|
-
defaultThinking,
|
|
734
|
-
overrides,
|
|
735
|
-
);
|
|
736
|
-
const resolvedDynamicRuntime = await resolveWorkflowRuntime(
|
|
737
|
-
{ model: defaultModel, thinking: defaultThinking },
|
|
766
|
+
resolvedDynamicRuntime,
|
|
738
767
|
{
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
768
|
+
runtimeOverrides,
|
|
769
|
+
runtimeDefaults,
|
|
770
|
+
specRuntimeDefaults,
|
|
771
|
+
stageRuntime: runtimeSettings(stage),
|
|
743
772
|
},
|
|
744
|
-
|
|
773
|
+
overrides,
|
|
745
774
|
);
|
|
746
775
|
dynamicTask.runtime = {
|
|
747
776
|
...dynamicTask.runtime,
|
|
748
777
|
...resolvedDynamicRuntime,
|
|
749
778
|
};
|
|
779
|
+
if (options.availableModels?.length) {
|
|
780
|
+
dynamicTask.dynamic.availableModels = options.availableModels;
|
|
781
|
+
}
|
|
750
782
|
if (dynamicToolSelection.tools || dynamicToolSelection.toolProviders) {
|
|
751
783
|
dynamicTask.runtime = {
|
|
752
784
|
...dynamicTask.runtime,
|
|
@@ -823,15 +855,12 @@ async function compileArtifactGraphPlan(
|
|
|
823
855
|
validateToolSubset(toolSelection.tools, stageAgent, issues, toolPath);
|
|
824
856
|
validateDelegationBoundary(toolSelection.tools, issues, toolPath);
|
|
825
857
|
const filteredToolSelection = filterToolSelection(toolSelection);
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
stage.thinking ??
|
|
833
|
-
spec.defaults?.thinking,
|
|
834
|
-
};
|
|
858
|
+
const requestedRuntime = selectWorkflowRuntime(
|
|
859
|
+
runtimeOverrides,
|
|
860
|
+
runtimeSettings(stage),
|
|
861
|
+
runtimeDefaults,
|
|
862
|
+
specRuntimeDefaults,
|
|
863
|
+
);
|
|
835
864
|
const resolvedRuntime = await resolveWorkflowRuntime(
|
|
836
865
|
requestedRuntime,
|
|
837
866
|
{
|
|
@@ -1208,12 +1237,34 @@ async function compileArtifactGraphPlan(
|
|
|
1208
1237
|
tasks,
|
|
1209
1238
|
warnings,
|
|
1210
1239
|
budget: {
|
|
1211
|
-
models:
|
|
1240
|
+
models: budgetModelRows(tasks),
|
|
1212
1241
|
unratedModels: [],
|
|
1213
1242
|
},
|
|
1214
1243
|
};
|
|
1215
1244
|
}
|
|
1216
1245
|
|
|
1246
|
+
function budgetModelRows(tasks: any[]): Array<{ model: string }> {
|
|
1247
|
+
const models = new Set<string>();
|
|
1248
|
+
for (const task of tasks) {
|
|
1249
|
+
if (typeof task?.runtime?.model === "string" && task.runtime.model.trim()) {
|
|
1250
|
+
models.add(task.runtime.model.trim());
|
|
1251
|
+
}
|
|
1252
|
+
const loop = task?.dynamic?.decisionLoop;
|
|
1253
|
+
if (!loop || typeof loop !== "object") continue;
|
|
1254
|
+
for (const profile of [
|
|
1255
|
+
loop.planner,
|
|
1256
|
+
loop.workerDefaults,
|
|
1257
|
+
loop.verifier,
|
|
1258
|
+
loop.synthesis,
|
|
1259
|
+
]) {
|
|
1260
|
+
if (typeof profile?.model === "string" && profile.model.trim()) {
|
|
1261
|
+
models.add(profile.model.trim());
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
return [...models].sort().map((model) => ({ model }));
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1217
1268
|
function isSupportStage(stage: any): boolean {
|
|
1218
1269
|
return stage?.support !== undefined && stage?.type === undefined;
|
|
1219
1270
|
}
|
|
@@ -1301,8 +1352,13 @@ function buildDynamicTask(
|
|
|
1301
1352
|
specDir: string,
|
|
1302
1353
|
workflowInputText: string,
|
|
1303
1354
|
runtimeTask: string | undefined,
|
|
1304
|
-
|
|
1305
|
-
|
|
1355
|
+
controllerRuntime: WorkflowRuntimeResolutionInput,
|
|
1356
|
+
runtimePriority: {
|
|
1357
|
+
runtimeOverrides?: WorkflowRuntimeDefaults;
|
|
1358
|
+
runtimeDefaults?: WorkflowRuntimeDefaults;
|
|
1359
|
+
specRuntimeDefaults?: WorkflowRuntimeDefaults;
|
|
1360
|
+
stageRuntime?: WorkflowRuntimeDefaults;
|
|
1361
|
+
},
|
|
1306
1362
|
overrides: Partial<CompiledTask> & Record<string, unknown>,
|
|
1307
1363
|
): any {
|
|
1308
1364
|
const dynamic = stage.dynamic ?? {};
|
|
@@ -1365,8 +1421,7 @@ function buildDynamicTask(
|
|
|
1365
1421
|
}
|
|
1366
1422
|
const decisionLoop = compileDynamicDecisionLoop(
|
|
1367
1423
|
dynamic.decisionLoop,
|
|
1368
|
-
|
|
1369
|
-
defaultThinking,
|
|
1424
|
+
runtimePriority,
|
|
1370
1425
|
);
|
|
1371
1426
|
|
|
1372
1427
|
return {
|
|
@@ -1386,8 +1441,7 @@ function buildDynamicTask(
|
|
|
1386
1441
|
explicitWorktreePolicy: false,
|
|
1387
1442
|
runtime: {
|
|
1388
1443
|
approvalMode: "non-interactive",
|
|
1389
|
-
|
|
1390
|
-
thinking: defaultThinking,
|
|
1444
|
+
...controllerRuntime,
|
|
1391
1445
|
maxRuntimeMs:
|
|
1392
1446
|
dynamic.budget?.maxRuntimeMs ?? DEFAULT_DYNAMIC_MAX_RUNTIME_MS,
|
|
1393
1447
|
},
|
|
@@ -1431,6 +1485,9 @@ function buildDynamicTask(
|
|
|
1431
1485
|
helpers,
|
|
1432
1486
|
workflows,
|
|
1433
1487
|
...(decisionLoop ? { decisionLoop } : {}),
|
|
1488
|
+
...(runtimePriority.runtimeOverrides
|
|
1489
|
+
? { runtimeOverrides: runtimePriority.runtimeOverrides }
|
|
1490
|
+
: {}),
|
|
1434
1491
|
},
|
|
1435
1492
|
...overrides,
|
|
1436
1493
|
};
|
|
@@ -1438,8 +1495,12 @@ function buildDynamicTask(
|
|
|
1438
1495
|
|
|
1439
1496
|
function compileDynamicDecisionLoop(
|
|
1440
1497
|
value: unknown,
|
|
1441
|
-
|
|
1442
|
-
|
|
1498
|
+
runtimePriority: {
|
|
1499
|
+
runtimeOverrides?: WorkflowRuntimeDefaults;
|
|
1500
|
+
runtimeDefaults?: WorkflowRuntimeDefaults;
|
|
1501
|
+
specRuntimeDefaults?: WorkflowRuntimeDefaults;
|
|
1502
|
+
stageRuntime?: WorkflowRuntimeDefaults;
|
|
1503
|
+
},
|
|
1443
1504
|
): any | undefined {
|
|
1444
1505
|
if (!isPlainRecord(value)) return undefined;
|
|
1445
1506
|
const allowedToolSelection = filterToolSelection(
|
|
@@ -1455,25 +1516,18 @@ function compileDynamicDecisionLoop(
|
|
|
1455
1516
|
recordValue(value.stateIndex, "requiredFindingIds"),
|
|
1456
1517
|
);
|
|
1457
1518
|
return {
|
|
1458
|
-
planner: compileDynamicDecisionLoopProfile(
|
|
1459
|
-
value.planner,
|
|
1460
|
-
defaultModel,
|
|
1461
|
-
defaultThinking,
|
|
1462
|
-
),
|
|
1519
|
+
planner: compileDynamicDecisionLoopProfile(value.planner, runtimePriority),
|
|
1463
1520
|
workerDefaults: compileDynamicDecisionLoopProfile(
|
|
1464
1521
|
value.workerDefaults,
|
|
1465
|
-
|
|
1466
|
-
defaultThinking,
|
|
1522
|
+
runtimePriority,
|
|
1467
1523
|
),
|
|
1468
1524
|
verifier: compileDynamicDecisionLoopProfile(
|
|
1469
1525
|
value.verifier,
|
|
1470
|
-
|
|
1471
|
-
defaultThinking,
|
|
1526
|
+
runtimePriority,
|
|
1472
1527
|
),
|
|
1473
1528
|
synthesis: compileDynamicDecisionLoopProfile(
|
|
1474
1529
|
value.synthesis,
|
|
1475
|
-
|
|
1476
|
-
defaultThinking,
|
|
1530
|
+
runtimePriority,
|
|
1477
1531
|
),
|
|
1478
1532
|
allowedAgents: stringArray(value.allowedAgents),
|
|
1479
1533
|
...(allowedToolSelection.tools
|
|
@@ -1528,8 +1582,12 @@ function compileDynamicDecisionLoop(
|
|
|
1528
1582
|
|
|
1529
1583
|
function compileDynamicDecisionLoopProfile(
|
|
1530
1584
|
value: unknown,
|
|
1531
|
-
|
|
1532
|
-
|
|
1585
|
+
runtimePriority: {
|
|
1586
|
+
runtimeOverrides?: WorkflowRuntimeDefaults;
|
|
1587
|
+
runtimeDefaults?: WorkflowRuntimeDefaults;
|
|
1588
|
+
specRuntimeDefaults?: WorkflowRuntimeDefaults;
|
|
1589
|
+
stageRuntime?: WorkflowRuntimeDefaults;
|
|
1590
|
+
},
|
|
1533
1591
|
): any | undefined {
|
|
1534
1592
|
if (!isPlainRecord(value)) return undefined;
|
|
1535
1593
|
const toolSelection = filterToolSelection(
|
|
@@ -1538,20 +1596,18 @@ function compileDynamicDecisionLoopProfile(
|
|
|
1538
1596
|
undefined,
|
|
1539
1597
|
),
|
|
1540
1598
|
);
|
|
1541
|
-
const
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
: defaultThinking;
|
|
1599
|
+
const runtime = selectWorkflowRuntime(
|
|
1600
|
+
runtimePriority.runtimeOverrides,
|
|
1601
|
+
runtimeSettings(value),
|
|
1602
|
+
runtimePriority.stageRuntime,
|
|
1603
|
+
runtimePriority.runtimeDefaults,
|
|
1604
|
+
runtimePriority.specRuntimeDefaults,
|
|
1605
|
+
);
|
|
1549
1606
|
return {
|
|
1550
1607
|
...(typeof value.agent === "string" && value.agent.trim()
|
|
1551
1608
|
? { agent: value.agent.trim() }
|
|
1552
1609
|
: {}),
|
|
1553
|
-
...
|
|
1554
|
-
...(thinking ? { thinking } : {}),
|
|
1610
|
+
...runtime,
|
|
1555
1611
|
...(toolSelection.tools ? { tools: toolSelection.tools } : {}),
|
|
1556
1612
|
...(toolSelection.toolProviders
|
|
1557
1613
|
? { toolProviders: toolSelection.toolProviders }
|
|
@@ -24,6 +24,11 @@ import type {
|
|
|
24
24
|
WorkflowRunRecord,
|
|
25
25
|
WorkflowTaskRunRecord,
|
|
26
26
|
} from "./types.js";
|
|
27
|
+
import {
|
|
28
|
+
resolveWorkflowRuntime,
|
|
29
|
+
selectWorkflowRuntime,
|
|
30
|
+
type WorkflowModelInfo,
|
|
31
|
+
} from "./workflow-runtime.js";
|
|
27
32
|
|
|
28
33
|
const DYNAMIC_OUTPUT_MAX_DIGEST_CHARS = 1000;
|
|
29
34
|
const DYNAMIC_DELEGATION_TOOLS = new Set([
|
|
@@ -92,6 +97,7 @@ export async function buildDynamicGeneratedCompiledTask(input: {
|
|
|
92
97
|
branchId?: string;
|
|
93
98
|
request: DynamicAgentRequest;
|
|
94
99
|
dynamic: CompiledDynamicWorkflowTask;
|
|
100
|
+
availableModels?: WorkflowModelInfo[];
|
|
95
101
|
}): Promise<CompiledTask> {
|
|
96
102
|
if (input.dynamic.budget.maxAgents <= 0) {
|
|
97
103
|
throw new Error("dynamic agent budget is exhausted");
|
|
@@ -172,6 +178,23 @@ export async function buildDynamicGeneratedCompiledTask(input: {
|
|
|
172
178
|
),
|
|
173
179
|
),
|
|
174
180
|
);
|
|
181
|
+
const selectedRuntime = selectWorkflowRuntime(
|
|
182
|
+
input.dynamic.runtimeOverrides,
|
|
183
|
+
runtimeSettings(input.request),
|
|
184
|
+
runtimeSettings(executionProfile),
|
|
185
|
+
runtimeSettings(input.controllerCompiledTask.runtime),
|
|
186
|
+
runtimeSettings(agentDefinition),
|
|
187
|
+
);
|
|
188
|
+
const resolvedRuntime = await resolveWorkflowRuntime(
|
|
189
|
+
selectedRuntime,
|
|
190
|
+
{
|
|
191
|
+
taskKey: input.generatedSpecId,
|
|
192
|
+
stageId: input.controllerStageId,
|
|
193
|
+
taskId: input.request.id,
|
|
194
|
+
agent: requestedAgent,
|
|
195
|
+
},
|
|
196
|
+
{ availableModels: input.availableModels ?? input.dynamic.availableModels },
|
|
197
|
+
);
|
|
175
198
|
const unknownTools = (tools ?? []).filter(
|
|
176
199
|
(tool) => effectiveToolClassification(tool, toolProviders) === undefined,
|
|
177
200
|
);
|
|
@@ -253,16 +276,7 @@ export async function buildDynamicGeneratedCompiledTask(input: {
|
|
|
253
276
|
explicitWorktreePolicy: requiresWorktree,
|
|
254
277
|
runtime: {
|
|
255
278
|
approvalMode: "non-interactive",
|
|
256
|
-
|
|
257
|
-
input.request.model ??
|
|
258
|
-
executionProfile?.model ??
|
|
259
|
-
input.controllerCompiledTask.runtime.model ??
|
|
260
|
-
agentDefinition.model,
|
|
261
|
-
thinking:
|
|
262
|
-
input.request.thinking ??
|
|
263
|
-
executionProfile?.thinking ??
|
|
264
|
-
input.controllerCompiledTask.runtime.thinking ??
|
|
265
|
-
agentDefinition.thinking,
|
|
279
|
+
...resolvedRuntime,
|
|
266
280
|
tools,
|
|
267
281
|
...(toolProviders ? { toolProviders } : {}),
|
|
268
282
|
maxRuntimeMs:
|
|
@@ -419,7 +433,9 @@ function dynamicDecisionLoopProfile(
|
|
|
419
433
|
);
|
|
420
434
|
}
|
|
421
435
|
|
|
422
|
-
export function isDynamicCompiledTaskPayload(
|
|
436
|
+
export function isDynamicCompiledTaskPayload(
|
|
437
|
+
value: unknown,
|
|
438
|
+
): value is CompiledTask {
|
|
423
439
|
return (
|
|
424
440
|
!!value &&
|
|
425
441
|
typeof value === "object" &&
|
|
@@ -679,7 +695,9 @@ export async function readDynamicGeneratedTaskResult(
|
|
|
679
695
|
};
|
|
680
696
|
}
|
|
681
697
|
|
|
682
|
-
export function normalizeDynamicAgentRequest(
|
|
698
|
+
export function normalizeDynamicAgentRequest(
|
|
699
|
+
value: unknown,
|
|
700
|
+
): DynamicAgentRequest {
|
|
683
701
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
684
702
|
throw new Error("ctx.agent() request must be an object");
|
|
685
703
|
}
|
|
@@ -730,6 +748,23 @@ function requiredDynamicString(
|
|
|
730
748
|
return value.trim();
|
|
731
749
|
}
|
|
732
750
|
|
|
751
|
+
function runtimeSettings(
|
|
752
|
+
value: unknown,
|
|
753
|
+
): { model?: string; thinking?: ThinkingLevel } | undefined {
|
|
754
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
755
|
+
return undefined;
|
|
756
|
+
const record = value as Record<string, unknown>;
|
|
757
|
+
const model =
|
|
758
|
+
typeof record.model === "string" && record.model.trim()
|
|
759
|
+
? record.model.trim()
|
|
760
|
+
: undefined;
|
|
761
|
+
const thinking =
|
|
762
|
+
typeof record.thinking === "string" && record.thinking.trim()
|
|
763
|
+
? (record.thinking.trim() as ThinkingLevel)
|
|
764
|
+
: undefined;
|
|
765
|
+
return model || thinking ? { model, thinking } : undefined;
|
|
766
|
+
}
|
|
767
|
+
|
|
733
768
|
function optionalDynamicString(
|
|
734
769
|
value: unknown,
|
|
735
770
|
field: string,
|
package/src/engine.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import { dirname, extname, join,
|
|
1
|
+
import { appendFile, mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, extname, join, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
4
|
import { Worker } from "node:worker_threads";
|
|
5
5
|
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
toProjectPath,
|
|
24
24
|
updateIndex,
|
|
25
25
|
withRunLease,
|
|
26
|
-
|
|
26
|
+
workflowRunPath,
|
|
27
27
|
writeJsonAtomic,
|
|
28
28
|
writeRunRecord,
|
|
29
29
|
writeCompiledRunArtifact,
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
import {
|
|
41
41
|
readSimpleJsonPath,
|
|
42
42
|
type WorkflowModelInfo,
|
|
43
|
+
type WorkflowRuntimeDefaults,
|
|
43
44
|
} from "./workflow-runtime.js";
|
|
44
45
|
import {
|
|
45
46
|
dynamicRunDir,
|
|
@@ -74,7 +75,6 @@ import {
|
|
|
74
75
|
isDynamicCompiledTaskPayload,
|
|
75
76
|
normalizeDynamicAgentRequest,
|
|
76
77
|
readDynamicGeneratedTaskResult,
|
|
77
|
-
type DynamicAgentRequest,
|
|
78
78
|
} from "./dynamic-generated-task-runtime.js";
|
|
79
79
|
import {
|
|
80
80
|
optionalEventString,
|
|
@@ -116,10 +116,6 @@ import {
|
|
|
116
116
|
readSupportSources,
|
|
117
117
|
writeArtifactGraphDynamicResult,
|
|
118
118
|
} from "./artifact-graph-runtime.js";
|
|
119
|
-
import {
|
|
120
|
-
isDynamicOutputProfile,
|
|
121
|
-
type DynamicOutputProfile,
|
|
122
|
-
} from "./dynamic-profiles.js";
|
|
123
119
|
import {
|
|
124
120
|
DIRECT_DYNAMIC_RUNTIME_VERSION,
|
|
125
121
|
ensureDirectDynamicRuntimeBundle,
|
|
@@ -128,7 +124,6 @@ import {
|
|
|
128
124
|
type CompiledDynamicWorkflowTask,
|
|
129
125
|
type CompiledTask,
|
|
130
126
|
type CompiledWorkflow,
|
|
131
|
-
type ThinkingLevel,
|
|
132
127
|
WORKFLOW_RUN_TYPE,
|
|
133
128
|
type WorkflowIndexRecord,
|
|
134
129
|
type WorkflowRunRecord,
|
|
@@ -151,10 +146,12 @@ const DYNAMIC_CONTROLLER_ENGINE_CAPABILITIES = Object.freeze({
|
|
|
151
146
|
const DYNAMIC_CONTROLLER_ENGINE_INTEGRITY_ERROR_MESSAGE =
|
|
152
147
|
"incompatible or stale pi-workflow engine: dynamic controller context is missing runDecisionLoop (rebuild dist / reload workflow engine)";
|
|
153
148
|
const supervisorTimers = new Map<string, ReturnType<typeof setInterval>>();
|
|
149
|
+
const supervisorRunMtimes = new Map<string, number>();
|
|
154
150
|
|
|
155
151
|
export interface WorkflowRunOptions {
|
|
156
152
|
task?: string;
|
|
157
|
-
|
|
153
|
+
runtimeOverrides?: WorkflowRuntimeDefaults;
|
|
154
|
+
runtimeDefaults?: WorkflowRuntimeDefaults;
|
|
158
155
|
availableModels?: WorkflowModelInfo[];
|
|
159
156
|
dynamicUi?: DynamicWorkflowUi;
|
|
160
157
|
runId?: string;
|
|
@@ -163,6 +160,7 @@ export interface WorkflowRunOptions {
|
|
|
163
160
|
|
|
164
161
|
interface WorkflowScheduleOptions {
|
|
165
162
|
dynamicUi?: DynamicWorkflowUi;
|
|
163
|
+
availableModels?: WorkflowModelInfo[];
|
|
166
164
|
}
|
|
167
165
|
|
|
168
166
|
export async function runWorkflowSpec(
|
|
@@ -207,6 +205,7 @@ async function runLoadedWorkflowSpec(
|
|
|
207
205
|
cwd,
|
|
208
206
|
specPath,
|
|
209
207
|
task: options.task,
|
|
208
|
+
runtimeOverrides: options.runtimeOverrides,
|
|
210
209
|
runtimeDefaults: options.runtimeDefaults,
|
|
211
210
|
availableModels: options.availableModels,
|
|
212
211
|
});
|
|
@@ -222,12 +221,15 @@ async function runLoadedWorkflowSpec(
|
|
|
222
221
|
await writeRunRecord(cwd, run);
|
|
223
222
|
});
|
|
224
223
|
|
|
224
|
+
const scheduleOptions = {
|
|
225
|
+
dynamicUi: options.dynamicUi,
|
|
226
|
+
availableModels: options.availableModels,
|
|
227
|
+
};
|
|
225
228
|
const scheduled =
|
|
226
|
-
(await scheduleRun(cwd, run.runId, compiled,
|
|
227
|
-
|
|
228
|
-
})) ?? (await readRunRecord(cwd, run.runId));
|
|
229
|
+
(await scheduleRun(cwd, run.runId, compiled, scheduleOptions)) ??
|
|
230
|
+
(await readRunRecord(cwd, run.runId));
|
|
229
231
|
if (scheduled.status === "running")
|
|
230
|
-
watchRun(cwd, scheduled.runId,
|
|
232
|
+
watchRun(cwd, scheduled.runId, scheduleOptions);
|
|
231
233
|
return scheduled;
|
|
232
234
|
}
|
|
233
235
|
|
|
@@ -366,15 +368,27 @@ export function watchRun(
|
|
|
366
368
|
|
|
367
369
|
const timer = setInterval(() => {
|
|
368
370
|
void (async () => {
|
|
371
|
+
const previousMtime = supervisorRunMtimes.get(key);
|
|
372
|
+
const beforeMtime = await readRunMtimeMs(cwd, runId);
|
|
369
373
|
const refreshed = await refreshRun(cwd, runId);
|
|
374
|
+
const afterMtime = await readRunMtimeMs(cwd, runId);
|
|
375
|
+
const currentMtime = afterMtime ?? beforeMtime;
|
|
376
|
+
if (currentMtime !== undefined)
|
|
377
|
+
supervisorRunMtimes.set(key, currentMtime);
|
|
378
|
+
|
|
370
379
|
if (refreshed.status === "running") {
|
|
371
|
-
|
|
380
|
+
const unchanged =
|
|
381
|
+
previousMtime !== undefined &&
|
|
382
|
+
currentMtime !== undefined &&
|
|
383
|
+
currentMtime <= previousMtime;
|
|
384
|
+
if (!unchanged) await scheduleRun(cwd, runId, undefined, options);
|
|
372
385
|
return;
|
|
373
386
|
}
|
|
374
387
|
|
|
375
388
|
const existing = supervisorTimers.get(key);
|
|
376
389
|
if (existing) clearInterval(existing);
|
|
377
390
|
supervisorTimers.delete(key);
|
|
391
|
+
supervisorRunMtimes.delete(key);
|
|
378
392
|
})().catch((error) => {
|
|
379
393
|
void recordSupervisorError(cwd, runId, error);
|
|
380
394
|
});
|
|
@@ -384,6 +398,18 @@ export function watchRun(
|
|
|
384
398
|
supervisorTimers.set(key, timer);
|
|
385
399
|
}
|
|
386
400
|
|
|
401
|
+
async function readRunMtimeMs(
|
|
402
|
+
cwd: string,
|
|
403
|
+
runId: string,
|
|
404
|
+
): Promise<number | undefined> {
|
|
405
|
+
try {
|
|
406
|
+
return (await stat(workflowRunPath(cwd, runId))).mtimeMs;
|
|
407
|
+
} catch (error) {
|
|
408
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
|
|
409
|
+
throw error;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
387
413
|
export async function scheduleRun(
|
|
388
414
|
cwd: string,
|
|
389
415
|
runId: string,
|
|
@@ -1047,6 +1073,7 @@ async function executeDynamicControllerTask(
|
|
|
1047
1073
|
sources,
|
|
1048
1074
|
dynamic: compiledTask.dynamic,
|
|
1049
1075
|
dynamicUi: options.dynamicUi,
|
|
1076
|
+
availableModels: options.availableModels,
|
|
1050
1077
|
});
|
|
1051
1078
|
await assertDynamicGeneratedTasksSettled({
|
|
1052
1079
|
cwd,
|
|
@@ -1056,6 +1083,7 @@ async function executeDynamicControllerTask(
|
|
|
1056
1083
|
controllerTask: task,
|
|
1057
1084
|
controllerCompiledTask: compiledTask,
|
|
1058
1085
|
dynamic: compiledTask.dynamic,
|
|
1086
|
+
availableModels: options.availableModels,
|
|
1059
1087
|
});
|
|
1060
1088
|
await recordActiveRuntime();
|
|
1061
1089
|
const unrunBranchBlockers = await dynamicUnrunBranchBlockers(
|
|
@@ -1188,6 +1216,7 @@ async function runDynamicControllerWorker(input: {
|
|
|
1188
1216
|
sources: Record<string, unknown>;
|
|
1189
1217
|
dynamic: CompiledDynamicWorkflowTask;
|
|
1190
1218
|
dynamicUi?: DynamicWorkflowUi;
|
|
1219
|
+
availableModels?: WorkflowModelInfo[];
|
|
1191
1220
|
}): Promise<unknown> {
|
|
1192
1221
|
const resolved = await resolveWorkflowHelperRef(
|
|
1193
1222
|
input.dynamic.uses,
|
|
@@ -1810,65 +1839,6 @@ function requiredDynamicString(
|
|
|
1810
1839
|
return value.trim();
|
|
1811
1840
|
}
|
|
1812
1841
|
|
|
1813
|
-
function optionalDynamicString(
|
|
1814
|
-
value: unknown,
|
|
1815
|
-
field: string,
|
|
1816
|
-
): string | undefined {
|
|
1817
|
-
if (value === undefined) return undefined;
|
|
1818
|
-
return requiredDynamicString(value, field);
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
function optionalDynamicStringArray(
|
|
1822
|
-
value: unknown,
|
|
1823
|
-
field: string,
|
|
1824
|
-
): string[] | undefined {
|
|
1825
|
-
if (value === undefined) return undefined;
|
|
1826
|
-
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
1827
|
-
throw new Error(`ctx.agent() ${field} must be an array of strings`);
|
|
1828
|
-
}
|
|
1829
|
-
return value.map((item) => item.trim()).filter(Boolean);
|
|
1830
|
-
}
|
|
1831
|
-
|
|
1832
|
-
function isPlainDynamicRecord(
|
|
1833
|
-
value: unknown,
|
|
1834
|
-
): value is Record<string, unknown> {
|
|
1835
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1836
|
-
}
|
|
1837
|
-
|
|
1838
|
-
function optionalDynamicPositiveInteger(
|
|
1839
|
-
value: unknown,
|
|
1840
|
-
field: string,
|
|
1841
|
-
): number | undefined {
|
|
1842
|
-
if (value === undefined) return undefined;
|
|
1843
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
1844
|
-
throw new Error(`ctx.agent() ${field} must be a positive integer`);
|
|
1845
|
-
}
|
|
1846
|
-
return value;
|
|
1847
|
-
}
|
|
1848
|
-
|
|
1849
|
-
function requiredDynamicOutputProfile(
|
|
1850
|
-
value: unknown,
|
|
1851
|
-
field: string,
|
|
1852
|
-
api: string,
|
|
1853
|
-
): DynamicOutputProfile {
|
|
1854
|
-
const profile = requiredDynamicString(value, field, api);
|
|
1855
|
-
if (!isDynamicOutputProfile(profile)) {
|
|
1856
|
-
throw new Error(`${api} ${field} has an unsupported output profile`);
|
|
1857
|
-
}
|
|
1858
|
-
return profile;
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1861
|
-
function requiredDynamicNonNegativeInteger(
|
|
1862
|
-
value: unknown,
|
|
1863
|
-
field: string,
|
|
1864
|
-
api: string,
|
|
1865
|
-
): number {
|
|
1866
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
1867
|
-
throw new Error(`${api} ${field} must be a non-negative integer`);
|
|
1868
|
-
}
|
|
1869
|
-
return value;
|
|
1870
|
-
}
|
|
1871
|
-
|
|
1872
1842
|
function requiredDynamicPositiveInteger(
|
|
1873
1843
|
value: unknown,
|
|
1874
1844
|
field: string,
|
|
@@ -1880,17 +1850,6 @@ function requiredDynamicPositiveInteger(
|
|
|
1880
1850
|
return value;
|
|
1881
1851
|
}
|
|
1882
1852
|
|
|
1883
|
-
function optionalDynamicStringField(value: unknown): string | undefined {
|
|
1884
|
-
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
1885
|
-
}
|
|
1886
|
-
|
|
1887
|
-
function optionalDynamicOutputProfile(
|
|
1888
|
-
value: unknown,
|
|
1889
|
-
): DynamicOutputProfile | undefined {
|
|
1890
|
-
if (value === undefined) return undefined;
|
|
1891
|
-
return requiredDynamicOutputProfile(value, "outputProfile", "ctx.agent()");
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
1853
|
async function currentDynamicBudgetRemaining(input: {
|
|
1895
1854
|
cwd: string;
|
|
1896
1855
|
run: WorkflowRunRecord;
|
|
@@ -2263,6 +2222,7 @@ async function assertDynamicGeneratedTasksSettled(input: {
|
|
|
2263
2222
|
controllerTask: WorkflowTaskRunRecord;
|
|
2264
2223
|
controllerCompiledTask: CompiledTask;
|
|
2265
2224
|
dynamic: CompiledDynamicWorkflowTask;
|
|
2225
|
+
availableModels?: WorkflowModelInfo[];
|
|
2266
2226
|
}): Promise<void> {
|
|
2267
2227
|
const state = await readOrRebuildDynamicState(input.cwd, input.run.runId);
|
|
2268
2228
|
const generatedTaskIds =
|
|
@@ -2289,6 +2249,7 @@ async function repairMissingDynamicGeneratedTask(
|
|
|
2289
2249
|
controllerTask: WorkflowTaskRunRecord;
|
|
2290
2250
|
controllerCompiledTask: CompiledTask;
|
|
2291
2251
|
dynamic: CompiledDynamicWorkflowTask;
|
|
2252
|
+
availableModels?: WorkflowModelInfo[];
|
|
2292
2253
|
},
|
|
2293
2254
|
specId: string,
|
|
2294
2255
|
): Promise<WorkflowTaskRunRecord> {
|
|
@@ -2325,6 +2286,7 @@ async function repairMissingDynamicGeneratedTask(
|
|
|
2325
2286
|
branchId: optionalEventString(event.payload.branchId),
|
|
2326
2287
|
request,
|
|
2327
2288
|
dynamic: input.dynamic,
|
|
2289
|
+
availableModels: input.availableModels,
|
|
2328
2290
|
});
|
|
2329
2291
|
assertDynamicGeneratedMetadataMatches(compiledTask, {
|
|
2330
2292
|
controllerSpecId: input.controllerTask.specId,
|
|
@@ -2374,6 +2336,7 @@ async function runDynamicAgentRequest(input: {
|
|
|
2374
2336
|
request: unknown;
|
|
2375
2337
|
generatedTaskIds: string[];
|
|
2376
2338
|
isSettled?: () => boolean;
|
|
2339
|
+
availableModels?: WorkflowModelInfo[];
|
|
2377
2340
|
}): Promise<unknown> {
|
|
2378
2341
|
await assertDynamicRuntimeBudgetAvailable({
|
|
2379
2342
|
cwd: input.cwd,
|
|
@@ -2459,6 +2422,7 @@ async function runDynamicAgentRequest(input: {
|
|
|
2459
2422
|
branchId: generationBranchId,
|
|
2460
2423
|
request: generationRequest,
|
|
2461
2424
|
dynamic: input.dynamic,
|
|
2425
|
+
availableModels: input.availableModels,
|
|
2462
2426
|
});
|
|
2463
2427
|
assertDynamicGeneratedMetadataMatches(compiledTask, {
|
|
2464
2428
|
controllerSpecId: input.controllerTask.specId,
|