@agwab/pi-workflow 0.1.2 → 0.2.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 +7 -13
- package/dist/compiler.d.ts +2 -0
- package/dist/compiler.js +27 -2
- package/dist/engine.d.ts +2 -0
- package/dist/engine.js +3 -2
- package/dist/extension.js +201 -16
- package/dist/store.js +1 -0
- package/dist/types.d.ts +3 -0
- package/dist/workflow-progress-health.d.ts +37 -0
- package/dist/workflow-progress-health.js +296 -0
- package/dist/workflow-runtime.d.ts +6 -0
- package/dist/workflow-runtime.js +33 -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 +1 -1
- package/package.json +6 -6
- package/src/compiler.ts +41 -2
- package/src/engine.ts +7 -16
- package/src/extension.ts +254 -22
- package/src/store.ts +1 -0
- package/src/types.ts +4 -0
- package/src/workflow-progress-health.ts +461 -0
- package/src/workflow-runtime.ts +50 -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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { appendFile, mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { appendFile, mkdir, readFile, readdir, rename, writeFile, } from "node:fs/promises";
|
|
3
3
|
import { isIP } from "node:net";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
export const WORKFLOW_WEB_SOURCE_CACHE_SCHEMA = "workflow-web-source-cache-v1";
|
|
@@ -238,7 +238,9 @@ export async function findWorkflowWebSourceByUrl(config, url) {
|
|
|
238
238
|
function sourceIndexEntryMatchesUrl(entry, url, redactedUrl, targetKey, targetDisplayKey) {
|
|
239
239
|
if (entry.urlKey)
|
|
240
240
|
return entry.urlKey === targetKey;
|
|
241
|
-
if (redactedUrlIdentityUnsafe(redactedUrl) ||
|
|
241
|
+
if (redactedUrlIdentityUnsafe(redactedUrl) ||
|
|
242
|
+
redactedUrlIdentityUnsafe(entry.redactedUrl) ||
|
|
243
|
+
redactedUrlIdentityUnsafe(entry.url)) {
|
|
242
244
|
return false;
|
|
243
245
|
}
|
|
244
246
|
return (entry.redactedUrl === redactedUrl ||
|
|
@@ -247,7 +249,8 @@ function sourceIndexEntryMatchesUrl(entry, url, redactedUrl, targetKey, targetDi
|
|
|
247
249
|
sourceUrlDisplayCacheKey(entry.url) === targetDisplayKey);
|
|
248
250
|
}
|
|
249
251
|
function redactedUrlIdentityUnsafe(url) {
|
|
250
|
-
return /REDACTED/.test(url) ||
|
|
252
|
+
return (/REDACTED/.test(url) ||
|
|
253
|
+
/[?&#][^=]*(?:token|secret|password|signature|sig|key|auth|session|credential)[^=]*=/i.test(url));
|
|
251
254
|
}
|
|
252
255
|
async function findWorkflowWebSourceByUrlFromSources(config, url, redactedUrl, targetKey, targetDisplayKey) {
|
|
253
256
|
let entries;
|
|
@@ -269,7 +272,9 @@ async function findWorkflowWebSourceByUrlFromSources(config, url, redactedUrl, t
|
|
|
269
272
|
return source;
|
|
270
273
|
continue;
|
|
271
274
|
}
|
|
272
|
-
if (redactedUrlIdentityUnsafe(redactedUrl) ||
|
|
275
|
+
if (redactedUrlIdentityUnsafe(redactedUrl) ||
|
|
276
|
+
redactedUrlIdentityUnsafe(source.redactedUrl) ||
|
|
277
|
+
redactedUrlIdentityUnsafe(source.url)) {
|
|
273
278
|
continue;
|
|
274
279
|
}
|
|
275
280
|
if (source.redactedUrl === redactedUrl ||
|
|
@@ -316,7 +321,7 @@ export function buildWorkflowWebSourceCard(options) {
|
|
|
316
321
|
};
|
|
317
322
|
}
|
|
318
323
|
export function readWorkflowWebSourceSnippet(options) {
|
|
319
|
-
return readWorkflowWebSourceSnippets({
|
|
324
|
+
return (readWorkflowWebSourceSnippets({
|
|
320
325
|
source: options.source,
|
|
321
326
|
requests: [
|
|
322
327
|
{
|
|
@@ -328,7 +333,7 @@ export function readWorkflowWebSourceSnippet(options) {
|
|
|
328
333
|
],
|
|
329
334
|
maxChars: options.maxChars,
|
|
330
335
|
budget: options.budget,
|
|
331
|
-
})[0] ?? { status: "not_found", visibleChars: 0 };
|
|
336
|
+
})[0] ?? { status: "not_found", visibleChars: 0 });
|
|
332
337
|
}
|
|
333
338
|
export function readWorkflowWebSourceSnippets(options) {
|
|
334
339
|
let normalizedSource;
|
|
@@ -491,7 +496,10 @@ function readWorkflowWebSourceSnippetWithCache(options) {
|
|
|
491
496
|
}
|
|
492
497
|
function snippetForTerms(options) {
|
|
493
498
|
const needles = options.terms
|
|
494
|
-
.map((term) => ({
|
|
499
|
+
.map((term) => ({
|
|
500
|
+
raw: term,
|
|
501
|
+
normalized: normalizeForSearch(term).normalized,
|
|
502
|
+
}))
|
|
495
503
|
.filter((term) => term.normalized.length > 0);
|
|
496
504
|
if (needles.length === 0)
|
|
497
505
|
return { status: "not_found", visibleChars: 0 };
|
|
@@ -545,7 +553,8 @@ function scoreTermWindow(text, matchStart, matchEnd, maxChars, terms) {
|
|
|
545
553
|
.filter((term) => !windowNorm.includes(term.normalized))
|
|
546
554
|
.map((term) => term.raw);
|
|
547
555
|
const occurrenceScore = terms.reduce((score, term) => {
|
|
548
|
-
return score +
|
|
556
|
+
return (score +
|
|
557
|
+
(windowNorm.includes(term.normalized) ? term.normalized.length : 0));
|
|
549
558
|
}, 0);
|
|
550
559
|
return {
|
|
551
560
|
start,
|
|
@@ -675,7 +684,8 @@ function nearbySnippet(text, needle, maxChars) {
|
|
|
675
684
|
async function readWorkflowWebSourceIndexFile(config) {
|
|
676
685
|
try {
|
|
677
686
|
const parsed = JSON.parse(await readFile(indexPath(config), "utf8"));
|
|
678
|
-
if (!isRecord(parsed) ||
|
|
687
|
+
if (!isRecord(parsed) ||
|
|
688
|
+
parsed.schema !== WORKFLOW_WEB_SOURCE_INDEX_SCHEMA) {
|
|
679
689
|
throw new Error("invalid index");
|
|
680
690
|
}
|
|
681
691
|
const sources = Array.isArray(parsed.sources)
|
|
@@ -686,7 +696,9 @@ async function readWorkflowWebSourceIndexFile(config) {
|
|
|
686
696
|
: [];
|
|
687
697
|
return {
|
|
688
698
|
schema: WORKFLOW_WEB_SOURCE_INDEX_SCHEMA,
|
|
689
|
-
updatedAt: typeof parsed.updatedAt === "string"
|
|
699
|
+
updatedAt: typeof parsed.updatedAt === "string"
|
|
700
|
+
? parsed.updatedAt
|
|
701
|
+
: new Date().toISOString(),
|
|
690
702
|
runId: typeof parsed.runId === "string" ? parsed.runId : config.runId,
|
|
691
703
|
sources: mergeSourceIndexEntries(sources),
|
|
692
704
|
};
|
|
@@ -719,7 +731,8 @@ async function readWorkflowWebSourceIndexLedger(config) {
|
|
|
719
731
|
continue;
|
|
720
732
|
try {
|
|
721
733
|
const parsed = JSON.parse(line);
|
|
722
|
-
if (!isRecord(parsed) ||
|
|
734
|
+
if (!isRecord(parsed) ||
|
|
735
|
+
parsed.schema !== WORKFLOW_WEB_SOURCE_INDEX_EVENT_SCHEMA)
|
|
723
736
|
continue;
|
|
724
737
|
const entry = sourceIndexEntryFromUnknown(parsed.entry);
|
|
725
738
|
if (entry)
|
|
@@ -734,7 +747,8 @@ async function readWorkflowWebSourceIndexLedger(config) {
|
|
|
734
747
|
function sourceIndexEntryFromUnknown(value) {
|
|
735
748
|
if (!isRecord(value))
|
|
736
749
|
return undefined;
|
|
737
|
-
if (typeof value.sourceRef !== "string" ||
|
|
750
|
+
if (typeof value.sourceRef !== "string" ||
|
|
751
|
+
!isWorkflowWebSourceRef(value.sourceRef))
|
|
738
752
|
return undefined;
|
|
739
753
|
if (typeof value.createdAt !== "string")
|
|
740
754
|
return undefined;
|
|
@@ -822,7 +836,8 @@ function nonPublicIpReason(address) {
|
|
|
822
836
|
}
|
|
823
837
|
if (isIP(lower) === 4) {
|
|
824
838
|
const parts = lower.split(".").map((part) => Number(part));
|
|
825
|
-
if (parts.length !== 4 ||
|
|
839
|
+
if (parts.length !== 4 ||
|
|
840
|
+
parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255))
|
|
826
841
|
return "non_public_ip_blocked";
|
|
827
842
|
const [a, b, c, d] = parts;
|
|
828
843
|
if (a === 0 || a === 10 || a === 127 || a >= 224)
|
|
@@ -859,7 +874,10 @@ function nonPublicIpReason(address) {
|
|
|
859
874
|
return undefined;
|
|
860
875
|
}
|
|
861
876
|
function redactRecordForModel(value) {
|
|
862
|
-
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
877
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
878
|
+
key,
|
|
879
|
+
redactValueForModel(item),
|
|
880
|
+
]));
|
|
863
881
|
}
|
|
864
882
|
function redactValueForModel(value) {
|
|
865
883
|
if (typeof value === "string")
|
package/docs/usage.md
CHANGED
|
@@ -211,7 +211,7 @@ Benchmark note: cache-enabled runs are a distinct cohort from older uncached run
|
|
|
211
211
|
|
|
212
212
|
| Workflow | Required agents | Mode | Use when |
|
|
213
213
|
|---|---|---|---|
|
|
214
|
-
| `deep-research` | `researcher` | plan + foreach questions + normalize-input packet support + normalize + foreach verifier + audit support + final-audit packet support +
|
|
214
|
+
| `deep-research` | `researcher` | plan + foreach questions + normalize-input packet support + normalize + foreach verifier + audit support + final-audit packet support + compact final synthesis reduce + deterministic ledger-backed executive render | Use when you need a grounded answer or summary based on source material. |
|
|
215
215
|
| `deep-review` | `scout` | triage + foreach review lenses + dedup support + foreach devil's advocate + verdict-partition support + reduce | Use when you want code or design reviewed carefully from multiple angles. |
|
|
216
216
|
| `spec-review` | `scout` | extract spec + map implementation + inspect tests -> reduce candidates -> foreach verifier -> reduce report | Use when you want to check whether requirements, an API spec, or a contract are reflected in the implementation and tests. |
|
|
217
217
|
| `impact-review` | `scout` | scope/implementation/validation maps -> impact lenses -> consistency/regression/ship-readiness joins -> final synthesis | Use before merging or releasing a change to check affected areas, risks, missing tests, and missing docs. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agwab/pi-workflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Workflow orchestration for Pi subagents.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "rm -rf dist && tsc -p tsconfig.json --outDir dist --noEmit false",
|
|
45
45
|
"typecheck": "tsc --noEmit",
|
|
46
|
-
"check:scripts": "node
|
|
46
|
+
"check:scripts": "node tools/release/check-scripts.mjs",
|
|
47
47
|
"validate": "npm run check:scripts && npm run typecheck && npm run test:unit",
|
|
48
48
|
"test": "npm run test:unit",
|
|
49
49
|
"test:build": "rm -rf .tmp/unit && tsc -p tsconfig.json --outDir .tmp/unit --noEmit false",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"e2e": "node test/e2e/run.mjs",
|
|
52
52
|
"pack:dry": "npm pack --dry-run --json",
|
|
53
53
|
"prepack": "npm run build",
|
|
54
|
-
"release:check": "node
|
|
55
|
-
"release:dispatch": "node
|
|
54
|
+
"release:check": "node tools/release/release-check.mjs",
|
|
55
|
+
"release:dispatch": "node tools/release/dispatch-release.mjs"
|
|
56
56
|
},
|
|
57
57
|
"pi": {
|
|
58
58
|
"extensions": [
|
|
@@ -70,7 +70,6 @@
|
|
|
70
70
|
"@earendil-works/pi-ai": "^0.78.0",
|
|
71
71
|
"@earendil-works/pi-coding-agent": "*",
|
|
72
72
|
"@types/node": "^24.0.0",
|
|
73
|
-
"typebox": "^1.1.39",
|
|
74
73
|
"typescript": "^5.0.0"
|
|
75
74
|
},
|
|
76
75
|
"engines": {
|
|
@@ -78,7 +77,8 @@
|
|
|
78
77
|
},
|
|
79
78
|
"dependencies": {
|
|
80
79
|
"@agwab/pi-subagent": "^0.3.6",
|
|
81
|
-
"pi-web-access": "^0.10.7"
|
|
80
|
+
"pi-web-access": "^0.10.7",
|
|
81
|
+
"typebox": "^1.1.39"
|
|
82
82
|
},
|
|
83
83
|
"publishConfig": {
|
|
84
84
|
"access": "public"
|
package/src/compiler.ts
CHANGED
|
@@ -31,6 +31,10 @@ import {
|
|
|
31
31
|
type WorkflowToolSpec,
|
|
32
32
|
type WorktreePolicy,
|
|
33
33
|
} from "./types.js";
|
|
34
|
+
import {
|
|
35
|
+
resolveWorkflowRuntime,
|
|
36
|
+
type WorkflowModelInfo,
|
|
37
|
+
} from "./workflow-runtime.js";
|
|
34
38
|
|
|
35
39
|
const DELEGATION_TOOLS = new Set([
|
|
36
40
|
"skill_test_subagent",
|
|
@@ -57,6 +61,7 @@ const DEFAULT_DYNAMIC_DECISION_LOOP_MAX_STALLS = 3;
|
|
|
57
61
|
interface CompileOptions {
|
|
58
62
|
cwd: string;
|
|
59
63
|
specPath?: string;
|
|
64
|
+
availableModels?: WorkflowModelInfo[];
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
interface ArtifactGraphCompilePlanBuildResult {
|
|
@@ -728,6 +733,20 @@ async function compileArtifactGraphPlan(
|
|
|
728
733
|
defaultThinking,
|
|
729
734
|
overrides,
|
|
730
735
|
);
|
|
736
|
+
const resolvedDynamicRuntime = await resolveWorkflowRuntime(
|
|
737
|
+
{ model: defaultModel, thinking: defaultThinking },
|
|
738
|
+
{
|
|
739
|
+
taskKey: key,
|
|
740
|
+
stageId: stage.id,
|
|
741
|
+
taskId,
|
|
742
|
+
agent: "dynamic",
|
|
743
|
+
},
|
|
744
|
+
{ availableModels: options.availableModels },
|
|
745
|
+
);
|
|
746
|
+
dynamicTask.runtime = {
|
|
747
|
+
...dynamicTask.runtime,
|
|
748
|
+
...resolvedDynamicRuntime,
|
|
749
|
+
};
|
|
731
750
|
if (dynamicToolSelection.tools || dynamicToolSelection.toolProviders) {
|
|
732
751
|
dynamicTask.runtime = {
|
|
733
752
|
...dynamicTask.runtime,
|
|
@@ -804,11 +823,31 @@ async function compileArtifactGraphPlan(
|
|
|
804
823
|
validateToolSubset(toolSelection.tools, stageAgent, issues, toolPath);
|
|
805
824
|
validateDelegationBoundary(toolSelection.tools, issues, toolPath);
|
|
806
825
|
const filteredToolSelection = filterToolSelection(toolSelection);
|
|
826
|
+
// Explicit runtime overrides outrank stage pins; spec defaults fill last.
|
|
827
|
+
const requestedRuntime = {
|
|
828
|
+
model:
|
|
829
|
+
options.runtimeDefaults?.model ?? stage.model ?? spec.defaults?.model,
|
|
830
|
+
thinking:
|
|
831
|
+
options.runtimeDefaults?.thinking ??
|
|
832
|
+
stage.thinking ??
|
|
833
|
+
spec.defaults?.thinking,
|
|
834
|
+
};
|
|
835
|
+
const resolvedRuntime = await resolveWorkflowRuntime(
|
|
836
|
+
requestedRuntime,
|
|
837
|
+
{
|
|
838
|
+
taskKey: key,
|
|
839
|
+
stageId: stage.id,
|
|
840
|
+
taskId,
|
|
841
|
+
agent: stageAgentName,
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
availableModels: options.availableModels,
|
|
845
|
+
},
|
|
846
|
+
);
|
|
807
847
|
const runtime = {
|
|
808
848
|
approvalMode:
|
|
809
849
|
stage.approvalMode ?? spec.defaults?.approvalMode ?? "non-interactive",
|
|
810
|
-
|
|
811
|
-
thinking: stage.thinking ?? defaultThinking,
|
|
850
|
+
...resolvedRuntime,
|
|
812
851
|
tools: filteredToolSelection.tools,
|
|
813
852
|
...(filteredToolSelection.toolProviders
|
|
814
853
|
? { toolProviders: filteredToolSelection.toolProviders }
|
package/src/engine.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import {
|
|
3
|
-
dirname,
|
|
4
|
-
extname,
|
|
5
|
-
join,
|
|
6
|
-
relative,
|
|
7
|
-
resolve,
|
|
8
|
-
} from "node:path";
|
|
2
|
+
import { dirname, extname, join, relative, resolve } from "node:path";
|
|
9
3
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
10
4
|
import { Worker } from "node:worker_threads";
|
|
11
5
|
|
|
@@ -43,7 +37,10 @@ import {
|
|
|
43
37
|
workflowBundleFingerprint,
|
|
44
38
|
workflowBundleSpecPath,
|
|
45
39
|
} from "./workflow-source-context-runtime.js";
|
|
46
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
readSimpleJsonPath,
|
|
42
|
+
type WorkflowModelInfo,
|
|
43
|
+
} from "./workflow-runtime.js";
|
|
47
44
|
import {
|
|
48
45
|
dynamicRunDir,
|
|
49
46
|
hashDynamicRequest,
|
|
@@ -158,6 +155,7 @@ const supervisorTimers = new Map<string, ReturnType<typeof setInterval>>();
|
|
|
158
155
|
export interface WorkflowRunOptions {
|
|
159
156
|
task?: string;
|
|
160
157
|
runtimeDefaults?: { model?: string; thinking?: ThinkingLevel };
|
|
158
|
+
availableModels?: WorkflowModelInfo[];
|
|
161
159
|
dynamicUi?: DynamicWorkflowUi;
|
|
162
160
|
runId?: string;
|
|
163
161
|
parentRunId?: string;
|
|
@@ -210,6 +208,7 @@ async function runLoadedWorkflowSpec(
|
|
|
210
208
|
specPath,
|
|
211
209
|
task: options.task,
|
|
212
210
|
runtimeDefaults: options.runtimeDefaults,
|
|
211
|
+
availableModels: options.availableModels,
|
|
213
212
|
});
|
|
214
213
|
|
|
215
214
|
const { run } = await createRunRecord(cwd, compiled, specPath, {
|
|
@@ -861,7 +860,6 @@ async function extractArtifactGraphForeachItems(
|
|
|
861
860
|
return { items };
|
|
862
861
|
}
|
|
863
862
|
|
|
864
|
-
|
|
865
863
|
async function launchPendingTaskAt(
|
|
866
864
|
cwd: string,
|
|
867
865
|
run: WorkflowRunRecord,
|
|
@@ -1801,7 +1799,6 @@ function isDynamicReplayInvariantError(error: unknown): boolean {
|
|
|
1801
1799
|
);
|
|
1802
1800
|
}
|
|
1803
1801
|
|
|
1804
|
-
|
|
1805
1802
|
function requiredDynamicString(
|
|
1806
1803
|
value: unknown,
|
|
1807
1804
|
field: string,
|
|
@@ -2534,9 +2531,6 @@ async function runDynamicAgentRequest(input: {
|
|
|
2534
2531
|
);
|
|
2535
2532
|
}
|
|
2536
2533
|
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
2534
|
interface DynamicControllerOutcome {
|
|
2541
2535
|
taskStatus: "completed" | "blocked" | "failed";
|
|
2542
2536
|
statusDetail: string;
|
|
@@ -2676,7 +2670,6 @@ function dynamicControllerIssueMessage(
|
|
|
2676
2670
|
return `${prefix}: ${first}${suffix}`;
|
|
2677
2671
|
}
|
|
2678
2672
|
|
|
2679
|
-
|
|
2680
2673
|
function applyExistingLoopWorktree(
|
|
2681
2674
|
run: WorkflowRunRecord,
|
|
2682
2675
|
task: WorkflowTaskRunRecord,
|
|
@@ -2724,12 +2717,10 @@ function recordCreatedLoopWorktree(
|
|
|
2724
2717
|
else run.loopWorktrees[index] = record;
|
|
2725
2718
|
}
|
|
2726
2719
|
|
|
2727
|
-
|
|
2728
2720
|
function uniqueStrings(values: readonly string[]): string[] {
|
|
2729
2721
|
return [...new Set(values.filter((value) => value.trim().length > 0))];
|
|
2730
2722
|
}
|
|
2731
2723
|
|
|
2732
|
-
|
|
2733
2724
|
async function readCompiledWorkflow(
|
|
2734
2725
|
cwd: string,
|
|
2735
2726
|
runId: string,
|