pi-workflow-engine 0.11.0 → 0.13.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/.pi/extensions/pi-workflow-engine/index.ts +255 -97
- package/.pi/extensions/pi-workflow-engine/src/advisory-challenge.ts +75 -0
- package/.pi/extensions/pi-workflow-engine/src/advisory-evidence.ts +90 -0
- package/.pi/extensions/pi-workflow-engine/src/advisory-schema.ts +13 -5
- package/.pi/extensions/pi-workflow-engine/src/agent-attempt.ts +3 -1
- package/.pi/extensions/pi-workflow-engine/src/agent-limits.ts +35 -0
- package/.pi/extensions/pi-workflow-engine/src/agent-replay.ts +27 -4
- package/.pi/extensions/pi-workflow-engine/src/agent-retry.ts +116 -0
- package/.pi/extensions/pi-workflow-engine/src/agent-runner-types.ts +49 -45
- package/.pi/extensions/pi-workflow-engine/src/agent-runner.ts +95 -26
- package/.pi/extensions/pi-workflow-engine/src/agent-session-providers.ts +50 -0
- package/.pi/extensions/pi-workflow-engine/src/agent-session.ts +158 -203
- package/.pi/extensions/pi-workflow-engine/src/agent-skills.ts +48 -42
- package/.pi/extensions/pi-workflow-engine/src/agent-workspace.ts +26 -4
- package/.pi/extensions/pi-workflow-engine/src/background-workflow-tool.ts +75 -0
- package/.pi/extensions/pi-workflow-engine/src/background-workflows.ts +492 -0
- package/.pi/extensions/pi-workflow-engine/src/budget.ts +3 -3
- package/.pi/extensions/pi-workflow-engine/src/cancellation.ts +38 -0
- package/.pi/extensions/pi-workflow-engine/src/command-completions.ts +36 -0
- package/.pi/extensions/pi-workflow-engine/src/concurrency.ts +82 -10
- package/.pi/extensions/pi-workflow-engine/src/diff-capture.ts +16 -130
- package/.pi/extensions/pi-workflow-engine/src/discovery.ts +9 -5
- package/.pi/extensions/pi-workflow-engine/src/dynamax.ts +109 -60
- package/.pi/extensions/pi-workflow-engine/src/engine.ts +166 -18
- package/.pi/extensions/pi-workflow-engine/src/filesystem-error.ts +4 -0
- package/.pi/extensions/pi-workflow-engine/src/inline-workflow.ts +2 -2
- package/.pi/extensions/pi-workflow-engine/src/journal.ts +13 -6
- package/.pi/extensions/pi-workflow-engine/src/model-profile-command.ts +167 -0
- package/.pi/extensions/pi-workflow-engine/src/model-profiles.ts +310 -0
- package/.pi/extensions/pi-workflow-engine/src/options.ts +88 -1
- package/.pi/extensions/pi-workflow-engine/src/pi-compat.ts +32 -0
- package/.pi/extensions/pi-workflow-engine/src/progress-types.ts +50 -0
- package/.pi/extensions/pi-workflow-engine/src/progress.ts +72 -117
- package/.pi/extensions/pi-workflow-engine/src/provider-usage-limit.ts +189 -0
- package/.pi/extensions/pi-workflow-engine/src/research-contract.ts +89 -0
- package/.pi/extensions/pi-workflow-engine/src/research-evidence.ts +289 -0
- package/.pi/extensions/pi-workflow-engine/src/resume-context.ts +127 -41
- package/.pi/extensions/pi-workflow-engine/src/review/code-review-orchestration.ts +24 -0
- package/.pi/extensions/pi-workflow-engine/src/review/patch-validation.ts +120 -0
- package/.pi/extensions/pi-workflow-engine/src/review/review-fix-workflow.ts +43 -7
- package/.pi/extensions/pi-workflow-engine/src/review/review-format.ts +2 -1
- package/.pi/extensions/pi-workflow-engine/src/review/review-issues.ts +1 -1
- package/.pi/extensions/pi-workflow-engine/src/review/review-report.ts +1 -1
- package/.pi/extensions/pi-workflow-engine/src/review/review-results-flow.ts +2 -1
- package/.pi/extensions/pi-workflow-engine/src/review/review-results-viewer.ts +11 -30
- package/.pi/extensions/pi-workflow-engine/src/review/review-session-coordinator.ts +5 -5
- package/.pi/extensions/pi-workflow-engine/src/review/review-snapshot.ts +3 -4
- package/.pi/extensions/pi-workflow-engine/src/review-diff-target.ts +130 -0
- package/.pi/extensions/pi-workflow-engine/src/session-identity.ts +6 -0
- package/.pi/extensions/pi-workflow-engine/src/structured-output.ts +32 -0
- package/.pi/extensions/pi-workflow-engine/src/tool-capabilities.ts +44 -0
- package/.pi/extensions/pi-workflow-engine/src/tool-source-identity.ts +1 -4
- package/.pi/extensions/pi-workflow-engine/src/tree-fingerprint.ts +1 -4
- package/.pi/extensions/pi-workflow-engine/src/types.ts +52 -15
- package/.pi/extensions/pi-workflow-engine/src/ui/dynamax-editor-decoration.ts +220 -0
- package/.pi/extensions/pi-workflow-engine/src/ui/workflow-format.ts +26 -6
- package/.pi/extensions/pi-workflow-engine/src/ui/workflow-inspector.ts +88 -46
- package/.pi/extensions/pi-workflow-engine/src/ui/workflow-result-renderer.ts +7 -3
- package/.pi/extensions/pi-workflow-engine/src/ui/workflow-viewer-layout.ts +51 -0
- package/.pi/extensions/pi-workflow-engine/src/ui/workflow-widget.ts +12 -67
- package/.pi/extensions/pi-workflow-engine/src/usage.ts +124 -16
- package/.pi/extensions/pi-workflow-engine/src/workflow-advisory-utils.ts +151 -178
- package/.pi/extensions/pi-workflow-engine/src/workflow-execution.ts +1 -1
- package/.pi/extensions/pi-workflow-engine/src/workflow-run-background.ts +72 -0
- package/.pi/extensions/pi-workflow-engine/src/workflow-run-controller.ts +354 -0
- package/.pi/extensions/pi-workflow-engine/src/workflow-run-history.ts +162 -0
- package/.pi/extensions/pi-workflow-engine/src/workflow-run-record.ts +632 -0
- package/.pi/extensions/pi-workflow-engine/src/workflow-run-store.ts +172 -0
- package/.pi/extensions/pi-workflow-engine/src/workflow-usage-limit-scheduler.ts +80 -0
- package/.pi/extensions/pi-workflow-engine/src/workflows.ts +2 -0
- package/.pi/extensions/pi-workflow-engine/src/worktree.ts +47 -3
- package/.pi/extensions/pi-workflow-engine/workflows/code-review.ts +65 -162
- package/.pi/extensions/pi-workflow-engine/workflows/diagnose.ts +48 -149
- package/.pi/extensions/pi-workflow-engine/workflows/perf-review.ts +28 -77
- package/.pi/extensions/pi-workflow-engine/workflows/refactor-scout.ts +28 -77
- package/.pi/extensions/pi-workflow-engine/workflows/research.ts +170 -0
- package/README.md +72 -141
- package/USAGE.md +270 -17
- package/package.json +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { assertWorkflowBudgetAvailable } from "./budget.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { WorkflowAgentTimeoutError } from "./agent-limits.ts";
|
|
3
|
+
import { abortReason, linkAbortSignal, throwIfAborted } from "./cancellation.ts";
|
|
3
4
|
import { executeAgentAttempt } from "./agent-attempt.ts";
|
|
4
5
|
import {
|
|
5
6
|
createAgentReplayPlan,
|
|
6
7
|
isReplayEnabled,
|
|
7
8
|
settleAgentAttempt,
|
|
9
|
+
type AgentAttemptResult,
|
|
8
10
|
type AgentReplayPlan,
|
|
9
11
|
} from "./agent-replay.ts";
|
|
10
12
|
import {
|
|
@@ -18,13 +20,16 @@ import type {
|
|
|
18
20
|
} from "./agent-runner-types.ts";
|
|
19
21
|
import type { AgentResumeBaseContext } from "./resume-context.ts";
|
|
20
22
|
import { unknownErrorMessage } from "./unknown-error.ts";
|
|
23
|
+
import {
|
|
24
|
+
agentRetryDelayMs,
|
|
25
|
+
WorkflowProviderError,
|
|
26
|
+
} from "./agent-retry.ts";
|
|
27
|
+
import { resolveAgentModelProfile } from "./model-profiles.ts";
|
|
21
28
|
|
|
22
29
|
export type {
|
|
23
30
|
AgentExecutionOptions,
|
|
24
31
|
AgentProgress,
|
|
25
|
-
AgentRunnerEvent,
|
|
26
32
|
AgentRunnerSession,
|
|
27
|
-
AgentRunnerToolInfo,
|
|
28
33
|
CreateAgentSession,
|
|
29
34
|
RunContext,
|
|
30
35
|
} from "./agent-runner-types.ts";
|
|
@@ -35,7 +40,7 @@ export type { ResolvedAgentModel, ResolvedAgentModelRequest } from "./agent-sess
|
|
|
35
40
|
* Run one subagent to completion in an isolated in-memory session.
|
|
36
41
|
*
|
|
37
42
|
* Session construction, replay validation, and workspace cleanup live in focused
|
|
38
|
-
* modules; this boundary owns
|
|
43
|
+
* modules; this boundary owns admission, progress, and bounded cache/provider retries.
|
|
39
44
|
*/
|
|
40
45
|
export async function runAgent(
|
|
41
46
|
rc: RunContext,
|
|
@@ -53,35 +58,63 @@ export async function runAgent(
|
|
|
53
58
|
|
|
54
59
|
return await rc.perf.time("agent.total_ms", async () => {
|
|
55
60
|
throwIfAborted(rc.signal);
|
|
56
|
-
const
|
|
57
|
-
const
|
|
61
|
+
const routing = resolveAgentRouting(rc, opts, label);
|
|
62
|
+
const effectiveOpts = routing.thinkingLevel === opts.thinkingLevel
|
|
63
|
+
? opts
|
|
64
|
+
: { ...opts, thinkingLevel: routing.thinkingLevel };
|
|
65
|
+
const replay = createAgentReplayPlan(prompt, effectiveOpts);
|
|
58
66
|
if (!isReplayEnabled(replay)) assertWorkflowBudgetAvailable(rc.budget);
|
|
59
67
|
|
|
60
68
|
const rowId = rc.progress.agentQueued(opts.phase, label);
|
|
69
|
+
const liveScope = createAgentLiveScope(rc, label);
|
|
61
70
|
try {
|
|
62
71
|
return await rc.semaphore.run(
|
|
63
72
|
async () => {
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
const agentRc: RunContext = { ...rc, signal: liveScope.signal };
|
|
74
|
+
throwIfAborted(agentRc.signal);
|
|
75
|
+
if (!isReplayEnabled(replay)) assertWorkflowBudgetAvailable(agentRc.budget);
|
|
66
76
|
rc.progress.agentStart(opts.phase, label, rowId);
|
|
67
77
|
if (replay.kind === "disabled") {
|
|
68
78
|
rc.progress.log(`${label}: resume disabled for this call (${replay.reason})`);
|
|
69
79
|
}
|
|
70
80
|
|
|
71
81
|
let attemptPlan: AgentReplayPlan = replay;
|
|
82
|
+
let providerRetries = 0;
|
|
72
83
|
while (true) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
let outcome: AgentAttemptResult;
|
|
85
|
+
try {
|
|
86
|
+
outcome = await executeAgentAttempt({
|
|
87
|
+
rc: agentRc,
|
|
88
|
+
prompt,
|
|
89
|
+
opts: effectiveOpts,
|
|
90
|
+
resumeBaseContext,
|
|
91
|
+
model: routing.model,
|
|
92
|
+
replay: attemptPlan,
|
|
93
|
+
label,
|
|
94
|
+
rowId,
|
|
95
|
+
tags,
|
|
96
|
+
admitLiveAgent: liveScope.admit,
|
|
97
|
+
});
|
|
98
|
+
} catch (error) {
|
|
99
|
+
if (!(error instanceof WorkflowProviderError) || !error.retryable) throw error;
|
|
100
|
+
if (providerRetries >= agentRc.agentRetries) {
|
|
101
|
+
if (agentRc.agentRetries > 0) {
|
|
102
|
+
rc.progress.log(`${label}: transient provider failure after ${providerRetries} retries; giving up`);
|
|
103
|
+
}
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
assertWorkflowBudgetAvailable(agentRc.budget);
|
|
108
|
+
providerRetries++;
|
|
109
|
+
const delayMs = agentRetryDelayMs(providerRetries);
|
|
110
|
+
rc.progress.log(`${label}: transient provider failure; retry ${providerRetries}/${agentRc.agentRetries} in ${delayMs}ms`);
|
|
111
|
+
rc.perf.counter("agent.provider_retry", 1, tags);
|
|
112
|
+
rc.perf.observe("agent.provider_retry_delay_ms", delayMs, tags);
|
|
113
|
+
attemptPlan = { kind: "off" };
|
|
114
|
+
await agentRc.retryScheduler.sleep(delayMs, agentRc.signal);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const settlement = await settleAgentAttempt({ rc: agentRc, label, tags, replay: attemptPlan, outcome });
|
|
85
118
|
if (settlement.kind === "retry-live") {
|
|
86
119
|
attemptPlan = { kind: "off" };
|
|
87
120
|
continue;
|
|
@@ -96,20 +129,56 @@ export async function runAgent(
|
|
|
96
129
|
},
|
|
97
130
|
);
|
|
98
131
|
} catch (error) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
132
|
+
let failure = error;
|
|
133
|
+
if (liveScope.signal.aborted) failure = abortReason(liveScope.signal);
|
|
134
|
+
if (rc.signal?.aborted) failure = abortReason(rc.signal);
|
|
135
|
+
rc.progress.agentFailed(label, failure, rowId);
|
|
136
|
+
rc.progress.log(`${label} failed: ${unknownErrorMessage(failure)}`);
|
|
137
|
+
throw failure;
|
|
138
|
+
} finally {
|
|
139
|
+
liveScope.dispose();
|
|
102
140
|
}
|
|
103
141
|
}, tags);
|
|
104
142
|
}
|
|
105
143
|
|
|
106
|
-
function
|
|
144
|
+
function createAgentLiveScope(rc: RunContext, label: string) {
|
|
145
|
+
const controller = new AbortController();
|
|
146
|
+
const unlink = linkAbortSignal(rc.signal, controller);
|
|
147
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
148
|
+
let timerStarted = false;
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
signal: controller.signal,
|
|
152
|
+
admit() {
|
|
153
|
+
rc.agentLimiter.admit(controller.signal);
|
|
154
|
+
if (timerStarted) return;
|
|
155
|
+
timerStarted = true;
|
|
156
|
+
timer = setTimeout(
|
|
157
|
+
() => controller.abort(new WorkflowAgentTimeoutError(label, rc.agentTimeoutMs)),
|
|
158
|
+
rc.agentTimeoutMs,
|
|
159
|
+
);
|
|
160
|
+
},
|
|
161
|
+
dispose() {
|
|
162
|
+
if (timer) clearTimeout(timer);
|
|
163
|
+
unlink();
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function resolveAgentRouting(
|
|
107
169
|
rc: RunContext,
|
|
108
170
|
opts: AgentExecutionOptions,
|
|
109
171
|
label: string,
|
|
110
|
-
): ResolvedAgentModel["model"] {
|
|
172
|
+
): { readonly model: ResolvedAgentModel["model"]; readonly thinkingLevel: AgentExecutionOptions["thinkingLevel"] } {
|
|
111
173
|
try {
|
|
112
|
-
return
|
|
174
|
+
return resolveAgentModelProfile(
|
|
175
|
+
{
|
|
176
|
+
request: opts,
|
|
177
|
+
profiles: rc.modelProfiles,
|
|
178
|
+
resolveExplicitModel: (modelRef) => resolveAgentModel(modelRef, rc.modelRegistry, rc.hostModel).model,
|
|
179
|
+
hostModel: rc.hostModel,
|
|
180
|
+
},
|
|
181
|
+
);
|
|
113
182
|
} catch (error) {
|
|
114
183
|
rc.progress.agentFailed(label, error);
|
|
115
184
|
throw error;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { ModelRegistry, ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
|
|
4
|
+
type HostProviderRegistry = Pick<
|
|
5
|
+
ModelRegistry,
|
|
6
|
+
| "getApiKeyForProvider"
|
|
7
|
+
| "getProviderAuthStatus"
|
|
8
|
+
| "getRegisteredProviderConfig"
|
|
9
|
+
| "getRegisteredProviderIds"
|
|
10
|
+
| "isUsingOAuth"
|
|
11
|
+
>;
|
|
12
|
+
|
|
13
|
+
/** Mirror the host's live provider and selected-model auth state into a child runtime. */
|
|
14
|
+
export async function synchronizeWorkflowModelRuntime(input: {
|
|
15
|
+
readonly host: HostProviderRegistry;
|
|
16
|
+
readonly child: ModelRuntime;
|
|
17
|
+
readonly selectedModel: Model<Api> | undefined;
|
|
18
|
+
readonly removeChildOnlyProviders: boolean;
|
|
19
|
+
}): Promise<void> {
|
|
20
|
+
const { host, child, selectedModel, removeChildOnlyProviders } = input;
|
|
21
|
+
const hostProviderIds = new Set(host.getRegisteredProviderIds());
|
|
22
|
+
|
|
23
|
+
if (removeChildOnlyProviders) {
|
|
24
|
+
for (const providerId of child.getRegisteredProviderIds()) {
|
|
25
|
+
if (!hostProviderIds.has(providerId)) child.unregisterProvider(providerId);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
for (const providerId of hostProviderIds) {
|
|
30
|
+
const config = host.getRegisteredProviderConfig(providerId);
|
|
31
|
+
if (!config) continue;
|
|
32
|
+
child.unregisterProvider(providerId);
|
|
33
|
+
child.registerProvider(providerId, config);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const selectedProvider = selectedModel?.provider;
|
|
37
|
+
if (!selectedModel || !selectedProvider) return;
|
|
38
|
+
|
|
39
|
+
const hostAuth = host.getProviderAuthStatus(selectedProvider);
|
|
40
|
+
const childAuth = child.getProviderAuthStatus(selectedProvider);
|
|
41
|
+
if (hostAuth.configured && !childAuth.configured && host.isUsingOAuth(selectedModel)) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Workflow subagents cannot inherit OAuth credentials for "${selectedProvider}" from a host-only credential store; configure OAuth in Pi's shared agent directory.`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
if (hostAuth.source === "runtime" || (hostAuth.configured && !childAuth.configured)) {
|
|
47
|
+
const apiKey = await host.getApiKeyForProvider(selectedProvider);
|
|
48
|
+
if (apiKey !== undefined) await child.setRuntimeApiKey(selectedProvider, apiKey);
|
|
49
|
+
}
|
|
50
|
+
}
|