@agwab/pi-workflow 0.2.1 → 0.4.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/README.md +3 -1
- package/dist/artifact-graph-runtime.d.ts +1 -1
- package/dist/artifact-graph-runtime.js +10 -5
- package/dist/artifact-graph-schema.js +127 -5
- package/dist/compiler.js +52 -19
- package/dist/dynamic-generated-task-runtime.js +3 -1
- package/dist/dynamic-profiles.d.ts +1 -1
- package/dist/engine-run-graph.d.ts +3 -0
- package/dist/engine-run-graph.js +194 -4
- package/dist/engine.d.ts +5 -0
- package/dist/engine.js +389 -41
- package/dist/extension.d.ts +2 -1
- package/dist/extension.js +30 -8
- package/dist/index.d.ts +11 -3
- package/dist/index.js +6 -1
- package/dist/prompt-json.d.ts +7 -0
- package/dist/prompt-json.js +13 -0
- package/dist/roles.d.ts +1 -1
- package/dist/roles.js +5 -8
- package/dist/store.d.ts +20 -1
- package/dist/store.js +139 -35
- package/dist/strings.d.ts +11 -0
- package/dist/strings.js +24 -0
- package/dist/subagent-backend.js +710 -40
- package/dist/types.d.ts +107 -1
- package/dist/verification-ontology.d.ts +31 -0
- package/dist/verification-ontology.js +66 -0
- package/dist/workflow-artifact-tool.js +5 -6
- package/dist/workflow-artifacts.d.ts +7 -0
- package/dist/workflow-artifacts.js +55 -4
- package/dist/workflow-fetch-cache-extension.d.ts +1 -0
- package/dist/workflow-fetch-cache-extension.js +57 -9
- package/dist/workflow-metrics.d.ts +113 -0
- package/dist/workflow-metrics.js +272 -0
- package/dist/workflow-output-artifacts.js +5 -3
- package/dist/workflow-partial-output.d.ts +45 -0
- package/dist/workflow-partial-output.js +205 -0
- package/dist/workflow-progress-health.js +42 -10
- package/dist/workflow-runtime.js +10 -1
- package/dist/workflow-view.js +3 -1
- package/dist/workflow-web-source-extension.js +194 -52
- package/dist/workflow-web-source.d.ts +2 -1
- package/dist/workflow-web-source.js +109 -30
- package/docs/usage.md +76 -29
- package/node_modules/@agwab/pi-subagent/README.md +3 -3
- package/node_modules/@agwab/pi-subagent/api.mjs +1 -0
- package/node_modules/@agwab/pi-subagent/docs/usage.md +63 -12
- package/node_modules/@agwab/pi-subagent/package.json +2 -2
- package/node_modules/@agwab/pi-subagent/src/api.ts +54 -1
- package/node_modules/@agwab/pi-subagent/src/artifacts/registry.ts +9 -4
- package/node_modules/@agwab/pi-subagent/src/artifacts/result.ts +8 -0
- package/node_modules/@agwab/pi-subagent/src/core/constants.ts +9 -0
- package/node_modules/@agwab/pi-subagent/src/core/validation.ts +21 -0
- package/node_modules/@agwab/pi-subagent/src/index.ts +1046 -576
- package/node_modules/@agwab/pi-subagent/src/orchestrate/async.ts +279 -156
- package/node_modules/@agwab/pi-subagent/src/orchestrate/interrupt.ts +165 -89
- package/node_modules/@agwab/pi-subagent/src/orchestrate/reconcile.ts +111 -65
- package/node_modules/@agwab/pi-subagent/src/orchestrate/run-ref.ts +219 -0
- package/node_modules/@agwab/pi-subagent/src/orchestrate/run.ts +88 -8
- package/node_modules/@agwab/pi-subagent/src/orchestrate/status.ts +614 -298
- package/node_modules/@agwab/pi-subagent/src/panel.ts +1356 -560
- package/node_modules/@agwab/pi-subagent/src/runners/headless-model.ts +53 -5
- package/node_modules/@agwab/pi-subagent/src/runners/tmux.ts +13 -6
- package/package.json +2 -2
- package/skills/workflow-guide/SKILL.md +1 -0
- package/src/artifact-graph-runtime.ts +19 -13
- package/src/artifact-graph-schema.ts +143 -3
- package/src/cli.mjs +52 -0
- package/src/compiler.ts +63 -18
- package/src/dynamic-generated-task-runtime.ts +3 -1
- package/src/dynamic-profiles.ts +1 -1
- package/src/engine-run-graph.ts +246 -4
- package/src/engine.ts +545 -38
- package/src/extension.ts +36 -6
- package/src/index.ts +52 -1
- package/src/prompt-json.ts +13 -0
- package/src/roles.ts +6 -9
- package/src/store.ts +194 -42
- package/src/strings.ts +38 -0
- package/src/subagent-backend.ts +921 -62
- package/src/types.ts +116 -2
- package/src/verification-ontology.ts +88 -0
- package/src/workflow-artifact-tool.ts +5 -7
- package/src/workflow-artifacts.ts +83 -3
- package/src/workflow-fetch-cache-extension.ts +78 -13
- package/src/workflow-metrics.ts +478 -0
- package/src/workflow-output-artifacts.ts +5 -3
- package/src/workflow-partial-output.ts +299 -0
- package/src/workflow-progress-health.ts +47 -15
- package/src/workflow-runtime.ts +18 -2
- package/src/workflow-view.ts +2 -1
- package/src/workflow-web-source-extension.ts +654 -232
- package/src/workflow-web-source.ts +153 -39
- package/workflows/README.md +7 -25
- package/workflows/deep-research/batched-verification.spec.json +253 -0
- package/workflows/deep-research/helpers/batch-verification-candidates.mjs +136 -0
- package/workflows/deep-research/helpers/claim-evidence-gate.mjs +229 -36
- package/workflows/deep-research/helpers/final-audit-packet.mjs +1 -4
- package/workflows/deep-research/helpers/normalize-input-packet.mjs +81 -2
- package/workflows/deep-research/helpers/render-executive.mjs +40 -26
- package/workflows/deep-research/helpers/sanitize-verification-candidates.mjs +89 -15
- package/workflows/deep-research/helpers/shadow-select-verification.mjs +229 -0
- package/workflows/deep-research/helpers/verification-ontology.mjs +77 -0
- package/workflows/deep-research/schemas/deep-research-executive-render-control.schema.json +3 -3
- package/workflows/deep-research/schemas/deep-research-research-questions-control.schema.json +38 -0
- package/workflows/deep-research/schemas/deep-research-sanitize-claims-control.schema.json +63 -0
- package/workflows/deep-research/schemas/deep-research-verify-claims-batch-control.schema.json +47 -0
- package/workflows/deep-research/schemas/deep-research-verify-claims-control.schema.json +13 -3
- package/workflows/deep-research/spec.json +32 -12
- package/workflows/impact-review/spec.json +3 -3
- package/workflows/spec-review/helpers/spec-review-pipeline.mjs +1 -8
- package/dist/dynamic-loader.d.ts +0 -25
- package/dist/dynamic-loader.js +0 -13
- package/skills/workflow-guide/scaffolds/dag-required-reads/spec.json.validate.stderr +0 -0
- package/skills/workflow-guide/scaffolds/dag-required-reads/spec.json.validate.stdout +0 -13
- package/src/dynamic-loader.ts +0 -49
- package/workflows/impact-review/schemas/docs-release-impact-control.schema.json +0 -42
- package/workflows/impact-review/schemas/security-performance-impact-control.schema.json +0 -42
- package/workflows/impact-review/schemas/state-data-impact-control.schema.json +0 -42
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
`pi-workflow` lets Pi run named, repeatable multi-step workflows: research, code review, spec conformance checks, impact review, and project-specific team routines.
|
|
14
14
|
|
|
15
|
-
Built on [`@agwab/pi-subagent`](https://github.com/AgwaB/pi-subagent), it coordinates Pi subagent workers across workflow steps, passes results between them, and records the run so it can be inspected or resumed.
|
|
15
|
+
Built on [`@agwab/pi-subagent`](https://github.com/AgwaB/pi-subagent), it coordinates Pi subagent workers across workflow steps, passes results between them, and records the run so it can be inspected, stopped, or resumed.
|
|
16
16
|
|
|
17
17
|
You choose a workflow and describe the task in natural language.
|
|
18
18
|
|
|
@@ -74,6 +74,8 @@ For a one-off adaptive workflow that should plan, fan out, and synthesize withou
|
|
|
74
74
|
|
|
75
75
|
`/workflow dynamic` uses pi-workflow's built-in trusted dynamic controller and records a normal workflow run under `.pi/workflows/`. Use it when you explicitly want adaptive orchestration rather than a named reusable workflow.
|
|
76
76
|
|
|
77
|
+
To interrupt a non-terminal run and stop its local supervisor watch, use `/workflow stop <run-id>`. Resume later with `/workflow resume <run-id>` if unfinished tasks should be retried.
|
|
78
|
+
|
|
77
79
|
## Usage: choose an execution mode
|
|
78
80
|
|
|
79
81
|
Use the bundled `execution-router` skill when you are not sure whether a task should be handled directly, by a targeted verifier/subagent, by an existing workflow, or by a new workflow:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type WorkflowSourceManifestSource } from "./workflow-artifact-tool.js";
|
|
2
|
-
import {
|
|
2
|
+
import type { CompiledTask, CompiledWorkflow, WorkflowRunRecord, WorkflowTaskRunRecord } from "./types.js";
|
|
3
3
|
export declare function executeSupportTask(cwd: string, run: WorkflowRunRecord, task: WorkflowTaskRunRecord, compiledTask: CompiledWorkflow["tasks"][number]): Promise<boolean>;
|
|
4
4
|
export declare function readSupportSources(cwd: string, run: WorkflowRunRecord, dependsOn: string[]): Promise<Record<string, unknown>>;
|
|
5
5
|
export declare function readArtifactGraphSupportSources(cwd: string, run: WorkflowRunRecord, dependsOn: string[]): Promise<Record<string, unknown>>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, extname, join } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { stringifyPromptJson } from "./prompt-json.js";
|
|
5
|
+
import { compactStrings } from "./strings.js";
|
|
4
6
|
import { loadWorkflowHelper } from "./workflow-helpers.js";
|
|
5
7
|
import { WORKFLOW_ARTIFACT_TOOL_NAME, writeWorkflowArtifactExtensionWrapper, } from "./workflow-artifact-extension.js";
|
|
6
8
|
import { WORKFLOW_SOURCE_MANIFEST_SCHEMA, } from "./workflow-artifact-tool.js";
|
|
@@ -337,11 +339,14 @@ export async function prepareDagTask(cwd, run, compiledFlow, index) {
|
|
|
337
339
|
compiledTask.compiledPrompt,
|
|
338
340
|
"# Source Stage Context",
|
|
339
341
|
"Use this deterministic source context packet. Prefer structuredOutput over outputPreview. Do not assume dependencies beyond this explicit packet.",
|
|
340
|
-
|
|
342
|
+
stringifyPromptJson({ ...context, missingDependencies: missing }),
|
|
341
343
|
].join("\n\n"),
|
|
342
344
|
};
|
|
343
345
|
}
|
|
344
346
|
async function prepareArtifactGraphTask(cwd, run, compiledTask, task, contextDependsOn) {
|
|
347
|
+
if (compiledTask.artifactGraph?.artifactAccess === "none") {
|
|
348
|
+
return { ...compiledTask, cwd: task.cwd };
|
|
349
|
+
}
|
|
345
350
|
const taskDir = dirname(fromProjectPath(cwd, task.files.result));
|
|
346
351
|
const manifestPath = join(taskDir, "source-manifest.json");
|
|
347
352
|
const ledgerPath = join(taskDir, "read-ledger.jsonl");
|
|
@@ -665,7 +670,7 @@ export function formatArtifactGraphSourceContext(sources, requiredReads) {
|
|
|
665
670
|
return [
|
|
666
671
|
"# Workflow Artifact Inputs",
|
|
667
672
|
"Use workflow_artifact to list/read upstream workflow artifacts. Inline controlProjection fields are authoritative for the projected data they contain; use artifact reads for declared requiredReads, missing fields, or debug detail.",
|
|
668
|
-
|
|
673
|
+
'Projected reads must include a JSON path when using maxItems or maxChars, for example {"action":"read","source":"plan","artifact":"control","path":"$.factSlots","maxItems":8,"maxChars":2000}. For a whole artifact read, omit maxItems/maxChars.',
|
|
669
674
|
requiredReads.length > 0
|
|
670
675
|
? [
|
|
671
676
|
"Required reads before final output:",
|
|
@@ -673,7 +678,7 @@ export function formatArtifactGraphSourceContext(sources, requiredReads) {
|
|
|
673
678
|
].join("\n")
|
|
674
679
|
: "No hard requiredReads are declared for this stage.",
|
|
675
680
|
"Available sources:",
|
|
676
|
-
|
|
681
|
+
stringifyPromptJson(sources.map((source) => ({
|
|
677
682
|
source: source.source,
|
|
678
683
|
taskId: source.taskId,
|
|
679
684
|
specId: source.specId,
|
|
@@ -687,11 +692,11 @@ export function formatArtifactGraphSourceContext(sources, requiredReads) {
|
|
|
687
692
|
projectionMissingPaths: source.projectionMissingPaths,
|
|
688
693
|
projectionTruncated: source.projectionTruncated,
|
|
689
694
|
availableArtifacts: Object.keys(source.artifacts),
|
|
690
|
-
}))
|
|
695
|
+
}))),
|
|
691
696
|
].join("\n\n");
|
|
692
697
|
}
|
|
693
698
|
function uniqueStrings(values) {
|
|
694
|
-
return
|
|
699
|
+
return compactStrings(values, { trim: false, dropWhitespaceOnly: true });
|
|
695
700
|
}
|
|
696
701
|
export async function readArtifactGraphControl(cwd, task) {
|
|
697
702
|
const taskDir = dirname(fromProjectPath(cwd, task.files.result));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isAbsolute } from "node:path";
|
|
2
2
|
import { DYNAMIC_OUTPUT_PROFILES } from "./dynamic-profiles.js";
|
|
3
|
+
import { compactStrings } from "./strings.js";
|
|
3
4
|
import { APPROVAL_MODES, FAST_MODES, THINKING_LEVELS, TOOL_CLASSIFICATIONS, WORKTREE_POLICIES, WorkflowValidationError, } from "./types.js";
|
|
4
5
|
const TOP_LEVEL_KEYS = new Set([
|
|
5
6
|
"schemaVersion",
|
|
@@ -58,10 +59,16 @@ const OUTPUT_KEYS = new Set([
|
|
|
58
59
|
"analysis",
|
|
59
60
|
"refs",
|
|
60
61
|
"maxDigestChars",
|
|
62
|
+
"partial",
|
|
61
63
|
]);
|
|
64
|
+
const OUTPUT_PARTIAL_KEYS = new Set(["paths"]);
|
|
62
65
|
const REQUIRED_FLAG_KEYS = new Set(["required"]);
|
|
63
66
|
const REFS_OUTPUT_KEYS = new Set(["required", "minItems"]);
|
|
64
|
-
const INPUT_POLICY_KEYS = new Set([
|
|
67
|
+
const INPUT_POLICY_KEYS = new Set([
|
|
68
|
+
"requiredReads",
|
|
69
|
+
"enforcement",
|
|
70
|
+
"artifactAccess",
|
|
71
|
+
]);
|
|
65
72
|
const SOURCE_PROJECTION_KEYS = new Set(["include", "maxChars"]);
|
|
66
73
|
const SUPPORT_KEYS = new Set(["uses", "options"]);
|
|
67
74
|
const DYNAMIC_STAGE_FORBIDDEN_KEYS = new Set([
|
|
@@ -172,7 +179,8 @@ const UNTIL_KEYS = new Set([
|
|
|
172
179
|
"all",
|
|
173
180
|
"any",
|
|
174
181
|
]);
|
|
175
|
-
const FOREACH_FROM_KEYS = new Set(["source", "path"]);
|
|
182
|
+
const FOREACH_FROM_KEYS = new Set(["source", "path", "streaming"]);
|
|
183
|
+
const FOREACH_STREAMING_KEYS = new Set(["enabled", "minChunk"]);
|
|
176
184
|
const NORMAL_ARTIFACT_KINDS = new Set([
|
|
177
185
|
"control",
|
|
178
186
|
"analysis",
|
|
@@ -262,8 +270,48 @@ function validateStageArray(value, path, issues) {
|
|
|
262
270
|
for (const [index, item] of value.entries()) {
|
|
263
271
|
validateStage(item, `${path}[${index}]`, ids, sourceIds, issues);
|
|
264
272
|
}
|
|
273
|
+
validateStreamingProducerDeclarations(value, path, issues);
|
|
265
274
|
validateStageDependencyGraph(value, path, issues);
|
|
266
275
|
}
|
|
276
|
+
function validateStreamingProducerDeclarations(stages, path, issues) {
|
|
277
|
+
const byId = new Map();
|
|
278
|
+
for (const stage of stages) {
|
|
279
|
+
if (isRecord(stage) && typeof stage.id === "string") {
|
|
280
|
+
byId.set(stage.id, stage);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
for (const [index, stage] of stages.entries()) {
|
|
284
|
+
if (!isRecord(stage) || !isRecord(stage.from))
|
|
285
|
+
continue;
|
|
286
|
+
const from = stage.from;
|
|
287
|
+
if (!isRecord(from.streaming) || from.streaming.enabled !== true)
|
|
288
|
+
continue;
|
|
289
|
+
const source = typeof from.source === "string" ? from.source : undefined;
|
|
290
|
+
const controlPath = typeof from.path === "string" ? from.path : undefined;
|
|
291
|
+
if (!source || !controlPath)
|
|
292
|
+
continue;
|
|
293
|
+
const sourceStage = byId.get(source);
|
|
294
|
+
const partialPaths = outputPartialPaths(sourceStage);
|
|
295
|
+
if (!partialPaths.includes(controlPath)) {
|
|
296
|
+
issues.push({
|
|
297
|
+
path: `${path}[${index}].from.streaming`,
|
|
298
|
+
message: `source stage "${source}" must declare output.partial.paths including "${controlPath}" to use streaming`,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function outputPartialPaths(stage) {
|
|
304
|
+
const output = isRecord(stage?.output) ? stage.output : undefined;
|
|
305
|
+
const partial = isRecord(output?.partial) ? output.partial : undefined;
|
|
306
|
+
return Array.isArray(partial?.paths)
|
|
307
|
+
? compactStrings(partial.paths, {
|
|
308
|
+
trim: false,
|
|
309
|
+
unique: false,
|
|
310
|
+
dropEmpty: false,
|
|
311
|
+
dropWhitespaceOnly: false,
|
|
312
|
+
})
|
|
313
|
+
: [];
|
|
314
|
+
}
|
|
267
315
|
function validateStageDependencyGraph(stages, path, issues) {
|
|
268
316
|
const ids = new Set(stages
|
|
269
317
|
.filter(isRecord)
|
|
@@ -334,7 +382,12 @@ function extractDependencyRefs(value) {
|
|
|
334
382
|
if (typeof value === "string")
|
|
335
383
|
return [value];
|
|
336
384
|
if (Array.isArray(value))
|
|
337
|
-
return value
|
|
385
|
+
return compactStrings(value, {
|
|
386
|
+
trim: false,
|
|
387
|
+
unique: false,
|
|
388
|
+
dropEmpty: false,
|
|
389
|
+
dropWhitespaceOnly: false,
|
|
390
|
+
});
|
|
338
391
|
if (isRecord(value) && typeof value.source === "string")
|
|
339
392
|
return [value.source];
|
|
340
393
|
return [];
|
|
@@ -445,7 +498,7 @@ function validateStage(value, path, siblingIds, sourceIds, issues) {
|
|
|
445
498
|
allowControlPath: false,
|
|
446
499
|
});
|
|
447
500
|
validateSourceProjection(stage.sourceProjection, `${path}.sourceProjection`, issues);
|
|
448
|
-
validateInputPolicy(stage.inputPolicy, `${path}.inputPolicy`, sourceIds, issues);
|
|
501
|
+
validateInputPolicy(stage.inputPolicy, `${path}.inputPolicy`, sourceIds, issues, stage.sourceProjection);
|
|
449
502
|
validateOutput(stage.output, `${path}.output`, issues);
|
|
450
503
|
validateSupportStage(stage, type, path, issues);
|
|
451
504
|
validateDynamicStage(stage, type, path, issues);
|
|
@@ -518,6 +571,19 @@ function validateControlPathRef(value, path, siblingIds, issues) {
|
|
|
518
571
|
message: "must be a control JSONPath starting with $.",
|
|
519
572
|
});
|
|
520
573
|
}
|
|
574
|
+
if (value.streaming !== undefined) {
|
|
575
|
+
validateForeachStreaming(value.streaming, `${path}.streaming`, issues);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function validateForeachStreaming(value, path, issues) {
|
|
579
|
+
const streaming = recordAt(value, path, issues);
|
|
580
|
+
if (!streaming)
|
|
581
|
+
return;
|
|
582
|
+
rejectUnknownKeys(streaming, FOREACH_STREAMING_KEYS, path, issues);
|
|
583
|
+
if (streaming.enabled !== true) {
|
|
584
|
+
issues.push({ path: `${path}.enabled`, message: "must be true" });
|
|
585
|
+
}
|
|
586
|
+
optionalPositiveInteger(streaming.minChunk, `${path}.minChunk`, issues);
|
|
521
587
|
}
|
|
522
588
|
function validateKnownStageRef(stageId, path, siblingIds, issues) {
|
|
523
589
|
if (stageId.trim() === "") {
|
|
@@ -539,6 +605,39 @@ function validateOutput(value, path, issues) {
|
|
|
539
605
|
optionalPositiveInteger(output.maxDigestChars, `${path}.maxDigestChars`, issues);
|
|
540
606
|
validateRequiredFlagObject(output.analysis, `${path}.analysis`, issues);
|
|
541
607
|
validateRefsOutputObject(output.refs, `${path}.refs`, issues);
|
|
608
|
+
validatePartialOutput(output.partial, `${path}.partial`, issues);
|
|
609
|
+
}
|
|
610
|
+
function validatePartialOutput(value, path, issues) {
|
|
611
|
+
if (value === undefined)
|
|
612
|
+
return;
|
|
613
|
+
const partial = recordAt(value, path, issues);
|
|
614
|
+
if (!partial)
|
|
615
|
+
return;
|
|
616
|
+
rejectUnknownKeys(partial, OUTPUT_PARTIAL_KEYS, path, issues);
|
|
617
|
+
if (!Array.isArray(partial.paths)) {
|
|
618
|
+
issues.push({ path: `${path}.paths`, message: "must be an array" });
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
if (partial.paths.length === 0) {
|
|
622
|
+
issues.push({ path: `${path}.paths`, message: "must not be empty" });
|
|
623
|
+
}
|
|
624
|
+
const seen = new Set();
|
|
625
|
+
for (const [index, item] of partial.paths.entries()) {
|
|
626
|
+
const itemPath = `${path}.paths[${index}]`;
|
|
627
|
+
if (typeof item !== "string" || item.trim() === "") {
|
|
628
|
+
issues.push({ path: itemPath, message: "must be a non-empty string" });
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
if (!item.startsWith("$.")) {
|
|
632
|
+
issues.push({
|
|
633
|
+
path: itemPath,
|
|
634
|
+
message: "must be a control JSONPath starting with $.",
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
if (seen.has(item))
|
|
638
|
+
issues.push({ path: itemPath, message: `duplicate value "${item}"` });
|
|
639
|
+
seen.add(item);
|
|
640
|
+
}
|
|
542
641
|
}
|
|
543
642
|
function validateControlSchemaRef(value, path, issues) {
|
|
544
643
|
if (value === undefined)
|
|
@@ -568,7 +667,7 @@ function validateRefsOutputObject(value, path, issues) {
|
|
|
568
667
|
optionalBoolean(object.required, `${path}.required`, issues);
|
|
569
668
|
optionalPositiveInteger(object.minItems, `${path}.minItems`, issues);
|
|
570
669
|
}
|
|
571
|
-
function validateInputPolicy(value, path, siblingIds, issues) {
|
|
670
|
+
function validateInputPolicy(value, path, siblingIds, issues, sourceProjection) {
|
|
572
671
|
if (value === undefined)
|
|
573
672
|
return;
|
|
574
673
|
const policy = recordAt(value, path, issues);
|
|
@@ -579,6 +678,29 @@ function validateInputPolicy(value, path, siblingIds, issues) {
|
|
|
579
678
|
if (policy.enforcement !== undefined && policy.enforcement !== "fail") {
|
|
580
679
|
issues.push({ path: `${path}.enforcement`, message: 'must be "fail"' });
|
|
581
680
|
}
|
|
681
|
+
if (policy.artifactAccess !== undefined &&
|
|
682
|
+
policy.artifactAccess !== "enabled" &&
|
|
683
|
+
policy.artifactAccess !== "none") {
|
|
684
|
+
issues.push({
|
|
685
|
+
path: `${path}.artifactAccess`,
|
|
686
|
+
message: 'must be "enabled" or "none"',
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
if (policy.artifactAccess === "none") {
|
|
690
|
+
if (Array.isArray(policy.requiredReads) &&
|
|
691
|
+
policy.requiredReads.length > 0) {
|
|
692
|
+
issues.push({
|
|
693
|
+
path: `${path}.requiredReads`,
|
|
694
|
+
message: 'must be empty when artifactAccess is "none"',
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
if (sourceProjection !== undefined) {
|
|
698
|
+
issues.push({
|
|
699
|
+
path: `${path}.artifactAccess`,
|
|
700
|
+
message: 'cannot be "none" when sourceProjection is declared',
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
}
|
|
582
704
|
}
|
|
583
705
|
function validateRequiredReads(value, path, sourceIds, issues) {
|
|
584
706
|
if (value === undefined)
|
package/dist/compiler.js
CHANGED
|
@@ -2,6 +2,8 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { loadAgentByName } from "./agents.js";
|
|
4
4
|
import { DYNAMIC_OUTPUT_PROFILES } from "./dynamic-profiles.js";
|
|
5
|
+
import { stringifyPromptJson } from "./prompt-json.js";
|
|
6
|
+
import { compileRole } from "./roles.js";
|
|
5
7
|
import { classifyToolCapability, effectiveToolClassification, providersForSelectedTools, resolveToolSelection, TOOL_NAME_PATTERN, toolAllowedByAuthorityCeiling, toolNameForSpec, } from "./tool-metadata.js";
|
|
6
8
|
import { WorkflowValidationError, WORKFLOW_RUN_TYPE, } from "./types.js";
|
|
7
9
|
import { resolveWorkflowRuntime, selectWorkflowRuntime, } from "./workflow-runtime.js";
|
|
@@ -101,7 +103,13 @@ function lowerArtifactGraphFrom(from) {
|
|
|
101
103
|
typeof from === "object" &&
|
|
102
104
|
!Array.isArray(from) &&
|
|
103
105
|
typeof from.source === "string") {
|
|
104
|
-
return {
|
|
106
|
+
return {
|
|
107
|
+
stage: from.source,
|
|
108
|
+
path: from.path,
|
|
109
|
+
...(from.streaming !== undefined
|
|
110
|
+
? { streaming: from.streaming }
|
|
111
|
+
: {}),
|
|
112
|
+
};
|
|
105
113
|
}
|
|
106
114
|
return from;
|
|
107
115
|
}
|
|
@@ -126,10 +134,23 @@ function appendWorkflowOutputInstructions(prompt, stage) {
|
|
|
126
134
|
: "Use schema `stage-control-v1` unless the workflow asks for a more specific control schema.",
|
|
127
135
|
"Put detailed prose, reasoning, and evidence discussion in <analysis> only.",
|
|
128
136
|
"Put structured evidence pointers in <refs> as a JSON array; use [] if none.",
|
|
137
|
+
...partialOutputInstructions(stage.output?.partial?.paths),
|
|
129
138
|
]
|
|
130
139
|
.filter(Boolean)
|
|
131
140
|
.join("\n\n");
|
|
132
141
|
}
|
|
142
|
+
function partialOutputInstructions(paths) {
|
|
143
|
+
if (!paths || paths.length === 0)
|
|
144
|
+
return [];
|
|
145
|
+
return [
|
|
146
|
+
"# Workflow Partial Output Protocol (optional)",
|
|
147
|
+
`If a complete stable array item is ready before your final answer for one of these control paths (${paths.join(", ")}), you may emit a partial-control section before the final output:`,
|
|
148
|
+
'<partial-control>{"schema":"workflow-partial-output-v1","path":"$.items","items":[{"id":"stable-id","...":"..."}]}</partial-control>',
|
|
149
|
+
"Use the actual declared path, not the example path, and include only items that are final/stable enough to appear unchanged in your final <control> at that path.",
|
|
150
|
+
"Every partial item must be the exact JSON object that will appear in the final array and must include a stable non-empty string `id`; never revise or withdraw a published partial item.",
|
|
151
|
+
"If an item might change, do not publish it partially; wait for the final workflow output. The final answer must still include the normal <control>, <analysis>, and <refs> sections exactly once.",
|
|
152
|
+
];
|
|
153
|
+
}
|
|
133
154
|
function artifactGraphTaskMetadata(stage, specDir) {
|
|
134
155
|
const controlSchema = stage.output?.controlSchema;
|
|
135
156
|
return {
|
|
@@ -143,8 +164,12 @@ function artifactGraphTaskMetadata(stage, specDir) {
|
|
|
143
164
|
? resolve(specDir, controlSchema)
|
|
144
165
|
: undefined,
|
|
145
166
|
maxDigestChars: stage.output?.maxDigestChars,
|
|
167
|
+
partial: stage.output?.partial
|
|
168
|
+
? { paths: [...stage.output.partial.paths] }
|
|
169
|
+
: undefined,
|
|
146
170
|
},
|
|
147
171
|
requiredReads: stage.inputPolicy?.requiredReads ?? [],
|
|
172
|
+
artifactAccess: stage.inputPolicy?.artifactAccess ?? "enabled",
|
|
148
173
|
sourceProjection: stage.sourceProjection,
|
|
149
174
|
};
|
|
150
175
|
}
|
|
@@ -442,14 +467,11 @@ async function compileArtifactGraphPlan(spec, options) {
|
|
|
442
467
|
return defaultAgent;
|
|
443
468
|
};
|
|
444
469
|
const roleEntries = Object.entries(spec.roles ?? {});
|
|
445
|
-
const roles = roleEntries.map(([name, role]) =>
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
truncated: false,
|
|
451
|
-
includedSections: [],
|
|
452
|
-
excludedSections: [],
|
|
470
|
+
const roles = await Promise.all(roleEntries.map(async ([name, role]) => {
|
|
471
|
+
const sourceAgent = role.fromAgent
|
|
472
|
+
? await loadWorkflowAgent(role.fromAgent, options.cwd, agentCache, `$.roles.${name}.fromAgent`)
|
|
473
|
+
: undefined;
|
|
474
|
+
return compileRole(name, role, sourceAgent);
|
|
453
475
|
}));
|
|
454
476
|
const roleText = roles.length
|
|
455
477
|
? `# Role Context\n\n${roles.map((r) => `## Role: ${r.name}\n${r.content}`).join("\n\n")}`
|
|
@@ -459,7 +481,7 @@ async function compileArtifactGraphPlan(spec, options) {
|
|
|
459
481
|
typeof workflowInput === "object" &&
|
|
460
482
|
!Array.isArray(workflowInput) &&
|
|
461
483
|
Object.keys(workflowInput).length > 0
|
|
462
|
-
? `# Workflow Input\n\n${
|
|
484
|
+
? `# Workflow Input\n\n${stringifyPromptJson(workflowInput)}`
|
|
463
485
|
: "";
|
|
464
486
|
const runtimeOverrides = options.runtimeOverrides;
|
|
465
487
|
const runtimeDefaults = options.runtimeDefaults;
|
|
@@ -540,15 +562,26 @@ async function compileArtifactGraphPlan(spec, options) {
|
|
|
540
562
|
const injectRuntimeTaskInPrompt = (runtimeStageKind !== "foreach" && injectTask) ||
|
|
541
563
|
(runtimeStageKind === "foreach" && optInInjectRuntimeTask);
|
|
542
564
|
const normalizedPrompt = String(prompt ?? "").replace(/\$\{item\}/g, "the relevant item from the dependency context");
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
565
|
+
const instructionText = `# Instructions\n\n${normalizedPrompt}`;
|
|
566
|
+
const stageText = `# Workflow Stage\n\nstage=${stage.id}\ntype=${runtimeStageKind}`;
|
|
567
|
+
const taskText = injectRuntimeTaskInPrompt && options.task
|
|
568
|
+
? `# Task\n\n${options.task}`
|
|
569
|
+
: undefined;
|
|
570
|
+
const compiledPrompt = (runtimeStageKind === "foreach"
|
|
571
|
+
? [
|
|
572
|
+
taskText,
|
|
573
|
+
workflowInputText || undefined,
|
|
574
|
+
stageText,
|
|
575
|
+
roleText || undefined,
|
|
576
|
+
instructionText,
|
|
577
|
+
]
|
|
578
|
+
: [
|
|
579
|
+
taskText,
|
|
580
|
+
workflowInputText || undefined,
|
|
581
|
+
stageText,
|
|
582
|
+
instructionText,
|
|
583
|
+
roleText || undefined,
|
|
584
|
+
])
|
|
552
585
|
.filter(Boolean)
|
|
553
586
|
.join("\n\n");
|
|
554
587
|
const toolSelection = resolveToolSelection([spec.defaults?.tools, stage.tools], stageAgent.tools);
|
|
@@ -4,6 +4,7 @@ import { DynamicControllerBudgetBlocked } from "./dynamic-controller-errors.js";
|
|
|
4
4
|
import { isDynamicOutputProfile, } from "./dynamic-profiles.js";
|
|
5
5
|
import { readOrRebuildDynamicState } from "./dynamic-state.js";
|
|
6
6
|
import { sanitizeTaskId } from "./engine-run-graph.js";
|
|
7
|
+
import { compactStrings } from "./strings.js";
|
|
7
8
|
import { fromProjectPath, isTerminalTaskStatus, readJson } from "./store.js";
|
|
8
9
|
import { classifyToolCapability, effectiveToolClassification, providersForSelectedTools, toolAllowedByAuthorityCeiling, } from "./tool-metadata.js";
|
|
9
10
|
import { resolveWorkflowRuntime, selectWorkflowRuntime, } from "./workflow-runtime.js";
|
|
@@ -173,6 +174,7 @@ export async function buildDynamicGeneratedCompiledTask(input) {
|
|
|
173
174
|
maxDigestChars: DYNAMIC_OUTPUT_MAX_DIGEST_CHARS,
|
|
174
175
|
},
|
|
175
176
|
requiredReads: input.request.requiredReads,
|
|
177
|
+
artifactAccess: "enabled",
|
|
176
178
|
sourceProjection: dynamicInputSourceProjection(input.request.inputs),
|
|
177
179
|
},
|
|
178
180
|
dynamicGenerated: {
|
|
@@ -630,5 +632,5 @@ function dynamicOutputProfileInstructions(outputProfile) {
|
|
|
630
632
|
return `# Dynamic Output Profile: ${outputProfile}\nEmit control JSON suitable for this output profile and surface gaps/blockers explicitly.`;
|
|
631
633
|
}
|
|
632
634
|
function uniqueStrings(values) {
|
|
633
|
-
return
|
|
635
|
+
return compactStrings(values, { trim: false, dropWhitespaceOnly: true });
|
|
634
636
|
}
|
|
@@ -5,4 +5,4 @@ export declare const DYNAMIC_TERMINAL_OUTPUT_PROFILES: readonly ["synthesis_v1"]
|
|
|
5
5
|
export declare function isDynamicOutputProfile(value: unknown): value is DynamicOutputProfile;
|
|
6
6
|
export declare function isExtractableDynamicOutputProfile(value: unknown): value is (typeof DYNAMIC_EXTRACTABLE_OUTPUT_PROFILES)[number];
|
|
7
7
|
export declare function isTerminalDynamicOutputProfile(value: unknown): value is (typeof DYNAMIC_TERMINAL_OUTPUT_PROFILES)[number];
|
|
8
|
-
export declare function dynamicOutputProfileValues():
|
|
8
|
+
export declare function dynamicOutputProfileValues(): DynamicOutputProfile[];
|
|
@@ -2,6 +2,7 @@ import type { CompiledTask, CompiledWorkflow, WorkflowRunRecord, WorkflowTaskRun
|
|
|
2
2
|
export declare function reconcileLoopTaskRecordsInMemory(cwd: string, run: WorkflowRunRecord, compiledFlow: CompiledWorkflow, loopIds: Set<string>): boolean;
|
|
3
3
|
export declare function recoverStaleRunningDynamicControllers(run: WorkflowRunRecord, compiledFlow: CompiledWorkflow): boolean;
|
|
4
4
|
export declare function reconcileDynamicGeneratedRunRecords(cwd: string, run: WorkflowRunRecord, compiledFlow: CompiledWorkflow): boolean;
|
|
5
|
+
export declare function reconcileForeachGeneratedRunRecords(cwd: string, run: WorkflowRunRecord, compiledFlow: CompiledWorkflow): boolean;
|
|
5
6
|
export declare function assertRunTaskPositionalAlignment(run: WorkflowRunRecord, compiledFlow: CompiledWorkflow): void;
|
|
6
7
|
export declare function assertLoopTaskPositionalAlignment(run: WorkflowRunRecord, compiledFlow: CompiledWorkflow, loopIds?: Set<string>): void;
|
|
7
8
|
export declare function upsertCompiledLoopTasksAtInsertion(compiledFlow: CompiledWorkflow, loopId: string, placeholderIndex: number, tasks: CompiledTask[]): void;
|
|
@@ -9,6 +10,8 @@ export declare function compiledTaskSpecId(task: CompiledTask): string;
|
|
|
9
10
|
export declare function loopStageIdSet(compiledFlow: CompiledWorkflow): Set<string>;
|
|
10
11
|
export declare function nextTaskRecordIndex(run: WorkflowRunRecord): number;
|
|
11
12
|
export declare function dependenciesReady(compiledTask: CompiledTask, bySpecId: Map<string, WorkflowTaskRunRecord>, compiledFlow: CompiledWorkflow): boolean;
|
|
13
|
+
export declare function foreachStreamingEnabled(compiledTask: CompiledTask): boolean;
|
|
14
|
+
export declare function foreachStreamingMinChunk(compiledTask: CompiledTask): number;
|
|
12
15
|
export declare function buildForeachGeneratedTasks(template: CompiledTask, runtimeTask: string | undefined, items: unknown[]): {
|
|
13
16
|
tasks: CompiledTask[];
|
|
14
17
|
error?: string;
|