pi-extensible-workflows 0.3.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 +72 -0
- package/dist/src/agent-execution.d.ts +213 -0
- package/dist/src/agent-execution.js +537 -0
- package/dist/src/ambient-workflow-evals.d.ts +112 -0
- package/dist/src/ambient-workflow-evals.js +375 -0
- package/dist/src/cli.d.ts +6 -0
- package/dist/src/cli.js +26 -0
- package/dist/src/doctor.d.ts +59 -0
- package/dist/src/doctor.js +269 -0
- package/dist/src/eval-capture-extension.d.ts +5 -0
- package/dist/src/eval-capture-extension.js +48 -0
- package/dist/src/index.d.ts +362 -0
- package/dist/src/index.js +2839 -0
- package/dist/src/persistence.d.ts +90 -0
- package/dist/src/persistence.js +530 -0
- package/dist/src/session-inspector.d.ts +59 -0
- package/dist/src/session-inspector.js +396 -0
- package/dist/src/workflow-evals-child.d.ts +1 -0
- package/dist/src/workflow-evals-child.js +13 -0
- package/dist/src/workflow-evals.d.ts +290 -0
- package/dist/src/workflow-evals.js +1221 -0
- package/evals/cases/custom-model-read.yaml +15 -0
- package/evals/cases/direct-answer.yaml +6 -0
- package/evals/cases/mixed-parallel-pipeline.yaml +18 -0
- package/evals/cases/output-schema.yaml +22 -0
- package/evals/cases/parallel.yaml +15 -0
- package/evals/cases/pipeline.yaml +10 -0
- package/evals/cases/ready-for-agent-parallel-merge.yaml +32 -0
- package/evals/cases/required-role.yaml +14 -0
- package/evals/cases/role-model-mixed.yaml +18 -0
- package/evals/cases/two-agents.yaml +10 -0
- package/package.json +65 -0
- package/skills/pi-extensible-workflows/SKILL.md +109 -0
- package/src/agent-execution.ts +529 -0
- package/src/ambient-workflow-evals.ts +452 -0
- package/src/cli.ts +23 -0
- package/src/doctor.ts +285 -0
- package/src/eval-capture-extension.ts +54 -0
- package/src/index.ts +2519 -0
- package/src/persistence.ts +470 -0
- package/src/session-inspector.ts +370 -0
- package/src/workflow-evals-child.ts +15 -0
- package/src/workflow-evals.ts +876 -0
- package/test/fixtures/ready-for-agent-tasks.md +9 -0
- package/test/fixtures/workflow-eval-roles/developer.md +18 -0
- package/test/fixtures/workflow-eval-roles/reviewer.md +18 -0
- package/test/fixtures/workflow-eval-roles/scout.md +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# pi-extensible-workflows
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Turn multi-agent tasks into deterministic jobs that fan out in parallel, pause for approval, and resume without rerunning completed work.
|
|
6
|
+
|
|
7
|
+
[Documentation](https://vekexasia.github.io/pi-extensible-workflows/) | [Developer guide](https://vekexasia.github.io/pi-extensible-workflows/developers.html) | [Agent guide](https://vekexasia.github.io/pi-extensible-workflows/agents.html)
|
|
8
|
+
|
|
9
|
+
Requires Node.js 22.19 or newer. This is a trusted Pi extension with the same filesystem and process access as Pi.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
pi install npm:pi-extensible-workflows
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
For source installs and local development, see the [installation guide](https://vekexasia.github.io/pi-extensible-workflows/developers.html#installation).
|
|
18
|
+
|
|
19
|
+
## Capabilities
|
|
20
|
+
|
|
21
|
+
A workflow can fan out across specialized agents, combine their results, and resume without rerunning completed work.
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
const reviews = await parallel("review", {
|
|
25
|
+
correctness: () => agent("Review the current changes for correctness issues."),
|
|
26
|
+
security: () => agent("Review the current changes for security risks.", {
|
|
27
|
+
role: "security-specialist",
|
|
28
|
+
}),
|
|
29
|
+
tests: () => agent("Review the current changes for missing test coverage."),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const summary = await agent(
|
|
33
|
+
prompt("Deduplicate and prioritize these findings:\n\n{reviews}", { reviews }),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return summary;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`security-specialist` is an example project role. Runs execute in the background by default, and completed work is reused when a run resumes.
|
|
40
|
+
|
|
41
|
+
The complete contracts and examples live in the documentation:
|
|
42
|
+
|
|
43
|
+
- [Workflow tool and invocation API](https://vekexasia.github.io/pi-extensible-workflows/developers.html#tool-api)
|
|
44
|
+
- [Workflow DSL and worktrees](https://vekexasia.github.io/pi-extensible-workflows/developers.html#dsl)
|
|
45
|
+
- [Reusable extension primitives](https://vekexasia.github.io/pi-extensible-workflows/developers.html#extensions)
|
|
46
|
+
- [Run inspection and recovery](https://vekexasia.github.io/pi-extensible-workflows/developers.html#operations)
|
|
47
|
+
- [Agent patterns and model selection](https://vekexasia.github.io/pi-extensible-workflows/agents.html#patterns)
|
|
48
|
+
- [Checkpoints](https://vekexasia.github.io/pi-extensible-workflows/agents.html#checkpoints)
|
|
49
|
+
|
|
50
|
+
## CLI
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
npx pi-extensible-workflows doctor
|
|
54
|
+
npx pi-extensible-workflows inspect [session-id]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`doctor` validates the installation and active Pi resources. `inspect` opens a read-only terminal view of persisted workflow runs.
|
|
58
|
+
|
|
59
|
+
## Development
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
npm ci
|
|
63
|
+
npm run check
|
|
64
|
+
npm run acceptance
|
|
65
|
+
npm pack --dry-run --json
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Model-backed evaluations are optional. See the [evaluation guide](https://vekexasia.github.io/pi-extensible-workflows/developers.html#evaluation).
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { type AgentSessionEvent, type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
3
|
+
type AgentMessage = {
|
|
4
|
+
role: string;
|
|
5
|
+
content?: unknown;
|
|
6
|
+
usage?: {
|
|
7
|
+
input: number;
|
|
8
|
+
output: number;
|
|
9
|
+
cacheRead: number;
|
|
10
|
+
cacheWrite: number;
|
|
11
|
+
cost: {
|
|
12
|
+
total: number;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
import type { JsonSchema, JsonValue, ModelSpec } from "./index.js";
|
|
17
|
+
import type { RunStore } from "./persistence.js";
|
|
18
|
+
export interface AgentDefinition {
|
|
19
|
+
prompt?: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
model?: string;
|
|
22
|
+
thinking?: ThinkingLevel;
|
|
23
|
+
tools?: readonly string[];
|
|
24
|
+
}
|
|
25
|
+
export interface AgentExecutionOptions {
|
|
26
|
+
label: string;
|
|
27
|
+
workflowName: string;
|
|
28
|
+
phase?: string;
|
|
29
|
+
parent?: string;
|
|
30
|
+
model?: string;
|
|
31
|
+
thinking?: ThinkingLevel;
|
|
32
|
+
onProgress?: (progress: AgentProgress) => void | Promise<void>;
|
|
33
|
+
onAttempt?: (attempt: Pick<AgentAttempt, "attempt" | "sessionId" | "sessionFile">) => void | Promise<void>;
|
|
34
|
+
tools?: readonly string[];
|
|
35
|
+
effectiveTools?: readonly string[];
|
|
36
|
+
role?: string;
|
|
37
|
+
schema?: JsonSchema;
|
|
38
|
+
retries?: number;
|
|
39
|
+
timeoutMs?: number | null;
|
|
40
|
+
retryState?: string;
|
|
41
|
+
worktreeOwner?: string;
|
|
42
|
+
cwd?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface AgentExecutionRoot {
|
|
45
|
+
cwd: string;
|
|
46
|
+
model: ModelSpec;
|
|
47
|
+
tools: ReadonlySet<string>;
|
|
48
|
+
agentDefinitions?: Readonly<Record<string, AgentDefinition>>;
|
|
49
|
+
agentDir?: string;
|
|
50
|
+
availableModels?: ReadonlySet<string>;
|
|
51
|
+
runStore?: RunStore;
|
|
52
|
+
providerPause?: () => Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
export interface AgentAccounting {
|
|
55
|
+
input: number;
|
|
56
|
+
output: number;
|
|
57
|
+
cacheRead: number;
|
|
58
|
+
cacheWrite: number;
|
|
59
|
+
cost: number;
|
|
60
|
+
}
|
|
61
|
+
export interface AgentToolCallProgress {
|
|
62
|
+
id: string;
|
|
63
|
+
name: string;
|
|
64
|
+
state: "running" | "completed" | "failed";
|
|
65
|
+
}
|
|
66
|
+
export interface AgentActivity {
|
|
67
|
+
kind: "reasoning" | "tool" | "text";
|
|
68
|
+
text: string;
|
|
69
|
+
}
|
|
70
|
+
export interface AgentProgress {
|
|
71
|
+
accounting: AgentAccounting;
|
|
72
|
+
toolCalls: readonly AgentToolCallProgress[];
|
|
73
|
+
activity?: AgentActivity;
|
|
74
|
+
persist: boolean;
|
|
75
|
+
}
|
|
76
|
+
export interface AgentAttempt {
|
|
77
|
+
attempt: number;
|
|
78
|
+
sessionId: string;
|
|
79
|
+
sessionFile: string;
|
|
80
|
+
result?: JsonValue;
|
|
81
|
+
error?: {
|
|
82
|
+
code: string;
|
|
83
|
+
message: string;
|
|
84
|
+
};
|
|
85
|
+
accounting: AgentAccounting;
|
|
86
|
+
}
|
|
87
|
+
export interface AgentExecutionResult {
|
|
88
|
+
value: JsonValue;
|
|
89
|
+
attempts: readonly AgentAttempt[];
|
|
90
|
+
cwd: string;
|
|
91
|
+
}
|
|
92
|
+
export interface NativeSession {
|
|
93
|
+
readonly sessionId: string;
|
|
94
|
+
readonly sessionFile: string | undefined;
|
|
95
|
+
readonly messages: readonly AgentMessage[];
|
|
96
|
+
readonly systemPrompt?: string;
|
|
97
|
+
readonly agent?: {
|
|
98
|
+
state: {
|
|
99
|
+
tools: readonly {
|
|
100
|
+
name: string;
|
|
101
|
+
}[];
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
subscribe?(listener: (event: AgentSessionEvent) => void): () => void;
|
|
105
|
+
prompt(text: string): Promise<void>;
|
|
106
|
+
steer?(text: string): Promise<void>;
|
|
107
|
+
abort?(): Promise<void>;
|
|
108
|
+
dispose(): void;
|
|
109
|
+
}
|
|
110
|
+
export interface SessionInput {
|
|
111
|
+
cwd: string;
|
|
112
|
+
model: ModelSpec;
|
|
113
|
+
tools: readonly string[];
|
|
114
|
+
sessionLabel: string;
|
|
115
|
+
agentDir?: string;
|
|
116
|
+
customTools?: readonly ToolDefinition[];
|
|
117
|
+
resultTool?: ToolDefinition;
|
|
118
|
+
systemPromptAppend?: string;
|
|
119
|
+
}
|
|
120
|
+
export type SessionFactory = (input: SessionInput) => Promise<NativeSession>;
|
|
121
|
+
export declare function createNativeAgentSession(input: SessionInput): Promise<NativeSession>;
|
|
122
|
+
export declare class WorkflowAgentExecutor {
|
|
123
|
+
private readonly root;
|
|
124
|
+
private readonly createSession;
|
|
125
|
+
constructor(root: AgentExecutionRoot, createSession?: SessionFactory);
|
|
126
|
+
resolve(options: AgentExecutionOptions, inheritedTools?: readonly string[]): {
|
|
127
|
+
model: ModelSpec;
|
|
128
|
+
tools: readonly string[];
|
|
129
|
+
systemPromptAppend: string;
|
|
130
|
+
};
|
|
131
|
+
execute(task: string, options: AgentExecutionOptions, signal?: AbortSignal, customTools?: readonly ToolDefinition[], setSteer?: (handler: (message: string) => void | Promise<void>) => void, beforeRetry?: () => void): Promise<AgentExecutionResult>;
|
|
132
|
+
}
|
|
133
|
+
export interface ScheduledAgentOptions {
|
|
134
|
+
label: string;
|
|
135
|
+
requestedLabel?: string;
|
|
136
|
+
parentBreadcrumb?: string;
|
|
137
|
+
cwd: string;
|
|
138
|
+
tools: readonly string[];
|
|
139
|
+
worktreeOwner?: string;
|
|
140
|
+
model?: string;
|
|
141
|
+
thinking?: ThinkingLevel;
|
|
142
|
+
role?: string;
|
|
143
|
+
schema?: JsonSchema;
|
|
144
|
+
retries?: number;
|
|
145
|
+
timeoutMs?: number | null;
|
|
146
|
+
}
|
|
147
|
+
export type ScheduledAgentResult = {
|
|
148
|
+
id: string;
|
|
149
|
+
ok: true;
|
|
150
|
+
value: JsonValue;
|
|
151
|
+
} | {
|
|
152
|
+
id: string;
|
|
153
|
+
ok: false;
|
|
154
|
+
error: {
|
|
155
|
+
code: string;
|
|
156
|
+
message: string;
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
export interface ScheduledAgentInput {
|
|
160
|
+
id: string;
|
|
161
|
+
runId: string;
|
|
162
|
+
parentId?: string;
|
|
163
|
+
prompt: string;
|
|
164
|
+
options: Readonly<ScheduledAgentOptions>;
|
|
165
|
+
signal: AbortSignal;
|
|
166
|
+
setSteer: (handler: (message: string) => void | Promise<void>) => void;
|
|
167
|
+
}
|
|
168
|
+
export type ScheduledAgentRunner = (input: ScheduledAgentInput) => Promise<JsonValue>;
|
|
169
|
+
type ScheduledNode = {
|
|
170
|
+
id: string;
|
|
171
|
+
runId: string;
|
|
172
|
+
parentId?: string;
|
|
173
|
+
options: Readonly<ScheduledAgentOptions>;
|
|
174
|
+
children: Set<string>;
|
|
175
|
+
collected: boolean;
|
|
176
|
+
state: "queued" | "running" | "waiting_for_child" | "completed" | "failed" | "cancelled";
|
|
177
|
+
controller: AbortController;
|
|
178
|
+
promise: Promise<ScheduledAgentResult>;
|
|
179
|
+
resolve: (result: ScheduledAgentResult) => void;
|
|
180
|
+
task: () => Promise<void>;
|
|
181
|
+
restored: boolean;
|
|
182
|
+
steer?: (message: string) => void | Promise<void>;
|
|
183
|
+
};
|
|
184
|
+
export type OwnershipRecord = {
|
|
185
|
+
id: string;
|
|
186
|
+
parentId?: string;
|
|
187
|
+
label: string;
|
|
188
|
+
state: ScheduledNode["state"];
|
|
189
|
+
options: Readonly<ScheduledAgentOptions>;
|
|
190
|
+
};
|
|
191
|
+
type OwnershipWriter = (runId: string, ownership: readonly OwnershipRecord[]) => void | Promise<void>;
|
|
192
|
+
export declare class FairAgentScheduler {
|
|
193
|
+
#private;
|
|
194
|
+
private readonly runner;
|
|
195
|
+
readonly sessionLimit: number;
|
|
196
|
+
private readonly writeOwnership?;
|
|
197
|
+
constructor(runner: ScheduledAgentRunner, sessionLimit?: number, writeOwnership?: OwnershipWriter | undefined);
|
|
198
|
+
addRun(runId: string, limit?: number, maxAgentLaunches?: number): void;
|
|
199
|
+
spawn(runId: string, prompt: string, options: ScheduledAgentOptions, parentId?: string): {
|
|
200
|
+
id: string;
|
|
201
|
+
result: Promise<ScheduledAgentResult>;
|
|
202
|
+
};
|
|
203
|
+
result(parentId: string, childId: string): Promise<ScheduledAgentResult>;
|
|
204
|
+
steer(parentId: string, childId: string, message: string): Promise<void>;
|
|
205
|
+
cancel(id: string): void;
|
|
206
|
+
cancelChildren(id: string): void;
|
|
207
|
+
cancelRun(runId: string): Promise<void>;
|
|
208
|
+
toolsFor(parentId: string, resolveTools?: (role: string | undefined, tools: readonly string[] | undefined, model: string | undefined, inheritedTools: readonly string[], thinking: ThinkingLevel | undefined) => readonly string[]): ToolDefinition[];
|
|
209
|
+
snapshot(): readonly OwnershipRecord[];
|
|
210
|
+
restoreRun(runId: string, limit: number, maxAgentLaunches: number, ownership: readonly OwnershipRecord[]): void;
|
|
211
|
+
flush(): Promise<void>;
|
|
212
|
+
}
|
|
213
|
+
export {};
|