@agwab/pi-workflow 0.1.2 → 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.
Files changed (41) hide show
  1. package/README.md +9 -13
  2. package/dist/compiler.d.ts +5 -5
  3. package/dist/compiler.js +82 -24
  4. package/dist/dynamic-generated-task-runtime.d.ts +2 -0
  5. package/dist/dynamic-generated-task-runtime.js +21 -8
  6. package/dist/engine.d.ts +6 -5
  7. package/dist/engine.js +39 -54
  8. package/dist/extension.js +211 -24
  9. package/dist/store.d.ts +3 -1
  10. package/dist/store.js +135 -38
  11. package/dist/subagent-backend.d.ts +4 -0
  12. package/dist/subagent-backend.js +128 -4
  13. package/dist/types.d.ts +5 -0
  14. package/dist/workflow-progress-health.d.ts +37 -0
  15. package/dist/workflow-progress-health.js +296 -0
  16. package/dist/workflow-runtime.d.ts +8 -0
  17. package/dist/workflow-runtime.js +63 -10
  18. package/dist/workflow-view.d.ts +2 -0
  19. package/dist/workflow-view.js +97 -18
  20. package/dist/workflow-web-source.js +32 -14
  21. package/docs/usage.md +12 -1
  22. package/package.json +6 -6
  23. package/src/compiler.ts +136 -41
  24. package/src/dynamic-generated-task-runtime.ts +47 -12
  25. package/src/engine.ts +55 -100
  26. package/src/extension.ts +270 -34
  27. package/src/store.ts +180 -44
  28. package/src/subagent-backend.ts +170 -6
  29. package/src/types.ts +10 -0
  30. package/src/workflow-progress-health.ts +461 -0
  31. package/src/workflow-runtime.ts +85 -13
  32. package/src/workflow-view.ts +186 -41
  33. package/src/workflow-web-source.ts +192 -69
  34. package/workflows/deep-research/helpers/claim-evidence-gate.mjs +111 -37
  35. package/workflows/deep-research/helpers/final-audit-packet.mjs +191 -14
  36. package/workflows/deep-research/helpers/normalize-input-packet.mjs +159 -50
  37. package/workflows/deep-research/helpers/render-executive.mjs +671 -37
  38. package/workflows/deep-research/helpers/sanitize-verification-candidates.mjs +624 -0
  39. package/workflows/deep-research/schemas/deep-research-executive-render-control.schema.json +2 -0
  40. package/workflows/deep-research/schemas/deep-research-final-synthesis-control.schema.json +110 -0
  41. package/workflows/deep-research/spec.json +41 -11
package/src/compiler.ts CHANGED
@@ -31,6 +31,13 @@ import {
31
31
  type WorkflowToolSpec,
32
32
  type WorktreePolicy,
33
33
  } from "./types.js";
34
+ import {
35
+ resolveWorkflowRuntime,
36
+ selectWorkflowRuntime,
37
+ type WorkflowModelInfo,
38
+ type WorkflowRuntimeDefaults,
39
+ type WorkflowRuntimeResolutionInput,
40
+ } from "./workflow-runtime.js";
34
41
 
