@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.
- package/README.md +9 -13
- package/dist/compiler.d.ts +5 -5
- package/dist/compiler.js +82 -24
- package/dist/dynamic-generated-task-runtime.d.ts +2 -0
- package/dist/dynamic-generated-task-runtime.js +21 -8
- package/dist/engine.d.ts +6 -5
- package/dist/engine.js +39 -54
- package/dist/extension.js +211 -24
- package/dist/store.d.ts +3 -1
- package/dist/store.js +135 -38
- package/dist/subagent-backend.d.ts +4 -0
- package/dist/subagent-backend.js +128 -4
- package/dist/types.d.ts +5 -0
- package/dist/workflow-progress-health.d.ts +37 -0
- package/dist/workflow-progress-health.js +296 -0
- package/dist/workflow-runtime.d.ts +8 -0
- package/dist/workflow-runtime.js +63 -10
- package/dist/workflow-view.d.ts +2 -0
- package/dist/workflow-view.js +97 -18
- package/dist/workflow-web-source.js +32 -14
- package/docs/usage.md +12 -1
- package/package.json +6 -6
- package/src/compiler.ts +136 -41
- package/src/dynamic-generated-task-runtime.ts +47 -12
- package/src/engine.ts +55 -100
- package/src/extension.ts +270 -34
- package/src/store.ts +180 -44
- package/src/subagent-backend.ts +170 -6
- package/src/types.ts +10 -0
- package/src/workflow-progress-health.ts +461 -0
- package/src/workflow-runtime.ts +85 -13
- package/src/workflow-view.ts +186 -41
- package/src/workflow-web-source.ts +192 -69
- package/workflows/deep-research/helpers/claim-evidence-gate.mjs +111 -37
- package/workflows/deep-research/helpers/final-audit-packet.mjs +191 -14
- package/workflows/deep-research/helpers/normalize-input-packet.mjs +159 -50
- package/workflows/deep-research/helpers/render-executive.mjs +671 -37
- package/workflows/deep-research/helpers/sanitize-verification-candidates.mjs +624 -0
- package/workflows/deep-research/schemas/deep-research-executive-render-control.schema.json +2 -0
- package/workflows/deep-research/schemas/deep-research-final-synthesis-control.schema.json +110 -0
- package/workflows/deep-research/spec.json +41 -11
package/README.md
CHANGED
|
@@ -64,6 +64,8 @@ If you want deterministic manual control, use the slash command form:
|
|
|
64
64
|
/workflow run deep-research "Research this repository and summarize the architecture tradeoffs."
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
For opt-in lower-latency runs, add `--thinking low`; defaults remain conservative pending holdout evidence. See [`docs/usage.md`](./docs/usage.md).
|
|
68
|
+
|
|
67
69
|
For a one-off adaptive workflow that should plan, fan out, and synthesize without choosing a saved workflow, use:
|
|
68
70
|
|
|
69
71
|
```text
|
|
@@ -167,22 +169,16 @@ Workflow definitions compose a small set of stage patterns and graph shapes.
|
|
|
167
169
|
|
|
168
170
|

|
|
169
171
|
|
|
170
|
-
Parallel execution is a graph shape, not a stage type: model parallel branches as multiple roots or with `after: []`. Support helpers are declared with a `support` object, not a stage `type`.
|
|
171
|
-
|
|
172
|
-
Dynamic workflows are the advanced form of adaptive orchestration. A JSON `type: "dynamic"` stage points at trusted bundle-local `.mjs` controller code; that controller can add official workflow tasks, call helpers, or run nested workflows while preserving replayable run state. See [`docs/usage.md`](./docs/usage.md) for approval, detach, helper retry, nested workflow, and cache details.
|
|
173
|
-
|
|
174
|
-
New workflows should prefer `workflow_web_search`, `workflow_web_fetch_source`, and `workflow_web_source_read`: search returns compact candidates, fetch returns a source card with an opaque `sourceRef`, and source-read retrieves narrow evidence snippets. Preserve `sourceRef` through workflow outputs; use `urls: [...]` or `sources: [...]` to batch several source fetches, use `queries: [...]` or `reads: [...]` to batch several snippets from one sourceRef, and use `claim` + distinctive `terms` to get candidate quote windows with match metadata when the exact quote is unknown. Normalized web sources are cached under `.pi/workflows/<run-id>/web-source-cache/` without exposing cache paths to agents; same-URL fetches and deterministic terminal failures are coordinated across parallel workers. Legacy `fetch_content` calls still use `.pi/workflows/<run-id>/source-cache/fetch-content/`; set `PI_WORKFLOW_FETCH_CONTENT_CACHE=0` to opt out of that legacy cache.
|
|
175
|
-
|
|
176
172
|
## Predefined workflows
|
|
177
173
|
|
|
178
|
-
The package includes
|
|
174
|
+
The package includes four bundled workflows for common research and review jobs. They are runnable defaults and authoring examples, not a complete workflow catalog.
|
|
179
175
|
|
|
180
|
-
| Workflow |
|
|
181
|
-
|
|
182
|
-
| `deep-research` |
|
|
183
|
-
| `deep-review` |
|
|
184
|
-
| `spec-review` |
|
|
185
|
-
| `impact-review` |
|
|
176
|
+
| Workflow | Best for | What it does |
|
|
177
|
+
|---|---|---|
|
|
178
|
+
| `deep-research` | Deep, source-grounded research when breadth, verification, and cited recommendations matter. | Plans research questions by depth, fans out question-level research, normalizes and ranks claims, verifies selected claims against evidence, and renders an audited executive handoff. |
|
|
179
|
+
| `deep-review` | Code or design review when one reviewer pass is not enough. | Triage selects review lenses, reviewers produce findings, a deterministic helper deduplicates them, a challenge pass tests the surviving findings, a deterministic helper partitions verdicts, and the final report keeps only evidence-backed issues. |
|
|
180
|
+
| `spec-review` | Requirements-to-implementation traceability for an existing spec, API contract, or acceptance criteria. | Extracts testable requirements, maps implementation and tests, verifies candidate gaps, and reports which requirements are covered, missing, ambiguous, or need human judgment. |
|
|
181
|
+
| `impact-review` | Side-effect and risk review for proposed or applied changes. | Maps change scope and affected surfaces, analyzes contract, state/data, validation, docs, security, and performance impact, then joins those lenses into likely regressions, missing checks, and next actions. |
|
|
186
182
|
|
|
187
183
|

|
|
188
184
|
|
package/dist/compiler.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { type ArtifactGraphWorkflowSpec
|
|
1
|
+
import { type ArtifactGraphWorkflowSpec } from "./types.js";
|
|
2
|
+
import { type WorkflowModelInfo, type WorkflowRuntimeDefaults } from "./workflow-runtime.js";
|
|
2
3
|
interface CompileOptions {
|
|
3
4
|
cwd: string;
|
|
4
5
|
specPath?: string;
|
|
6
|
+
availableModels?: WorkflowModelInfo[];
|
|
5
7
|
}
|
|
6
8
|
export declare function compileWorkflow(spec: ArtifactGraphWorkflowSpec, options: CompileOptions & {
|
|
7
9
|
task?: string;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
thinking?: ThinkingLevel;
|
|
11
|
-
};
|
|
10
|
+
runtimeOverrides?: WorkflowRuntimeDefaults;
|
|
11
|
+
runtimeDefaults?: WorkflowRuntimeDefaults;
|
|
12
12
|
}): Promise<any>;
|
|
13
13
|
export {};
|
package/dist/compiler.js
CHANGED
|
@@ -4,6 +4,7 @@ import { loadAgentByName } from "./agents.js";
|
|
|
4
4
|
import { DYNAMIC_OUTPUT_PROFILES } from "./dynamic-profiles.js";
|
|
5
5
|
import { classifyToolCapability, effectiveToolClassification, providersForSelectedTools, resolveToolSelection, TOOL_NAME_PATTERN, toolAllowedByAuthorityCeiling, toolNameForSpec, } from "./tool-metadata.js";
|
|
6
6
|
import { WorkflowValidationError, WORKFLOW_RUN_TYPE, } from "./types.js";
|
|
7
|
+
import { resolveWorkflowRuntime, selectWorkflowRuntime, } from "./workflow-runtime.js";
|
|
7
8
|
const DELEGATION_TOOLS = new Set([
|
|
8
9
|
"skill_test_subagent",
|
|
9
10
|
"workflow",
|
|
@@ -412,6 +413,17 @@ async function collectForeachPathWarnings(stages, specDir) {
|
|
|
412
413
|
}
|
|
413
414
|
return warnings;
|
|
414
415
|
}
|
|
416
|
+
function runtimeSettings(value) {
|
|
417
|
+
if (!isPlainRecord(value))
|
|
418
|
+
return undefined;
|
|
419
|
+
const model = typeof value.model === "string" && value.model.trim()
|
|
420
|
+
? value.model.trim()
|
|
421
|
+
: undefined;
|
|
422
|
+
const thinking = typeof value.thinking === "string" && value.thinking.trim()
|
|
423
|
+
? value.thinking.trim()
|
|
424
|
+
: undefined;
|
|
425
|
+
return model || thinking ? { model, thinking } : undefined;
|
|
426
|
+
}
|
|
415
427
|
async function compileArtifactGraphPlan(spec, options) {
|
|
416
428
|
const stages = spec.stages;
|
|
417
429
|
if (!Array.isArray(stages)) {
|
|
@@ -449,8 +461,9 @@ async function compileArtifactGraphPlan(spec, options) {
|
|
|
449
461
|
Object.keys(workflowInput).length > 0
|
|
450
462
|
? `# Workflow Input\n\n${JSON.stringify(workflowInput, null, 2)}`
|
|
451
463
|
: "";
|
|
452
|
-
const
|
|
453
|
-
const
|
|
464
|
+
const runtimeOverrides = options.runtimeOverrides;
|
|
465
|
+
const runtimeDefaults = options.runtimeDefaults;
|
|
466
|
+
const specRuntimeDefaults = runtimeSettings(spec.defaults);
|
|
454
467
|
const tasks = [];
|
|
455
468
|
const stageRecords = [];
|
|
456
469
|
const issues = [];
|
|
@@ -474,7 +487,26 @@ async function compileArtifactGraphPlan(spec, options) {
|
|
|
474
487
|
: `$.artifactGraph.stages.${jsonKey(stage.id)}.dynamic`;
|
|
475
488
|
validateDelegationBoundary(rawDynamicToolSelection.tools, issues, dynamicToolPath);
|
|
476
489
|
const dynamicToolSelection = filterToolSelection(rawDynamicToolSelection);
|
|
477
|
-
const
|
|
490
|
+
const requestedRuntime = selectWorkflowRuntime(runtimeOverrides, runtimeSettings(stage), runtimeDefaults, specRuntimeDefaults);
|
|
491
|
+
const resolvedDynamicRuntime = await resolveWorkflowRuntime(requestedRuntime, {
|
|
492
|
+
taskKey: key,
|
|
493
|
+
stageId: stage.id,
|
|
494
|
+
taskId,
|
|
495
|
+
agent: "dynamic",
|
|
496
|
+
}, { availableModels: options.availableModels });
|
|
497
|
+
const dynamicTask = buildDynamicTask(stage, taskId, key, prompt, dependencyKeys, options.cwd, specDir, workflowInputText, options.task, resolvedDynamicRuntime, {
|
|
498
|
+
runtimeOverrides,
|
|
499
|
+
runtimeDefaults,
|
|
500
|
+
specRuntimeDefaults,
|
|
501
|
+
stageRuntime: runtimeSettings(stage),
|
|
502
|
+
}, overrides);
|
|
503
|
+
dynamicTask.runtime = {
|
|
504
|
+
...dynamicTask.runtime,
|
|
505
|
+
...resolvedDynamicRuntime,
|
|
506
|
+
};
|
|
507
|
+
if (options.availableModels?.length) {
|
|
508
|
+
dynamicTask.dynamic.availableModels = options.availableModels;
|
|
509
|
+
}
|
|
478
510
|
if (dynamicToolSelection.tools || dynamicToolSelection.toolProviders) {
|
|
479
511
|
dynamicTask.runtime = {
|
|
480
512
|
...dynamicTask.runtime,
|
|
@@ -528,10 +560,18 @@ async function compileArtifactGraphPlan(spec, options) {
|
|
|
528
560
|
validateToolSubset(toolSelection.tools, stageAgent, issues, toolPath);
|
|
529
561
|
validateDelegationBoundary(toolSelection.tools, issues, toolPath);
|
|
530
562
|
const filteredToolSelection = filterToolSelection(toolSelection);
|
|
563
|
+
const requestedRuntime = selectWorkflowRuntime(runtimeOverrides, runtimeSettings(stage), runtimeDefaults, specRuntimeDefaults);
|
|
564
|
+
const resolvedRuntime = await resolveWorkflowRuntime(requestedRuntime, {
|
|
565
|
+
taskKey: key,
|
|
566
|
+
stageId: stage.id,
|
|
567
|
+
taskId,
|
|
568
|
+
agent: stageAgentName,
|
|
569
|
+
}, {
|
|
570
|
+
availableModels: options.availableModels,
|
|
571
|
+
});
|
|
531
572
|
const runtime = {
|
|
532
573
|
approvalMode: stage.approvalMode ?? spec.defaults?.approvalMode ?? "non-interactive",
|
|
533
|
-
|
|
534
|
-
thinking: stage.thinking ?? defaultThinking,
|
|
574
|
+
...resolvedRuntime,
|
|
535
575
|
tools: filteredToolSelection.tools,
|
|
536
576
|
...(filteredToolSelection.toolProviders
|
|
537
577
|
? { toolProviders: filteredToolSelection.toolProviders }
|
|
@@ -800,11 +840,33 @@ async function compileArtifactGraphPlan(spec, options) {
|
|
|
800
840
|
tasks,
|
|
801
841
|
warnings,
|
|
802
842
|
budget: {
|
|
803
|
-
models:
|
|
843
|
+
models: budgetModelRows(tasks),
|
|
804
844
|
unratedModels: [],
|
|
805
845
|
},
|
|
806
846
|
};
|
|
807
847
|
}
|
|
848
|
+
function budgetModelRows(tasks) {
|
|
849
|
+
const models = new Set();
|
|
850
|
+
for (const task of tasks) {
|
|
851
|
+
if (typeof task?.runtime?.model === "string" && task.runtime.model.trim()) {
|
|
852
|
+
models.add(task.runtime.model.trim());
|
|
853
|
+
}
|
|
854
|
+
const loop = task?.dynamic?.decisionLoop;
|
|
855
|
+
if (!loop || typeof loop !== "object")
|
|
856
|
+
continue;
|
|
857
|
+
for (const profile of [
|
|
858
|
+
loop.planner,
|
|
859
|
+
loop.workerDefaults,
|
|
860
|
+
loop.verifier,
|
|
861
|
+
loop.synthesis,
|
|
862
|
+
]) {
|
|
863
|
+
if (typeof profile?.model === "string" && profile.model.trim()) {
|
|
864
|
+
models.add(profile.model.trim());
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return [...models].sort().map((model) => ({ model }));
|
|
869
|
+
}
|
|
808
870
|
function isSupportStage(stage) {
|
|
809
871
|
return stage?.support !== undefined && stage?.type === undefined;
|
|
810
872
|
}
|
|
@@ -864,7 +926,7 @@ function buildSupportTask(stage, taskId, key, prompt, dependencyKeys, cwd, workf
|
|
|
864
926
|
...overrides,
|
|
865
927
|
};
|
|
866
928
|
}
|
|
867
|
-
function buildDynamicTask(stage, taskId, key, prompt, dependencyKeys, cwd, specDir, workflowInputText, runtimeTask,
|
|
929
|
+
function buildDynamicTask(stage, taskId, key, prompt, dependencyKeys, cwd, specDir, workflowInputText, runtimeTask, controllerRuntime, runtimePriority, overrides) {
|
|
868
930
|
const dynamic = stage.dynamic ?? {};
|
|
869
931
|
const uses = String(dynamic.uses);
|
|
870
932
|
const normalizedPrompt = String(prompt ?? "").replace(/\$\{item\}/g, "the relevant item from the dependency context");
|
|
@@ -917,7 +979,7 @@ function buildDynamicTask(stage, taskId, key, prompt, dependencyKeys, cwd, specD
|
|
|
917
979
|
usesPath: resolve(specDir, String(workflow.uses)),
|
|
918
980
|
};
|
|
919
981
|
}
|
|
920
|
-
const decisionLoop = compileDynamicDecisionLoop(dynamic.decisionLoop,
|
|
982
|
+
const decisionLoop = compileDynamicDecisionLoop(dynamic.decisionLoop, runtimePriority);
|
|
921
983
|
return {
|
|
922
984
|
key,
|
|
923
985
|
id: key,
|
|
@@ -935,8 +997,7 @@ function buildDynamicTask(stage, taskId, key, prompt, dependencyKeys, cwd, specD
|
|
|
935
997
|
explicitWorktreePolicy: false,
|
|
936
998
|
runtime: {
|
|
937
999
|
approvalMode: "non-interactive",
|
|
938
|
-
|
|
939
|
-
thinking: defaultThinking,
|
|
1000
|
+
...controllerRuntime,
|
|
940
1001
|
maxRuntimeMs: dynamic.budget?.maxRuntimeMs ?? DEFAULT_DYNAMIC_MAX_RUNTIME_MS,
|
|
941
1002
|
},
|
|
942
1003
|
safety: {
|
|
@@ -974,21 +1035,24 @@ function buildDynamicTask(stage, taskId, key, prompt, dependencyKeys, cwd, specD
|
|
|
974
1035
|
helpers,
|
|
975
1036
|
workflows,
|
|
976
1037
|
...(decisionLoop ? { decisionLoop } : {}),
|
|
1038
|
+
...(runtimePriority.runtimeOverrides
|
|
1039
|
+
? { runtimeOverrides: runtimePriority.runtimeOverrides }
|
|
1040
|
+
: {}),
|
|
977
1041
|
},
|
|
978
1042
|
...overrides,
|
|
979
1043
|
};
|
|
980
1044
|
}
|
|
981
|
-
function compileDynamicDecisionLoop(value,
|
|
1045
|
+
function compileDynamicDecisionLoop(value, runtimePriority) {
|
|
982
1046
|
if (!isPlainRecord(value))
|
|
983
1047
|
return undefined;
|
|
984
1048
|
const allowedToolSelection = filterToolSelection(resolveToolSelection([Array.isArray(value.allowedTools) ? value.allowedTools : undefined], undefined));
|
|
985
1049
|
const maxFindings = positiveInteger(recordValue(value.stateIndex, "maxFindings"));
|
|
986
1050
|
const deprecatedRequiredFindingIds = stringArray(recordValue(value.stateIndex, "requiredFindingIds"));
|
|
987
1051
|
return {
|
|
988
|
-
planner: compileDynamicDecisionLoopProfile(value.planner,
|
|
989
|
-
workerDefaults: compileDynamicDecisionLoopProfile(value.workerDefaults,
|
|
990
|
-
verifier: compileDynamicDecisionLoopProfile(value.verifier,
|
|
991
|
-
synthesis: compileDynamicDecisionLoopProfile(value.synthesis,
|
|
1052
|
+
planner: compileDynamicDecisionLoopProfile(value.planner, runtimePriority),
|
|
1053
|
+
workerDefaults: compileDynamicDecisionLoopProfile(value.workerDefaults, runtimePriority),
|
|
1054
|
+
verifier: compileDynamicDecisionLoopProfile(value.verifier, runtimePriority),
|
|
1055
|
+
synthesis: compileDynamicDecisionLoopProfile(value.synthesis, runtimePriority),
|
|
992
1056
|
allowedAgents: stringArray(value.allowedAgents),
|
|
993
1057
|
...(allowedToolSelection.tools
|
|
994
1058
|
? { allowedTools: allowedToolSelection.tools }
|
|
@@ -1029,22 +1093,16 @@ function compileDynamicDecisionLoop(value, defaultModel, defaultThinking) {
|
|
|
1029
1093
|
},
|
|
1030
1094
|
};
|
|
1031
1095
|
}
|
|
1032
|
-
function compileDynamicDecisionLoopProfile(value,
|
|
1096
|
+
function compileDynamicDecisionLoopProfile(value, runtimePriority) {
|
|
1033
1097
|
if (!isPlainRecord(value))
|
|
1034
1098
|
return undefined;
|
|
1035
1099
|
const toolSelection = filterToolSelection(resolveToolSelection([Array.isArray(value.tools) ? value.tools : undefined], undefined));
|
|
1036
|
-
const
|
|
1037
|
-
? value.model.trim()
|
|
1038
|
-
: defaultModel;
|
|
1039
|
-
const thinking = typeof value.thinking === "string" && value.thinking.trim()
|
|
1040
|
-
? value.thinking.trim()
|
|
1041
|
-
: defaultThinking;
|
|
1100
|
+
const runtime = selectWorkflowRuntime(runtimePriority.runtimeOverrides, runtimeSettings(value), runtimePriority.stageRuntime, runtimePriority.runtimeDefaults, runtimePriority.specRuntimeDefaults);
|
|
1042
1101
|
return {
|
|
1043
1102
|
...(typeof value.agent === "string" && value.agent.trim()
|
|
1044
1103
|
? { agent: value.agent.trim() }
|
|
1045
1104
|
: {}),
|
|
1046
|
-
...
|
|
1047
|
-
...(thinking ? { thinking } : {}),
|
|
1105
|
+
...runtime,
|
|
1048
1106
|
...(toolSelection.tools ? { tools: toolSelection.tools } : {}),
|
|
1049
1107
|
...(toolSelection.toolProviders
|
|
1050
1108
|
? { toolProviders: toolSelection.toolProviders }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CompiledDynamicWorkflowTask, CompiledTask, CompiledWorkflow, ThinkingLevel, WorkflowRunRecord, WorkflowTaskRunRecord } from "./types.js";
|
|
2
|
+
import { type WorkflowModelInfo } from "./workflow-runtime.js";
|
|
2
3
|
export interface DynamicArtifactInput {
|
|
3
4
|
kind: "workflow-artifact-ref";
|
|
4
5
|
name: string;
|
|
@@ -41,6 +42,7 @@ export declare function buildDynamicGeneratedCompiledTask(input: {
|
|
|
41
42
|
branchId?: string;
|
|
42
43
|
request: DynamicAgentRequest;
|
|
43
44
|
dynamic: CompiledDynamicWorkflowTask;
|
|
45
|
+
availableModels?: WorkflowModelInfo[];
|
|
44
46
|
}): Promise<CompiledTask>;
|
|
45
47
|
export declare function isDynamicCompiledTaskPayload(value: unknown): value is CompiledTask;
|
|
46
48
|
export declare function assertDynamicGeneratedMetadataMatches(compiledTask: CompiledTask, expected: {
|
|
@@ -6,6 +6,7 @@ import { readOrRebuildDynamicState } from "./dynamic-state.js";
|
|
|
6
6
|
import { sanitizeTaskId } from "./engine-run-graph.js";
|
|
7
7
|
import { fromProjectPath, isTerminalTaskStatus, readJson } from "./store.js";
|
|
8
8
|
import { classifyToolCapability, effectiveToolClassification, providersForSelectedTools, toolAllowedByAuthorityCeiling, } from "./tool-metadata.js";
|
|
9
|
+
import { resolveWorkflowRuntime, selectWorkflowRuntime, } from "./workflow-runtime.js";
|
|
9
10
|
const DYNAMIC_OUTPUT_MAX_DIGEST_CHARS = 1000;
|
|
10
11
|
const DYNAMIC_DELEGATION_TOOLS = new Set([
|
|
11
12
|
"skill_test_subagent",
|
|
@@ -69,6 +70,13 @@ export async function buildDynamicGeneratedCompiledTask(input) {
|
|
|
69
70
|
}
|
|
70
71
|
const toolProviders = executionProfile?.toolProviders ??
|
|
71
72
|
providersForSelectedTools(tools, new Map(Object.entries(input.controllerCompiledTask.runtime.toolProviders ?? {})));
|
|
73
|
+
const selectedRuntime = selectWorkflowRuntime(input.dynamic.runtimeOverrides, runtimeSettings(input.request), runtimeSettings(executionProfile), runtimeSettings(input.controllerCompiledTask.runtime), runtimeSettings(agentDefinition));
|
|
74
|
+
const resolvedRuntime = await resolveWorkflowRuntime(selectedRuntime, {
|
|
75
|
+
taskKey: input.generatedSpecId,
|
|
76
|
+
stageId: input.controllerStageId,
|
|
77
|
+
taskId: input.request.id,
|
|
78
|
+
agent: requestedAgent,
|
|
79
|
+
}, { availableModels: input.availableModels ?? input.dynamic.availableModels });
|
|
72
80
|
const unknownTools = (tools ?? []).filter((tool) => effectiveToolClassification(tool, toolProviders) === undefined);
|
|
73
81
|
if (unknownTools.length > 0) {
|
|
74
82
|
throw new Error(`dynamic agent requested tools without trusted classification metadata: ${unknownTools.join(", ")}`);
|
|
@@ -135,14 +143,7 @@ export async function buildDynamicGeneratedCompiledTask(input) {
|
|
|
135
143
|
explicitWorktreePolicy: requiresWorktree,
|
|
136
144
|
runtime: {
|
|
137
145
|
approvalMode: "non-interactive",
|
|
138
|
-
|
|
139
|
-
executionProfile?.model ??
|
|
140
|
-
input.controllerCompiledTask.runtime.model ??
|
|
141
|
-
agentDefinition.model,
|
|
142
|
-
thinking: input.request.thinking ??
|
|
143
|
-
executionProfile?.thinking ??
|
|
144
|
-
input.controllerCompiledTask.runtime.thinking ??
|
|
145
|
-
agentDefinition.thinking,
|
|
146
|
+
...resolvedRuntime,
|
|
146
147
|
tools,
|
|
147
148
|
...(toolProviders ? { toolProviders } : {}),
|
|
148
149
|
maxRuntimeMs: input.request.maxRuntimeMs ??
|
|
@@ -461,6 +462,18 @@ function requiredDynamicString(value, field, api = "ctx.agent()") {
|
|
|
461
462
|
}
|
|
462
463
|
return value.trim();
|
|
463
464
|
}
|
|
465
|
+
function runtimeSettings(value) {
|
|
466
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
467
|
+
return undefined;
|
|
468
|
+
const record = value;
|
|
469
|
+
const model = typeof record.model === "string" && record.model.trim()
|
|
470
|
+
? record.model.trim()
|
|
471
|
+
: undefined;
|
|
472
|
+
const thinking = typeof record.thinking === "string" && record.thinking.trim()
|
|
473
|
+
? record.thinking.trim()
|
|
474
|
+
: undefined;
|
|
475
|
+
return model || thinking ? { model, thinking } : undefined;
|
|
476
|
+
}
|
|
464
477
|
function optionalDynamicString(value, field) {
|
|
465
478
|
if (value === undefined)
|
|
466
479
|
return undefined;
|
package/dist/engine.d.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
+
import { type WorkflowModelInfo, type WorkflowRuntimeDefaults } from "./workflow-runtime.js";
|
|
1
2
|
import { type DynamicWorkflowUi } from "./dynamic-controller-policy.js";
|
|
2
|
-
import { type CompiledWorkflow, type
|
|
3
|
+
import { type CompiledWorkflow, type WorkflowRunRecord } from "./types.js";
|
|
3
4
|
export { buildRunSourceContext } from "./workflow-source-context-runtime.js";
|
|
4
5
|
export { evaluateLoopUntilCondition } from "./loop-runtime.js";
|
|
5
6
|
export type { DynamicWorkflowUi } from "./dynamic-controller-policy.js";
|
|
6
7
|
export interface WorkflowRunOptions {
|
|
7
8
|
task?: string;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
};
|
|
9
|
+
runtimeOverrides?: WorkflowRuntimeDefaults;
|
|
10
|
+
runtimeDefaults?: WorkflowRuntimeDefaults;
|
|
11
|
+
availableModels?: WorkflowModelInfo[];
|
|
12
12
|
dynamicUi?: DynamicWorkflowUi;
|
|
13
13
|
runId?: string;
|
|
14
14
|
parentRunId?: string;
|
|
15
15
|
}
|
|
16
16
|
interface WorkflowScheduleOptions {
|
|
17
17
|
dynamicUi?: DynamicWorkflowUi;
|
|
18
|
+
availableModels?: WorkflowModelInfo[];
|
|
18
19
|
}
|
|
19
20
|
export declare function runWorkflowSpec(specPath: string, cwd: string, options?: WorkflowRunOptions): Promise<WorkflowRunRecord>;
|
|
20
21
|
export declare function runDynamicTask(cwd: string, options?: WorkflowRunOptions): Promise<WorkflowRunRecord>;
|
package/dist/engine.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import { dirname, extname, join, resolve
|
|
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
|
import { compileWorkflow } from "./compiler.js";
|
|
6
6
|
import { loadWorkflowSpec } from "./schema.js";
|
|
7
|
-
import { createRunRecord, createTaskRunRecord, compiledWorkflowPath, fromProjectPath, indexSupervisorErrorPath, isTerminalWorkflowStatus, isTerminalTaskStatus, listRunRecords, readIndex, readJson, readRunRecord, resetTaskForResume, setTaskTerminal, supervisorPath, toProjectPath, updateIndex, withRunLease, writeJsonAtomic, writeRunRecord, writeCompiledRunArtifact, writeStaticRunArtifacts, } from "./store.js";
|
|
7
|
+
import { createRunRecord, createTaskRunRecord, compiledWorkflowPath, fromProjectPath, indexSupervisorErrorPath, isTerminalWorkflowStatus, isTerminalTaskStatus, listRunRecords, readIndex, readJson, readRunRecord, resetTaskForResume, setTaskTerminal, supervisorPath, toProjectPath, updateIndex, withRunLease, workflowRunPath, writeJsonAtomic, writeRunRecord, writeCompiledRunArtifact, writeStaticRunArtifacts, } from "./store.js";
|
|
8
8
|
import { resolveWorkflowBackend } from "./backend.js";
|
|
9
9
|
import { ensureManagedWorktree } from "./worktree.js";
|
|
10
10
|
import { resolveWorkflowHelperRef } from "./workflow-helpers.js";
|
|
11
11
|
import { buildAvailableToolView } from "./tool-metadata.js";
|
|
12
12
|
import { workflowBundleFingerprint, workflowBundleSpecPath, } from "./workflow-source-context-runtime.js";
|
|
13
|
-
import { readSimpleJsonPath } from "./workflow-runtime.js";
|
|
13
|
+
import { readSimpleJsonPath, } from "./workflow-runtime.js";
|
|
14
14
|
import { dynamicRunDir, hashDynamicRequest, readDynamicEvents, } from "./dynamic-events.js";
|
|
15
15
|
import { ensureDynamicControllerInitialized, readOrRebuildDynamicState, recordDynamicControllerPhase, recordDynamicControllerStatus, recordDynamicEventAndUpdateState, } from "./dynamic-state.js";
|
|
16
16
|
import { DynamicControllerBudgetBlocked, DynamicControllerNestedApprovalBlocked, DynamicControllerSuspended, } from "./dynamic-controller-errors.js";
|
|
@@ -21,7 +21,6 @@ import { normalizeDynamicFanoutPlanRequest, runDynamicDecisionLoopStatusPersistC
|
|
|
21
21
|
import { assertRunTaskPositionalAlignment, buildForeachGeneratedTasks, dependenciesReady, markDagDependentsSkipped, nextTaskRecordIndex, reconcileDynamicGeneratedRunRecords, recoverStaleRunningDynamicControllers, replaceDependencyList, sourceStageIdsForFrom, stageSourcePolicy, updateDownstreamDependencies, } from "./engine-run-graph.js";
|
|
22
22
|
import { reconcileLoopTaskMaterialization, scheduleLoop, } from "./loop-runtime.js";
|
|
23
23
|
import { executeSupportTask, normalizeDynamicControllerOutput, prepareArtifactGraphRetryTask, prepareDagTask, readArtifactGraphControl, readArtifactGraphSupportSources, readSupportSources, writeArtifactGraphDynamicResult, } from "./artifact-graph-runtime.js";
|
|
24
|
-
import { isDynamicOutputProfile, } from "./dynamic-profiles.js";
|
|
25
24
|
import { DIRECT_DYNAMIC_RUNTIME_VERSION, ensureDirectDynamicRuntimeBundle, } from "./dynamic-runtime-bundle.js";
|
|
26
25
|
import { WORKFLOW_RUN_TYPE, } from "./types.js";
|
|
27
26
|
export { buildRunSourceContext } from "./workflow-source-context-runtime.js";
|
|
@@ -37,6 +36,7 @@ const DYNAMIC_CONTROLLER_ENGINE_CAPABILITIES = Object.freeze({
|
|
|
37
36
|
});
|
|
38
37
|
const DYNAMIC_CONTROLLER_ENGINE_INTEGRITY_ERROR_MESSAGE = "incompatible or stale pi-workflow engine: dynamic controller context is missing runDecisionLoop (rebuild dist / reload workflow engine)";
|
|
39
38
|
const supervisorTimers = new Map();
|
|
39
|
+
const supervisorRunMtimes = new Map();
|
|
40
40
|
export async function runWorkflowSpec(specPath, cwd, options = {}) {
|
|
41
41
|
const loaded = await loadWorkflowSpec(specPath, cwd);
|
|
42
42
|
return runLoadedWorkflowSpec(cwd, loaded.specPath, loaded.spec, options);
|
|
@@ -62,7 +62,9 @@ async function runLoadedWorkflowSpec(cwd, specPath, spec, options, provenance) {
|
|
|
62
62
|
cwd,
|
|
63
63
|
specPath,
|
|
64
64
|
task: options.task,
|
|
65
|
+
runtimeOverrides: options.runtimeOverrides,
|
|
65
66
|
runtimeDefaults: options.runtimeDefaults,
|
|
67
|
+
availableModels: options.availableModels,
|
|
66
68
|
});
|
|
67
69
|
const { run } = await createRunRecord(cwd, compiled, specPath, {
|
|
68
70
|
runId: options.runId,
|
|
@@ -75,11 +77,14 @@ async function runLoadedWorkflowSpec(cwd, specPath, spec, options, provenance) {
|
|
|
75
77
|
await writeStaticRunArtifacts(cwd, run, compiled, spec);
|
|
76
78
|
await writeRunRecord(cwd, run);
|
|
77
79
|
});
|
|
78
|
-
const
|
|
80
|
+
const scheduleOptions = {
|
|
79
81
|
dynamicUi: options.dynamicUi,
|
|
80
|
-
|
|
82
|
+
availableModels: options.availableModels,
|
|
83
|
+
};
|
|
84
|
+
const scheduled = (await scheduleRun(cwd, run.runId, compiled, scheduleOptions)) ??
|
|
85
|
+
(await readRunRecord(cwd, run.runId));
|
|
81
86
|
if (scheduled.status === "running")
|
|
82
|
-
watchRun(cwd, scheduled.runId,
|
|
87
|
+
watchRun(cwd, scheduled.runId, scheduleOptions);
|
|
83
88
|
return scheduled;
|
|
84
89
|
}
|
|
85
90
|
export async function refreshRun(cwd, runIdOrPrefix) {
|
|
@@ -167,15 +172,26 @@ export function watchRun(cwd, runId, options = {}) {
|
|
|
167
172
|
return;
|
|
168
173
|
const timer = setInterval(() => {
|
|
169
174
|
void (async () => {
|
|
175
|
+
const previousMtime = supervisorRunMtimes.get(key);
|
|
176
|
+
const beforeMtime = await readRunMtimeMs(cwd, runId);
|
|
170
177
|
const refreshed = await refreshRun(cwd, runId);
|
|
178
|
+
const afterMtime = await readRunMtimeMs(cwd, runId);
|
|
179
|
+
const currentMtime = afterMtime ?? beforeMtime;
|
|
180
|
+
if (currentMtime !== undefined)
|
|
181
|
+
supervisorRunMtimes.set(key, currentMtime);
|
|
171
182
|
if (refreshed.status === "running") {
|
|
172
|
-
|
|
183
|
+
const unchanged = previousMtime !== undefined &&
|
|
184
|
+
currentMtime !== undefined &&
|
|
185
|
+
currentMtime <= previousMtime;
|
|
186
|
+
if (!unchanged)
|
|
187
|
+
await scheduleRun(cwd, runId, undefined, options);
|
|
173
188
|
return;
|
|
174
189
|
}
|
|
175
190
|
const existing = supervisorTimers.get(key);
|
|
176
191
|
if (existing)
|
|
177
192
|
clearInterval(existing);
|
|
178
193
|
supervisorTimers.delete(key);
|
|
194
|
+
supervisorRunMtimes.delete(key);
|
|
179
195
|
})().catch((error) => {
|
|
180
196
|
void recordSupervisorError(cwd, runId, error);
|
|
181
197
|
});
|
|
@@ -183,6 +199,16 @@ export function watchRun(cwd, runId, options = {}) {
|
|
|
183
199
|
timer.unref?.();
|
|
184
200
|
supervisorTimers.set(key, timer);
|
|
185
201
|
}
|
|
202
|
+
async function readRunMtimeMs(cwd, runId) {
|
|
203
|
+
try {
|
|
204
|
+
return (await stat(workflowRunPath(cwd, runId))).mtimeMs;
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
if (error.code === "ENOENT")
|
|
208
|
+
return undefined;
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
186
212
|
export async function scheduleRun(cwd, runId, compiled, options = {}) {
|
|
187
213
|
return withRunLease(cwd, runId, async () => {
|
|
188
214
|
let run = await readRunRecord(cwd, runId);
|
|
@@ -629,6 +655,7 @@ async function executeDynamicControllerTask(cwd, run, compiledFlow, controllerIn
|
|
|
629
655
|
sources,
|
|
630
656
|
dynamic: compiledTask.dynamic,
|
|
631
657
|
dynamicUi: options.dynamicUi,
|
|
658
|
+
availableModels: options.availableModels,
|
|
632
659
|
});
|
|
633
660
|
await assertDynamicGeneratedTasksSettled({
|
|
634
661
|
cwd,
|
|
@@ -638,6 +665,7 @@ async function executeDynamicControllerTask(cwd, run, compiledFlow, controllerIn
|
|
|
638
665
|
controllerTask: task,
|
|
639
666
|
controllerCompiledTask: compiledTask,
|
|
640
667
|
dynamic: compiledTask.dynamic,
|
|
668
|
+
availableModels: options.availableModels,
|
|
641
669
|
});
|
|
642
670
|
await recordActiveRuntime();
|
|
643
671
|
const unrunBranchBlockers = await dynamicUnrunBranchBlockers(cwd, run.runId, task.specId);
|
|
@@ -1170,57 +1198,12 @@ function requiredDynamicString(value, field, api = "ctx.agent()") {
|
|
|
1170
1198
|
}
|
|
1171
1199
|
return value.trim();
|
|
1172
1200
|
}
|
|
1173
|
-
function optionalDynamicString(value, field) {
|
|
1174
|
-
if (value === undefined)
|
|
1175
|
-
return undefined;
|
|
1176
|
-
return requiredDynamicString(value, field);
|
|
1177
|
-
}
|
|
1178
|
-
function optionalDynamicStringArray(value, field) {
|
|
1179
|
-
if (value === undefined)
|
|
1180
|
-
return undefined;
|
|
1181
|
-
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
1182
|
-
throw new Error(`ctx.agent() ${field} must be an array of strings`);
|
|
1183
|
-
}
|
|
1184
|
-
return value.map((item) => item.trim()).filter(Boolean);
|
|
1185
|
-
}
|
|
1186
|
-
function isPlainDynamicRecord(value) {
|
|
1187
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1188
|
-
}
|
|
1189
|
-
function optionalDynamicPositiveInteger(value, field) {
|
|
1190
|
-
if (value === undefined)
|
|
1191
|
-
return undefined;
|
|
1192
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
1193
|
-
throw new Error(`ctx.agent() ${field} must be a positive integer`);
|
|
1194
|
-
}
|
|
1195
|
-
return value;
|
|
1196
|
-
}
|
|
1197
|
-
function requiredDynamicOutputProfile(value, field, api) {
|
|
1198
|
-
const profile = requiredDynamicString(value, field, api);
|
|
1199
|
-
if (!isDynamicOutputProfile(profile)) {
|
|
1200
|
-
throw new Error(`${api} ${field} has an unsupported output profile`);
|
|
1201
|
-
}
|
|
1202
|
-
return profile;
|
|
1203
|
-
}
|
|
1204
|
-
function requiredDynamicNonNegativeInteger(value, field, api) {
|
|
1205
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
1206
|
-
throw new Error(`${api} ${field} must be a non-negative integer`);
|
|
1207
|
-
}
|
|
1208
|
-
return value;
|
|
1209
|
-
}
|
|
1210
1201
|
function requiredDynamicPositiveInteger(value, field, api) {
|
|
1211
1202
|
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
1212
1203
|
throw new Error(`${api} ${field} must be a positive integer`);
|
|
1213
1204
|
}
|
|
1214
1205
|
return value;
|
|
1215
1206
|
}
|
|
1216
|
-
function optionalDynamicStringField(value) {
|
|
1217
|
-
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
1218
|
-
}
|
|
1219
|
-
function optionalDynamicOutputProfile(value) {
|
|
1220
|
-
if (value === undefined)
|
|
1221
|
-
return undefined;
|
|
1222
|
-
return requiredDynamicOutputProfile(value, "outputProfile", "ctx.agent()");
|
|
1223
|
-
}
|
|
1224
1207
|
async function currentDynamicBudgetRemaining(input) {
|
|
1225
1208
|
const state = await readOrRebuildDynamicState(input.cwd, input.run.runId);
|
|
1226
1209
|
const run = await readRunRecord(input.cwd, input.run.runId).catch(() => input.run);
|
|
@@ -1539,6 +1522,7 @@ async function repairMissingDynamicGeneratedTask(input, specId) {
|
|
|
1539
1522
|
branchId: optionalEventString(event.payload.branchId),
|
|
1540
1523
|
request,
|
|
1541
1524
|
dynamic: input.dynamic,
|
|
1525
|
+
availableModels: input.availableModels,
|
|
1542
1526
|
});
|
|
1543
1527
|
assertDynamicGeneratedMetadataMatches(compiledTask, {
|
|
1544
1528
|
controllerSpecId: input.controllerTask.specId,
|
|
@@ -1634,6 +1618,7 @@ async function runDynamicAgentRequest(input) {
|
|
|
1634
1618
|
branchId: generationBranchId,
|
|
1635
1619
|
request: generationRequest,
|
|
1636
1620
|
dynamic: input.dynamic,
|
|
1621
|
+
availableModels: input.availableModels,
|
|
1637
1622
|
});
|
|
1638
1623
|
assertDynamicGeneratedMetadataMatches(compiledTask, {
|
|
1639
1624
|
controllerSpecId: input.controllerTask.specId,
|