@quintinshaw/pi-dynamic-workflows 1.0.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 +159 -0
- package/dist/adversarial-review.d.ts +20 -0
- package/dist/adversarial-review.js +87 -0
- package/dist/agent.d.ts +29 -0
- package/dist/agent.js +90 -0
- package/dist/auto-workflow.d.ts +26 -0
- package/dist/auto-workflow.js +121 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +17 -0
- package/dist/deep-research.d.ts +22 -0
- package/dist/deep-research.js +110 -0
- package/dist/display.d.ts +62 -0
- package/dist/display.js +163 -0
- package/dist/errors.d.ts +41 -0
- package/dist/errors.js +63 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +15 -0
- package/dist/logger.d.ts +21 -0
- package/dist/logger.js +67 -0
- package/dist/model-routing.d.ts +33 -0
- package/dist/model-routing.js +57 -0
- package/dist/run-persistence.d.ts +53 -0
- package/dist/run-persistence.js +78 -0
- package/dist/structured-output.d.ts +19 -0
- package/dist/structured-output.js +30 -0
- package/dist/workflow-manager.d.ts +74 -0
- package/dist/workflow-manager.js +241 -0
- package/dist/workflow-saved.d.ts +35 -0
- package/dist/workflow-saved.js +91 -0
- package/dist/workflow-tool.d.ts +22 -0
- package/dist/workflow-tool.js +216 -0
- package/dist/workflow.d.ts +75 -0
- package/dist/workflow.js +364 -0
- package/extensions/workflow.ts +14 -0
- package/package.json +70 -0
- package/src/adversarial-review.ts +107 -0
- package/src/agent.ts +135 -0
- package/src/auto-workflow.ts +146 -0
- package/src/config.ts +24 -0
- package/src/deep-research.ts +128 -0
- package/src/display.ts +236 -0
- package/src/errors.ts +85 -0
- package/src/index.ts +55 -0
- package/src/logger.ts +89 -0
- package/src/model-routing.ts +80 -0
- package/src/run-persistence.ts +132 -0
- package/src/structured-output.ts +47 -0
- package/src/workflow-manager.ts +294 -0
- package/src/workflow-saved.ts +131 -0
- package/src/workflow-tool.ts +254 -0
- package/src/workflow.ts +492 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep research workflow.
|
|
3
|
+
* Built-in workflow for comprehensive research across multiple sources.
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_CONFIG = {
|
|
6
|
+
searchAngles: 4,
|
|
7
|
+
sourcesPerAngle: 3,
|
|
8
|
+
crossCheck: true,
|
|
9
|
+
maxAgents: 20,
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Generate a deep research workflow script.
|
|
13
|
+
*/
|
|
14
|
+
export function generateDeepResearchWorkflow(question, config = {}) {
|
|
15
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
16
|
+
const escapedQuestion = question.replace(/'/g, "\\'").slice(0, 80);
|
|
17
|
+
const crossCheckPhase = cfg.crossCheck
|
|
18
|
+
? `phase('Cross-Check');
|
|
19
|
+
const crossCheck = await agent(
|
|
20
|
+
'Cross-check these research findings. Identify claims that are supported by multiple sources vs. claims that appear in only one source:\\n' +
|
|
21
|
+
'Sources: ' + JSON.stringify(sources),
|
|
22
|
+
{ label: 'cross-checker' }
|
|
23
|
+
);`
|
|
24
|
+
: "";
|
|
25
|
+
const crossCheckRef = cfg.crossCheck ? "'Cross-check: ' + crossCheck + '\\n' + " : "";
|
|
26
|
+
const crossCheckReturn = cfg.crossCheck ? "crossCheck, " : "";
|
|
27
|
+
return `export const meta = {
|
|
28
|
+
name: 'deep_research',
|
|
29
|
+
description: 'Deep research: ${escapedQuestion}',
|
|
30
|
+
phases: [
|
|
31
|
+
{ title: 'Search Planning' },
|
|
32
|
+
{ title: 'Source Gathering' },
|
|
33
|
+
{ title: 'Cross-Check' },
|
|
34
|
+
{ title: 'Report' },
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
phase('Search Planning');
|
|
39
|
+
const question = '${escapedQuestion}';
|
|
40
|
+
const searchPlan = await agent(
|
|
41
|
+
'Plan ${cfg.searchAngles} different search angles to research this question comprehensively: ' + question,
|
|
42
|
+
{ label: 'search-planner' }
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
phase('Source Gathering');
|
|
46
|
+
const sources = await parallel(Array.from({ length: ${cfg.searchAngles} }, (_, i) => () =>
|
|
47
|
+
agent(
|
|
48
|
+
'Research angle ' + (i + 1) + ' for this question: ' + question + '\\n\\nPlan: ' + searchPlan + '\\n\\nFind and summarize ${cfg.sourcesPerAngle} relevant sources.',
|
|
49
|
+
{ label: 'researcher-' + (i + 1) }
|
|
50
|
+
)
|
|
51
|
+
));
|
|
52
|
+
|
|
53
|
+
${crossCheckPhase}
|
|
54
|
+
|
|
55
|
+
phase('Report');
|
|
56
|
+
const report = await agent(
|
|
57
|
+
'Synthesize a comprehensive research report from these findings:\\n' +
|
|
58
|
+
'Question: ' + question + '\\n' +
|
|
59
|
+
'Sources: ' + JSON.stringify(sources) + '\\n' +
|
|
60
|
+
${crossCheckRef}'\\n\\nProduce a well-structured report with citations and confidence levels.',
|
|
61
|
+
{ label: 'report-writer' }
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
return { searchPlan, sources, ${crossCheckReturn}report };`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Generate a codebase audit workflow.
|
|
68
|
+
*/
|
|
69
|
+
export function generateCodebaseAuditWorkflow(scope, checks) {
|
|
70
|
+
const escapedScope = scope.replace(/'/g, "\\'").slice(0, 60);
|
|
71
|
+
const checkAgents = checks
|
|
72
|
+
.map((check) => {
|
|
73
|
+
const label = check
|
|
74
|
+
.toLowerCase()
|
|
75
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
76
|
+
.slice(0, 20);
|
|
77
|
+
return ` () => agent('Audit ${check} across: ' + scope, { label: '${label}' }),`;
|
|
78
|
+
})
|
|
79
|
+
.join("\n");
|
|
80
|
+
return `export const meta = {
|
|
81
|
+
name: 'codebase_audit',
|
|
82
|
+
description: 'Codebase audit: ${escapedScope}',
|
|
83
|
+
phases: [
|
|
84
|
+
{ title: 'Individual Checks' },
|
|
85
|
+
{ title: 'Cross-Validation' },
|
|
86
|
+
{ title: 'Report' },
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
phase('Individual Checks');
|
|
91
|
+
const scope = '${escapedScope}';
|
|
92
|
+
const findings = await parallel([
|
|
93
|
+
${checkAgents}
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
phase('Cross-Validation');
|
|
97
|
+
const validated = await agent(
|
|
98
|
+
'Cross-validate these audit findings. Remove false positives and confirm real issues:\\n' +
|
|
99
|
+
JSON.stringify(findings),
|
|
100
|
+
{ label: 'validator' }
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
phase('Report');
|
|
104
|
+
const report = await agent(
|
|
105
|
+
'Generate a prioritized audit report with actionable recommendations:\\n' + validated,
|
|
106
|
+
{ label: 'report-writer' }
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
return { findings, validated, report };`;
|
|
110
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { WorkflowMeta } from "./workflow.js";
|
|
3
|
+
export type WorkflowAgentStatus = "queued" | "running" | "done" | "error" | "skipped";
|
|
4
|
+
export interface WorkflowAgentSnapshot {
|
|
5
|
+
id: number;
|
|
6
|
+
label: string;
|
|
7
|
+
phase?: string;
|
|
8
|
+
prompt: string;
|
|
9
|
+
status: WorkflowAgentStatus;
|
|
10
|
+
resultPreview?: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
/** Tokens used by this agent. */
|
|
13
|
+
tokens?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface WorkflowSnapshot {
|
|
16
|
+
name: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
phases: string[];
|
|
19
|
+
currentPhase?: string;
|
|
20
|
+
logs: string[];
|
|
21
|
+
agents: WorkflowAgentSnapshot[];
|
|
22
|
+
agentCount: number;
|
|
23
|
+
runningCount: number;
|
|
24
|
+
doneCount: number;
|
|
25
|
+
errorCount: number;
|
|
26
|
+
durationMs?: number;
|
|
27
|
+
result?: unknown;
|
|
28
|
+
tokenUsage?: {
|
|
29
|
+
input: number;
|
|
30
|
+
output: number;
|
|
31
|
+
total: number;
|
|
32
|
+
};
|
|
33
|
+
runId?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface WorkflowDisplay {
|
|
36
|
+
update(snapshot: WorkflowSnapshot): void;
|
|
37
|
+
complete(snapshot: WorkflowSnapshot): void;
|
|
38
|
+
clear(): void;
|
|
39
|
+
}
|
|
40
|
+
export interface WorkflowDisplayOptions {
|
|
41
|
+
key?: string;
|
|
42
|
+
placement?: "aboveEditor" | "belowEditor";
|
|
43
|
+
maxAgents?: number;
|
|
44
|
+
maxLogs?: number;
|
|
45
|
+
showStatus?: boolean;
|
|
46
|
+
showResultPreviews?: boolean;
|
|
47
|
+
}
|
|
48
|
+
export declare function createWorkflowSnapshot(meta: WorkflowMeta): WorkflowSnapshot;
|
|
49
|
+
export declare function recomputeWorkflowSnapshot(snapshot: WorkflowSnapshot): WorkflowSnapshot;
|
|
50
|
+
export declare function createWidgetWorkflowDisplay(ctx: Pick<ExtensionContext, "ui" | "hasUI">, options?: WorkflowDisplayOptions): WorkflowDisplay;
|
|
51
|
+
export declare function createToolUpdateWorkflowDisplay(onUpdate: ((result: {
|
|
52
|
+
content: Array<{
|
|
53
|
+
type: "text";
|
|
54
|
+
text: string;
|
|
55
|
+
}>;
|
|
56
|
+
details: unknown;
|
|
57
|
+
}) => void) | undefined, ctx?: Pick<ExtensionContext, "ui" | "hasUI">, options?: WorkflowDisplayOptions & {
|
|
58
|
+
streamToolUpdates?: boolean;
|
|
59
|
+
}): WorkflowDisplay;
|
|
60
|
+
export declare function renderWorkflowLines(snapshot: WorkflowSnapshot, options?: WorkflowDisplayOptions): string[];
|
|
61
|
+
export declare function renderWorkflowText(snapshot: WorkflowSnapshot, completed?: boolean): string;
|
|
62
|
+
export declare function preview(value: unknown, max?: number): string;
|
package/dist/display.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
export function createWorkflowSnapshot(meta) {
|
|
2
|
+
return {
|
|
3
|
+
name: meta.name,
|
|
4
|
+
description: meta.description,
|
|
5
|
+
phases: meta.phases?.map((phase) => phase.title) ?? [],
|
|
6
|
+
logs: [],
|
|
7
|
+
agents: [],
|
|
8
|
+
agentCount: 0,
|
|
9
|
+
runningCount: 0,
|
|
10
|
+
doneCount: 0,
|
|
11
|
+
errorCount: 0,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function recomputeWorkflowSnapshot(snapshot) {
|
|
15
|
+
const runningCount = snapshot.agents.filter((agent) => agent.status === "running").length;
|
|
16
|
+
const doneCount = snapshot.agents.filter((agent) => agent.status === "done").length;
|
|
17
|
+
const errorCount = snapshot.agents.filter((agent) => agent.status === "error").length;
|
|
18
|
+
return { ...snapshot, agentCount: snapshot.agents.length, runningCount, doneCount, errorCount };
|
|
19
|
+
}
|
|
20
|
+
export function createWidgetWorkflowDisplay(ctx, options = {}) {
|
|
21
|
+
const key = options.key ?? "workflow";
|
|
22
|
+
const placement = options.placement ?? "belowEditor";
|
|
23
|
+
const showStatus = options.showStatus ?? false;
|
|
24
|
+
const render = (snapshot, completed = false) => {
|
|
25
|
+
if (!ctx.hasUI)
|
|
26
|
+
return;
|
|
27
|
+
if (showStatus)
|
|
28
|
+
ctx.ui.setStatus(key, statusLine(snapshot, completed));
|
|
29
|
+
ctx.ui.setWidget(key, renderWorkflowLines(snapshot, options), { placement });
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
update(snapshot) {
|
|
33
|
+
render(snapshot, false);
|
|
34
|
+
},
|
|
35
|
+
complete(snapshot) {
|
|
36
|
+
render(snapshot, true);
|
|
37
|
+
},
|
|
38
|
+
clear() {
|
|
39
|
+
if (!ctx.hasUI)
|
|
40
|
+
return;
|
|
41
|
+
if (showStatus)
|
|
42
|
+
ctx.ui.setStatus(key, undefined);
|
|
43
|
+
ctx.ui.setWidget(key, undefined);
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function createToolUpdateWorkflowDisplay(onUpdate, ctx, options = {}) {
|
|
48
|
+
const widget = ctx ? createWidgetWorkflowDisplay(ctx, options) : undefined;
|
|
49
|
+
const streamToolUpdates = options.streamToolUpdates ?? !ctx?.hasUI;
|
|
50
|
+
const emit = (snapshot, completed = false) => {
|
|
51
|
+
if (streamToolUpdates) {
|
|
52
|
+
onUpdate?.({
|
|
53
|
+
content: [{ type: "text", text: renderWorkflowText(snapshot, completed) }],
|
|
54
|
+
details: snapshot,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (completed)
|
|
58
|
+
widget?.complete(snapshot);
|
|
59
|
+
else
|
|
60
|
+
widget?.update(snapshot);
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
update(snapshot) {
|
|
64
|
+
emit(snapshot, false);
|
|
65
|
+
},
|
|
66
|
+
complete(snapshot) {
|
|
67
|
+
emit(snapshot, true);
|
|
68
|
+
},
|
|
69
|
+
clear() {
|
|
70
|
+
widget?.clear();
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function renderWorkflowLines(snapshot, options = {}) {
|
|
75
|
+
const maxAgents = options.maxAgents ?? 8;
|
|
76
|
+
const maxLogs = options.maxLogs ?? 2;
|
|
77
|
+
const showResultPreviews = options.showResultPreviews ?? false;
|
|
78
|
+
const state = snapshot.errorCount > 0
|
|
79
|
+
? `, ${snapshot.errorCount} errors`
|
|
80
|
+
: snapshot.runningCount > 0
|
|
81
|
+
? `, ${snapshot.runningCount} running`
|
|
82
|
+
: "";
|
|
83
|
+
// Build header with token info
|
|
84
|
+
const tokenInfo = snapshot.tokenUsage ? ` · ${snapshot.tokenUsage.total.toLocaleString()} tokens` : "";
|
|
85
|
+
const lines = [
|
|
86
|
+
`◆ Workflow: ${snapshot.name} (${snapshot.doneCount}/${snapshot.agentCount} done${state}${tokenInfo})`,
|
|
87
|
+
];
|
|
88
|
+
const phaseNames = snapshot.phases.length
|
|
89
|
+
? snapshot.phases
|
|
90
|
+
: unique(snapshot.agents.map((agent) => agent.phase).filter(Boolean));
|
|
91
|
+
const rendered = new Set();
|
|
92
|
+
for (const phase of phaseNames) {
|
|
93
|
+
const agents = snapshot.agents.filter((agent) => agent.phase === phase);
|
|
94
|
+
for (const agent of agents)
|
|
95
|
+
rendered.add(agent);
|
|
96
|
+
const done = agents.filter((agent) => agent.status === "done").length;
|
|
97
|
+
const running = agents.filter((agent) => agent.status === "running").length;
|
|
98
|
+
const errors = agents.filter((agent) => agent.status === "error").length;
|
|
99
|
+
const skipped = agents.filter((agent) => agent.status === "skipped").length;
|
|
100
|
+
const complete = agents.length > 0 && done + errors + skipped === agents.length;
|
|
101
|
+
const marker = running > 0 || (!complete && snapshot.currentPhase === phase) ? "▶" : complete ? "✓" : " ";
|
|
102
|
+
lines.push(` ${marker} ${phase} ${done}/${agents.length}${running ? ` · ${running} running` : ""}${errors ? ` · ${errors} errors` : ""}${skipped ? ` · ${skipped} skipped` : ""}`);
|
|
103
|
+
const visibleAgents = agents.slice(-maxAgents);
|
|
104
|
+
for (const agent of visibleAgents) {
|
|
105
|
+
const order = `#${agent.id}`;
|
|
106
|
+
const result = showResultPreviews && agent.resultPreview ? ` — ${agent.resultPreview}` : "";
|
|
107
|
+
const agentTokens = agent.tokens ? ` [${agent.tokens.toLocaleString()} tok]` : "";
|
|
108
|
+
lines.push(` ${order} ${statusIcon(agent.status)} ${shorten(agent.label, 48)}${agentTokens}${result}`);
|
|
109
|
+
}
|
|
110
|
+
if (agents.length > visibleAgents.length)
|
|
111
|
+
lines.push(` … ${agents.length - visibleAgents.length} earlier agents`);
|
|
112
|
+
}
|
|
113
|
+
const unphased = snapshot.agents.filter((agent) => !rendered.has(agent));
|
|
114
|
+
if (unphased.length) {
|
|
115
|
+
lines.push(" Unphased");
|
|
116
|
+
for (const agent of unphased.slice(-maxAgents)) {
|
|
117
|
+
const result = showResultPreviews && agent.resultPreview ? ` — ${agent.resultPreview}` : "";
|
|
118
|
+
const agentTokens = agent.tokens ? ` [${agent.tokens.toLocaleString()} tok]` : "";
|
|
119
|
+
lines.push(` #${agent.id} ${statusIcon(agent.status)} ${shorten(agent.label, 48)}${agentTokens}${result}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (const log of snapshot.logs.slice(-maxLogs))
|
|
123
|
+
lines.push(` log: ${log}`);
|
|
124
|
+
return lines;
|
|
125
|
+
}
|
|
126
|
+
export function renderWorkflowText(snapshot, completed = false) {
|
|
127
|
+
const header = completed ? "Workflow completed" : "Workflow running";
|
|
128
|
+
return [header, ...renderWorkflowLines(snapshot)].join("\n");
|
|
129
|
+
}
|
|
130
|
+
function statusLine(snapshot, completed) {
|
|
131
|
+
if (completed)
|
|
132
|
+
return `workflow ✓ ${snapshot.name}: ${snapshot.doneCount}/${snapshot.agentCount}`;
|
|
133
|
+
if (snapshot.runningCount > 0)
|
|
134
|
+
return `workflow ${snapshot.name}: ${snapshot.runningCount} running, ${snapshot.doneCount}/${snapshot.agentCount} done`;
|
|
135
|
+
return `workflow ${snapshot.name}: ${snapshot.doneCount}/${snapshot.agentCount} done`;
|
|
136
|
+
}
|
|
137
|
+
function statusIcon(status) {
|
|
138
|
+
switch (status) {
|
|
139
|
+
case "queued":
|
|
140
|
+
return "○";
|
|
141
|
+
case "running":
|
|
142
|
+
return "●";
|
|
143
|
+
case "done":
|
|
144
|
+
return "✓";
|
|
145
|
+
case "error":
|
|
146
|
+
return "✗";
|
|
147
|
+
case "skipped":
|
|
148
|
+
return "-";
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function unique(values) {
|
|
152
|
+
return [...new Set(values)];
|
|
153
|
+
}
|
|
154
|
+
function shorten(value, max) {
|
|
155
|
+
const text = value.replace(/\s+/g, " ").trim();
|
|
156
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
157
|
+
}
|
|
158
|
+
export function preview(value, max = 80) {
|
|
159
|
+
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
160
|
+
if (!text)
|
|
161
|
+
return "";
|
|
162
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
163
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-specific error types.
|
|
3
|
+
*/
|
|
4
|
+
export declare enum WorkflowErrorCode {
|
|
5
|
+
/** Agent exceeded timeout. */
|
|
6
|
+
AGENT_TIMEOUT = "AGENT_TIMEOUT",
|
|
7
|
+
/** Workflow was aborted by user. */
|
|
8
|
+
WORKFLOW_ABORTED = "WORKFLOW_ABORTED",
|
|
9
|
+
/** Agent limit exceeded. */
|
|
10
|
+
AGENT_LIMIT_EXCEEDED = "AGENT_LIMIT_EXCEEDED",
|
|
11
|
+
/** Token budget exhausted. */
|
|
12
|
+
TOKEN_BUDGET_EXHAUSTED = "TOKEN_BUDGET_EXHAUSTED",
|
|
13
|
+
/** Script validation failed. */
|
|
14
|
+
SCRIPT_VALIDATION_ERROR = "SCRIPT_VALIDATION_ERROR",
|
|
15
|
+
/** Agent execution failed. */
|
|
16
|
+
AGENT_EXECUTION_ERROR = "AGENT_EXECUTION_ERROR",
|
|
17
|
+
/** Run state persistence failed. */
|
|
18
|
+
PERSISTENCE_ERROR = "PERSISTENCE_ERROR",
|
|
19
|
+
/** Unknown error. */
|
|
20
|
+
UNKNOWN = "UNKNOWN"
|
|
21
|
+
}
|
|
22
|
+
export declare class WorkflowError extends Error {
|
|
23
|
+
readonly code: WorkflowErrorCode;
|
|
24
|
+
readonly recoverable: boolean;
|
|
25
|
+
readonly agentLabel?: string;
|
|
26
|
+
readonly details?: unknown;
|
|
27
|
+
constructor(message: string, code: WorkflowErrorCode, options?: {
|
|
28
|
+
recoverable?: boolean;
|
|
29
|
+
agentLabel?: string;
|
|
30
|
+
details?: unknown;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export declare function isWorkflowError(error: unknown): error is WorkflowError;
|
|
34
|
+
export declare function isAbortError(error: unknown): boolean;
|
|
35
|
+
export declare function isTimeoutError(error: unknown): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Wrap an unknown error into a WorkflowError with appropriate classification.
|
|
38
|
+
*/
|
|
39
|
+
export declare function wrapError(error: unknown, context?: {
|
|
40
|
+
agentLabel?: string;
|
|
41
|
+
}): WorkflowError;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-specific error types.
|
|
3
|
+
*/
|
|
4
|
+
export var WorkflowErrorCode;
|
|
5
|
+
(function (WorkflowErrorCode) {
|
|
6
|
+
/** Agent exceeded timeout. */
|
|
7
|
+
WorkflowErrorCode["AGENT_TIMEOUT"] = "AGENT_TIMEOUT";
|
|
8
|
+
/** Workflow was aborted by user. */
|
|
9
|
+
WorkflowErrorCode["WORKFLOW_ABORTED"] = "WORKFLOW_ABORTED";
|
|
10
|
+
/** Agent limit exceeded. */
|
|
11
|
+
WorkflowErrorCode["AGENT_LIMIT_EXCEEDED"] = "AGENT_LIMIT_EXCEEDED";
|
|
12
|
+
/** Token budget exhausted. */
|
|
13
|
+
WorkflowErrorCode["TOKEN_BUDGET_EXHAUSTED"] = "TOKEN_BUDGET_EXHAUSTED";
|
|
14
|
+
/** Script validation failed. */
|
|
15
|
+
WorkflowErrorCode["SCRIPT_VALIDATION_ERROR"] = "SCRIPT_VALIDATION_ERROR";
|
|
16
|
+
/** Agent execution failed. */
|
|
17
|
+
WorkflowErrorCode["AGENT_EXECUTION_ERROR"] = "AGENT_EXECUTION_ERROR";
|
|
18
|
+
/** Run state persistence failed. */
|
|
19
|
+
WorkflowErrorCode["PERSISTENCE_ERROR"] = "PERSISTENCE_ERROR";
|
|
20
|
+
/** Unknown error. */
|
|
21
|
+
WorkflowErrorCode["UNKNOWN"] = "UNKNOWN";
|
|
22
|
+
})(WorkflowErrorCode || (WorkflowErrorCode = {}));
|
|
23
|
+
export class WorkflowError extends Error {
|
|
24
|
+
code;
|
|
25
|
+
recoverable;
|
|
26
|
+
agentLabel;
|
|
27
|
+
details;
|
|
28
|
+
constructor(message, code, options = {}) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = "WorkflowError";
|
|
31
|
+
this.code = code;
|
|
32
|
+
this.recoverable = options.recoverable ?? false;
|
|
33
|
+
this.agentLabel = options.agentLabel;
|
|
34
|
+
this.details = options.details;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function isWorkflowError(error) {
|
|
38
|
+
return error instanceof WorkflowError;
|
|
39
|
+
}
|
|
40
|
+
export function isAbortError(error) {
|
|
41
|
+
if (!(error instanceof Error))
|
|
42
|
+
return false;
|
|
43
|
+
return /\babort(?:ed)?\b/i.test(error.message);
|
|
44
|
+
}
|
|
45
|
+
export function isTimeoutError(error) {
|
|
46
|
+
if (!(error instanceof Error))
|
|
47
|
+
return false;
|
|
48
|
+
return /\btimeout\b/i.test(error.message) || error.name === "TimeoutError";
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Wrap an unknown error into a WorkflowError with appropriate classification.
|
|
52
|
+
*/
|
|
53
|
+
export function wrapError(error, context) {
|
|
54
|
+
if (isWorkflowError(error))
|
|
55
|
+
return error;
|
|
56
|
+
if (isAbortError(error)) {
|
|
57
|
+
return new WorkflowError(error instanceof Error ? error.message : "Workflow was aborted", WorkflowErrorCode.WORKFLOW_ABORTED, { recoverable: true });
|
|
58
|
+
}
|
|
59
|
+
if (isTimeoutError(error)) {
|
|
60
|
+
return new WorkflowError(error instanceof Error ? error.message : "Agent timed out", WorkflowErrorCode.AGENT_TIMEOUT, { recoverable: true, agentLabel: context?.agentLabel });
|
|
61
|
+
}
|
|
62
|
+
return new WorkflowError(error instanceof Error ? error.message : String(error), WorkflowErrorCode.AGENT_EXECUTION_ERROR, { recoverable: true, agentLabel: context?.agentLabel, details: error });
|
|
63
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type { AdversarialReviewConfig } from "./adversarial-review.js";
|
|
2
|
+
export { generateAdversarialReviewWorkflow, generateMultiPerspectiveWorkflow } from "./adversarial-review.js";
|
|
3
|
+
export type { AgentRunOptions, AgentRunResult, WorkflowAgentOptions } from "./agent.js";
|
|
4
|
+
export { WorkflowAgent } from "./agent.js";
|
|
5
|
+
export type { AutoWorkflowConfig } from "./auto-workflow.js";
|
|
6
|
+
export { shouldUseWorkflow, suggestWorkflowScript } from "./auto-workflow.js";
|
|
7
|
+
export * from "./config.js";
|
|
8
|
+
export type { DeepResearchConfig } from "./deep-research.js";
|
|
9
|
+
export { generateCodebaseAuditWorkflow, generateDeepResearchWorkflow } from "./deep-research.js";
|
|
10
|
+
export type { WorkflowAgentSnapshot, WorkflowAgentStatus, WorkflowDisplay, WorkflowDisplayOptions, WorkflowSnapshot, } from "./display.js";
|
|
11
|
+
export { createToolUpdateWorkflowDisplay, createWidgetWorkflowDisplay, createWorkflowSnapshot, preview, recomputeWorkflowSnapshot, renderWorkflowLines, renderWorkflowText, } from "./display.js";
|
|
12
|
+
export { isAbortError, isTimeoutError, isWorkflowError, WorkflowError, WorkflowErrorCode, wrapError, } from "./errors.js";
|
|
13
|
+
export type { WorkflowLogger, WorkflowLoggerOptions } from "./logger.js";
|
|
14
|
+
export { createWorkflowLogger } from "./logger.js";
|
|
15
|
+
export type { ModelRoute, ModelRoutingConfig } from "./model-routing.js";
|
|
16
|
+
export { buildModelRoutingInstructions, parseModelRoutingFromMeta, resolveModelForPhase } from "./model-routing.js";
|
|
17
|
+
export type { PersistedRunState, RunPersistence, RunStatus } from "./run-persistence.js";
|
|
18
|
+
export { createRunPersistence, generateRunId } from "./run-persistence.js";
|
|
19
|
+
export type { StructuredOutputCapture, StructuredOutputToolOptions } from "./structured-output.js";
|
|
20
|
+
export { createStructuredOutputTool } from "./structured-output.js";
|
|
21
|
+
export type { AgentOptions, WorkflowMeta, WorkflowMetaPhase, WorkflowRunOptions, WorkflowRunResult, } from "./workflow.js";
|
|
22
|
+
export { parseWorkflowScript, runWorkflow } from "./workflow.js";
|
|
23
|
+
export type { ManagedRun, WorkflowManagerOptions } from "./workflow-manager.js";
|
|
24
|
+
export { WorkflowManager } from "./workflow-manager.js";
|
|
25
|
+
export type { SavedWorkflow, WorkflowStorage } from "./workflow-saved.js";
|
|
26
|
+
export { createWorkflowStorage } from "./workflow-saved.js";
|
|
27
|
+
export type { WorkflowToolInput, WorkflowToolOptions } from "./workflow-tool.js";
|
|
28
|
+
export { createWorkflowTool } from "./workflow-tool.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { generateAdversarialReviewWorkflow, generateMultiPerspectiveWorkflow } from "./adversarial-review.js";
|
|
2
|
+
export { WorkflowAgent } from "./agent.js";
|
|
3
|
+
export { shouldUseWorkflow, suggestWorkflowScript } from "./auto-workflow.js";
|
|
4
|
+
export * from "./config.js";
|
|
5
|
+
export { generateCodebaseAuditWorkflow, generateDeepResearchWorkflow } from "./deep-research.js";
|
|
6
|
+
export { createToolUpdateWorkflowDisplay, createWidgetWorkflowDisplay, createWorkflowSnapshot, preview, recomputeWorkflowSnapshot, renderWorkflowLines, renderWorkflowText, } from "./display.js";
|
|
7
|
+
export { isAbortError, isTimeoutError, isWorkflowError, WorkflowError, WorkflowErrorCode, wrapError, } from "./errors.js";
|
|
8
|
+
export { createWorkflowLogger } from "./logger.js";
|
|
9
|
+
export { buildModelRoutingInstructions, parseModelRoutingFromMeta, resolveModelForPhase } from "./model-routing.js";
|
|
10
|
+
export { createRunPersistence, generateRunId } from "./run-persistence.js";
|
|
11
|
+
export { createStructuredOutputTool } from "./structured-output.js";
|
|
12
|
+
export { parseWorkflowScript, runWorkflow } from "./workflow.js";
|
|
13
|
+
export { WorkflowManager } from "./workflow-manager.js";
|
|
14
|
+
export { createWorkflowStorage } from "./workflow-saved.js";
|
|
15
|
+
export { createWorkflowTool } from "./workflow-tool.js";
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow logger with file persistence.
|
|
3
|
+
*/
|
|
4
|
+
export interface WorkflowLogger {
|
|
5
|
+
log(message: string): void;
|
|
6
|
+
error(message: string): void;
|
|
7
|
+
warn(message: string): void;
|
|
8
|
+
getLogs(): string[];
|
|
9
|
+
persist(): string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface WorkflowLoggerOptions {
|
|
12
|
+
/** Run ID for persistence. */
|
|
13
|
+
runId?: string;
|
|
14
|
+
/** Working directory for file paths. */
|
|
15
|
+
cwd?: string;
|
|
16
|
+
/** Whether to persist logs to disk. */
|
|
17
|
+
persist?: boolean;
|
|
18
|
+
/** Callback for each log entry. */
|
|
19
|
+
onLog?: (message: string) => void;
|
|
20
|
+
}
|
|
21
|
+
export declare function createWorkflowLogger(options?: WorkflowLoggerOptions): WorkflowLogger;
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow logger with file persistence.
|
|
3
|
+
*/
|
|
4
|
+
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { WORKFLOW_RUNS_DIR } from "./config.js";
|
|
7
|
+
export function createWorkflowLogger(options = {}) {
|
|
8
|
+
const logs = [];
|
|
9
|
+
const persistLogs = options.persist ?? true;
|
|
10
|
+
const cwd = options.cwd ?? process.cwd();
|
|
11
|
+
const runId = options.runId ?? `run-${Date.now()}`;
|
|
12
|
+
let logFile = null;
|
|
13
|
+
const write = (level, message) => {
|
|
14
|
+
const timestamp = new Date().toISOString();
|
|
15
|
+
const entry = `[${timestamp}] [${level}] ${message}`;
|
|
16
|
+
logs.push(entry);
|
|
17
|
+
options.onLog?.(message);
|
|
18
|
+
if (persistLogs && logFile) {
|
|
19
|
+
try {
|
|
20
|
+
appendFileSync(logFile, `${entry}\n`);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Silent fail for log persistence
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const logger = {
|
|
28
|
+
log(message) {
|
|
29
|
+
write("INFO", message);
|
|
30
|
+
},
|
|
31
|
+
error(message) {
|
|
32
|
+
write("ERROR", message);
|
|
33
|
+
},
|
|
34
|
+
warn(message) {
|
|
35
|
+
write("WARN", message);
|
|
36
|
+
},
|
|
37
|
+
getLogs() {
|
|
38
|
+
return [...logs];
|
|
39
|
+
},
|
|
40
|
+
persist() {
|
|
41
|
+
if (!persistLogs)
|
|
42
|
+
return null;
|
|
43
|
+
try {
|
|
44
|
+
const runsDir = join(cwd, WORKFLOW_RUNS_DIR);
|
|
45
|
+
mkdirSync(runsDir, { recursive: true });
|
|
46
|
+
logFile = join(runsDir, `${runId}.log`);
|
|
47
|
+
writeFileSync(logFile, `${logs.join("\n")}\n`);
|
|
48
|
+
return logFile;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
// Initialize log file if persisting
|
|
56
|
+
if (persistLogs) {
|
|
57
|
+
try {
|
|
58
|
+
const runsDir = join(cwd, WORKFLOW_RUNS_DIR);
|
|
59
|
+
mkdirSync(runsDir, { recursive: true });
|
|
60
|
+
logFile = join(runsDir, `${runId}.log`);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Silent fail
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return logger;
|
|
67
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-stage model routing for workflows.
|
|
3
|
+
* Allows different phases to use different models.
|
|
4
|
+
*/
|
|
5
|
+
export interface ModelRoute {
|
|
6
|
+
/** Phase name pattern (regex or exact match). */
|
|
7
|
+
phasePattern: string;
|
|
8
|
+
/** Model to use for this phase. */
|
|
9
|
+
model: string;
|
|
10
|
+
/** Whether to use regex matching. */
|
|
11
|
+
useRegex?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface ModelRoutingConfig {
|
|
14
|
+
/** Default model for all phases. */
|
|
15
|
+
defaultModel?: string;
|
|
16
|
+
/** Per-phase model overrides. */
|
|
17
|
+
routes: ModelRoute[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve which model to use for a given phase.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveModelForPhase(phase: string | undefined, config: ModelRoutingConfig): string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Build model routing instructions for a workflow agent.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildModelRoutingInstructions(phase: string | undefined, config: ModelRoutingConfig): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Parse model routing from workflow meta phases.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseModelRoutingFromMeta(phases?: Array<{
|
|
31
|
+
title: string;
|
|
32
|
+
model?: string;
|
|
33
|
+
}>): ModelRoutingConfig;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-stage model routing for workflows.
|
|
3
|
+
* Allows different phases to use different models.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Resolve which model to use for a given phase.
|
|
7
|
+
*/
|
|
8
|
+
export function resolveModelForPhase(phase, config) {
|
|
9
|
+
if (!phase || !config.routes.length) {
|
|
10
|
+
return config.defaultModel;
|
|
11
|
+
}
|
|
12
|
+
for (const route of config.routes) {
|
|
13
|
+
if (route.useRegex) {
|
|
14
|
+
try {
|
|
15
|
+
const regex = new RegExp(route.phasePattern, "i");
|
|
16
|
+
if (regex.test(phase)) {
|
|
17
|
+
return route.model;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Invalid regex, skip
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
if (phase.toLowerCase().includes(route.phasePattern.toLowerCase())) {
|
|
26
|
+
return route.model;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return config.defaultModel;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build model routing instructions for a workflow agent.
|
|
34
|
+
*/
|
|
35
|
+
export function buildModelRoutingInstructions(phase, config) {
|
|
36
|
+
const model = resolveModelForPhase(phase, config);
|
|
37
|
+
if (!model)
|
|
38
|
+
return undefined;
|
|
39
|
+
return `Use model: ${model}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Parse model routing from workflow meta phases.
|
|
43
|
+
*/
|
|
44
|
+
export function parseModelRoutingFromMeta(phases) {
|
|
45
|
+
const routes = [];
|
|
46
|
+
if (phases) {
|
|
47
|
+
for (const phase of phases) {
|
|
48
|
+
if (phase.model) {
|
|
49
|
+
routes.push({
|
|
50
|
+
phasePattern: phase.title,
|
|
51
|
+
model: phase.model,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { routes };
|
|
57
|
+
}
|