35
42
  const DELEGATION_TOOLS = new Set([
36
43
  "skill_test_subagent",
@@ -57,6 +64,7 @@ const DEFAULT_DYNAMIC_DECISION_LOOP_MAX_STALLS = 3;
57
64
  interface CompileOptions {
58
65
  cwd: string;
59
66
  specPath?: string;
67
+ availableModels?: WorkflowModelInfo[];
60
68
  }
61
69
 
62
70
  interface ArtifactGraphCompilePlanBuildResult {
@@ -544,7 +552,8 @@ export async function compileWorkflow(
544
552
  spec: ArtifactGraphWorkflowSpec,
545
553
  options: CompileOptions & {
546
554
  task?: string;
547
- runtimeDefaults?: { model?: string; thinking?: ThinkingLevel };
555
+ runtimeOverrides?: WorkflowRuntimeDefaults;
556
+ runtimeDefaults?: WorkflowRuntimeDefaults;
548
557
  },
549
558
  ): Promise<any> {
550
559
  const compilePlan = buildArtifactGraphCompilePlan(spec, options);
@@ -610,11 +619,25 @@ async function collectForeachPathWarnings(
610
619
  return warnings;
611
620
  }
612
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
+
613
635
  async function compileArtifactGraphPlan(
614
636
  spec: any,
615
637
  options: CompileOptions & {
616
638
  task?: string;
617
- runtimeDefaults?: { model?: string; thinking?: ThinkingLevel };
639
+ runtimeOverrides?: WorkflowRuntimeDefaults;
640
+ runtimeDefaults?: WorkflowRuntimeDefaults;
618
641
  },
619
642
  ): Promise<any> {
620
643
  const stages = spec.stages;
@@ -660,9 +683,9 @@ async function compileArtifactGraphPlan(
660
683
  Object.keys(workflowInput).length > 0
661
684
  ? `# Workflow Input\n\n${JSON.stringify(workflowInput, null, 2)}`
662
685
  : "";
663
- const defaultModel = options.runtimeDefaults?.model ?? spec.defaults?.model;
664
- const defaultThinking =
665
- options.runtimeDefaults?.thinking ?? spec.defaults?.thinking;
686
+ const runtimeOverrides = options.runtimeOverrides;
687
+ const runtimeDefaults = options.runtimeDefaults;
688
+ const specRuntimeDefaults = runtimeSettings(spec.defaults);
666
689
  const tasks: any[] = [];
667
690
  const stageRecords: any[] = [];
668
691
  const issues: ValidationIssue[] = [];
@@ -714,6 +737,22 @@ async function compileArtifactGraphPlan(
714
737
  dynamicToolPath,
715
738
  );
716
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
+ );
717
756
  const dynamicTask = buildDynamicTask(
718
757
  stage,
719
758
  taskId,
@@ -724,10 +763,22 @@ async function compileArtifactGraphPlan(
724
763
  specDir,
725
764
  workflowInputText,
726
765
  options.task,
727
- defaultModel,
728
- defaultThinking,
766
+ resolvedDynamicRuntime,
767
+ {
768
+ runtimeOverrides,
769
+ runtimeDefaults,
770
+ specRuntimeDefaults,
771
+ stageRuntime: runtimeSettings(stage),
772
+ },
729
773
  overrides,
730
774
  );
775
+ dynamicTask.runtime = {
776
+ ...dynamicTask.runtime,
777
+ ...resolvedDynamicRuntime,
778
+ };
779
+ if (options.availableModels?.length) {
780
+ dynamicTask.dynamic.availableModels = options.availableModels;
781
+ }
731
782
  if (dynamicToolSelection.tools || dynamicToolSelection.toolProviders) {
732
783
  dynamicTask.runtime = {
733
784
  ...dynamicTask.runtime,
@@ -804,11 +855,28 @@ async function compileArtifactGraphPlan(
804
855
  validateToolSubset(toolSelection.tools, stageAgent, issues, toolPath);
805
856
  validateDelegationBoundary(toolSelection.tools, issues, toolPath);
806
857
  const filteredToolSelection = filterToolSelection(toolSelection);
858
+ const requestedRuntime = selectWorkflowRuntime(
859
+ runtimeOverrides,
860
+ runtimeSettings(stage),
861
+ runtimeDefaults,
862
+ specRuntimeDefaults,
863
+ );
864
+ const resolvedRuntime = await resolveWorkflowRuntime(
865
+ requestedRuntime,
866
+ {
867
+ taskKey: key,
868
+ stageId: stage.id,
869
+ taskId,
870
+ agent: stageAgentName,
871
+ },
872
+ {
873
+ availableModels: options.availableModels,
874
+ },
875
+ );
807
876
  const runtime = {
808
877
  approvalMode:
809
878
  stage.approvalMode ?? spec.defaults?.approvalMode ?? "non-interactive",
810
- model: stage.model ?? defaultModel,
811
- thinking: stage.thinking ?? defaultThinking,
879
+ ...resolvedRuntime,
812
880
  tools: filteredToolSelection.tools,
813
881
  ...(filteredToolSelection.toolProviders
814
882
  ? { toolProviders: filteredToolSelection.toolProviders }
@@ -1169,12 +1237,34 @@ async function compileArtifactGraphPlan(
1169
1237
  tasks,
1170
1238
  warnings,
1171
1239
  budget: {
1172
- models: defaultModel ? [{ model: defaultModel }] : [],
1240
+ models: budgetModelRows(tasks),
1173
1241
  unratedModels: [],
1174
1242
  },
1175
1243
  };
1176
1244
  }
1177
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
+
1178
1268
  function isSupportStage(stage: any): boolean {
1179
1269
  return stage?.support !== undefined && stage?.type === undefined;
1180
1270
  }
@@ -1262,8 +1352,13 @@ function buildDynamicTask(
1262
1352
  specDir: string,
1263
1353
  workflowInputText: string,
1264
1354
  runtimeTask: string | undefined,
1265
- defaultModel: string | undefined,
1266
- defaultThinking: ThinkingLevel | undefined,
1355
+ controllerRuntime: WorkflowRuntimeResolutionInput,
1356
+ runtimePriority: {
1357
+ runtimeOverrides?: WorkflowRuntimeDefaults;
1358
+ runtimeDefaults?: WorkflowRuntimeDefaults;
1359
+ specRuntimeDefaults?: WorkflowRuntimeDefaults;
1360
+ stageRuntime?: WorkflowRuntimeDefaults;
1361
+ },
1267
1362
  overrides: Partial<CompiledTask> & Record<string, unknown>,
1268
1363
  ): any {
1269
1364
  const dynamic = stage.dynamic ?? {};
@@ -1326,8 +1421,7 @@ function buildDynamicTask(
1326
1421
  }
1327
1422
  const decisionLoop = compileDynamicDecisionLoop(
1328
1423
  dynamic.decisionLoop,
1329
- defaultModel,
1330
- defaultThinking,
1424
+ runtimePriority,
1331
1425
  );
1332
1426
 
1333
1427
  return {
@@ -1347,8 +1441,7 @@ function buildDynamicTask(
1347
1441
  explicitWorktreePolicy: false,
1348
1442
  runtime: {
1349
1443
  approvalMode: "non-interactive",
1350
- model: defaultModel,
1351
- thinking: defaultThinking,
1444
+ ...controllerRuntime,
1352
1445
  maxRuntimeMs:
1353
1446
  dynamic.budget?.maxRuntimeMs ?? DEFAULT_DYNAMIC_MAX_RUNTIME_MS,
1354
1447
  },
@@ -1392,6 +1485,9 @@ function buildDynamicTask(
1392
1485
  helpers,
1393
1486
  workflows,
1394
1487
  ...(decisionLoop ? { decisionLoop } : {}),
1488
+ ...(runtimePriority.runtimeOverrides
1489
+ ? { runtimeOverrides: runtimePriority.runtimeOverrides }
1490
+ : {}),
1395
1491
  },
1396
1492
  ...overrides,
1397
1493
  };
@@ -1399,8 +1495,12 @@ function buildDynamicTask(
1399
1495
 
1400
1496
  function compileDynamicDecisionLoop(
1401
1497
  value: unknown,
1402
- defaultModel?: string,
1403
- defaultThinking?: ThinkingLevel,
1498
+ runtimePriority: {
1499
+ runtimeOverrides?: WorkflowRuntimeDefaults;
1500
+ runtimeDefaults?: WorkflowRuntimeDefaults;
1501
+ specRuntimeDefaults?: WorkflowRuntimeDefaults;
1502
+ stageRuntime?: WorkflowRuntimeDefaults;
1503
+ },
1404
1504
  ): any | undefined {
1405
1505
  if (!isPlainRecord(value)) return undefined;
1406
1506
  const allowedToolSelection = filterToolSelection(
@@ -1416,25 +1516,18 @@ function compileDynamicDecisionLoop(
1416
1516
  recordValue(value.stateIndex, "requiredFindingIds"),
1417
1517
  );
1418
1518
  return {
1419
- planner: compileDynamicDecisionLoopProfile(
1420
- value.planner,
1421
- defaultModel,
1422
- defaultThinking,
1423
- ),
1519
+ planner: compileDynamicDecisionLoopProfile(value.planner, runtimePriority),
1424
1520
  workerDefaults: compileDynamicDecisionLoopProfile(
1425
1521
  value.workerDefaults,
1426
- defaultModel,
1427
- defaultThinking,
1522
+ runtimePriority,
1428
1523
  ),
1429
1524
  verifier: compileDynamicDecisionLoopProfile(
1430
1525
  value.verifier,
1431
- defaultModel,
1432
- defaultThinking,
1526
+ runtimePriority,
1433
1527
  ),
1434
1528
  synthesis: compileDynamicDecisionLoopProfile(
1435
1529
  value.synthesis,
1436
- defaultModel,
1437
- defaultThinking,
1530
+ runtimePriority,
1438
1531
  ),
1439
1532
  allowedAgents: stringArray(value.allowedAgents),
1440
1533
  ...(allowedToolSelection.tools
@@ -1489,8 +1582,12 @@ function compileDynamicDecisionLoop(
1489
1582
 
1490
1583
  function compileDynamicDecisionLoopProfile(
1491
1584
  value: unknown,
1492
- defaultModel?: string,
1493
- defaultThinking?: ThinkingLevel,
1585
+ runtimePriority: {
1586
+ runtimeOverrides?: WorkflowRuntimeDefaults;
1587
+ runtimeDefaults?: WorkflowRuntimeDefaults;
1588
+ specRuntimeDefaults?: WorkflowRuntimeDefaults;
1589
+ stageRuntime?: WorkflowRuntimeDefaults;
1590
+ },
1494
1591
  ): any | undefined {
1495
1592
  if (!isPlainRecord(value)) return undefined;
1496
1593
  const toolSelection = filterToolSelection(
@@ -1499,20 +1596,18 @@ function compileDynamicDecisionLoopProfile(
1499
1596
  undefined,
1500
1597
  ),
1501
1598
  );
1502
- const model =
1503
- typeof value.model === "string" && value.model.trim()
1504
- ? value.model.trim()
1505
- : defaultModel;
1506
- const thinking =
1507
- typeof value.thinking === "string" && value.thinking.trim()
1508
- ? value.thinking.trim()
1509
- : defaultThinking;
1599
+ const runtime = selectWorkflowRuntime(
1600
+ runtimePriority.runtimeOverrides,
1601
+ runtimeSettings(value),
1602
+ runtimePriority.stageRuntime,
1603
+ runtimePriority.runtimeDefaults,
1604
+ runtimePriority.specRuntimeDefaults,
1605
+ );
1510
1606
  return {
1511
1607
  ...(typeof value.agent === "string" && value.agent.trim()
1512
1608
  ? { agent: value.agent.trim() }
1513
1609
  : {}),
1514
- ...(model ? { model } : {}),
1515
- ...(thinking ? { thinking } : {}),
1610
+ ...runtime,
1516
1611
  ...(toolSelection.tools ? { tools: toolSelection.tools } : {}),
1517
1612
  ...(toolSelection.toolProviders
1518
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
- model:
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(value: unknown): value is CompiledTask {
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(value: unknown): DynamicAgentRequest {
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,