@wichayutdew/pi-workflows 2.5.1 → 2.6.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 +4 -1
- package/dist/index.js +1133 -2409
- package/examples/starter-kit/agents/planner.md +7 -0
- package/examples/starter-kit/agents/reviewer.md +6 -0
- package/examples/starter-kit/agents/scout.md +6 -0
- package/examples/starter-kit/agents/worker.md +7 -0
- package/examples/starter-kit/agents/workspace-preparer.md +6 -0
- package/examples/starter-kit/investigate.workflow.yaml +114 -0
- package/examples/starter-kit/mr-comment.workflow.yaml +114 -54
- package/examples/starter-kit/mr-review.workflow.yaml +77 -56
- package/examples/starter-kit/settings.yaml +3 -0
- package/examples/starter-kit/steps/investigate/investigate.md +77 -0
- package/examples/starter-kit/steps/investigate/retrieve.md +63 -0
- package/examples/starter-kit/steps/investigate/validate.md +46 -0
- package/examples/starter-kit/steps/mr-comment/checkout-source.md +67 -0
- package/examples/starter-kit/steps/mr-comment/fetch.md +34 -26
- package/examples/starter-kit/steps/mr-comment/implement.md +34 -27
- package/examples/starter-kit/steps/mr-comment/plan.md +54 -40
- package/examples/starter-kit/steps/mr-comment/publish.md +28 -27
- package/examples/starter-kit/steps/mr-comment/verify.md +42 -35
- package/examples/starter-kit/steps/mr-review/fetch.md +52 -22
- package/examples/starter-kit/steps/mr-review/publish-approved.md +49 -0
- package/examples/starter-kit/steps/mr-review/review-for-approval.md +142 -0
- package/examples/starter-kit/steps/mr-review/verify-published.md +37 -0
- package/examples/starter-kit/steps/shared/prepare-workspace.md +91 -73
- package/examples/starter-kit/steps/shared/publish-remote.md +45 -0
- package/examples/starter-kit/steps/ticket/implement.md +59 -25
- package/examples/starter-kit/steps/ticket/plan.md +145 -72
- package/examples/starter-kit/steps/ticket/verify.md +80 -45
- package/examples/starter-kit/steps/work/implement.md +60 -24
- package/examples/starter-kit/steps/work/plan.md +129 -57
- package/examples/starter-kit/steps/work/verify.md +58 -22
- package/examples/starter-kit/ticket.workflow.yaml +75 -36
- package/examples/starter-kit/work.workflow.yaml +63 -33
- package/package.json +3 -16
- package/schemas/workflow.schema.json +2 -15
- package/src/agents/profile.ts +98 -0
- package/src/config/ceiling.ts +0 -82
- package/src/config/types.ts +6 -53
- package/src/config/validation/settings.ts +2 -14
- package/src/config/validation/step.ts +12 -8
- package/src/config/validation/workflow.ts +1 -10
- package/src/engine/run-workflow-validation.ts +0 -3
- package/src/engine/state-types.ts +1 -1
- package/src/harness/action-context.ts +1 -13
- package/src/harness/delegation-control-actions.ts +4 -63
- package/src/harness/delegation-plan.ts +26 -80
- package/src/harness/delegation-response-actions.ts +22 -135
- package/src/harness/dependencies.ts +1 -12
- package/src/harness/status-actions.ts +1 -0
- package/src/harness/step-execution-actions.ts +2 -9
- package/src/harness/types.ts +2 -40
- package/src/harness.ts +2 -17
- package/src/index.ts +4 -9
- package/src/integrations/subagents/child-policy-validation.ts +6 -7
- package/src/integrations/subagents/child-runtime-dependencies.ts +1 -1
- package/src/integrations/subagents/child-runtime-policy.ts +1 -7
- package/src/integrations/subagents/child-runtime.ts +18 -9
- package/src/integrations/subagents/client.ts +240 -98
- package/src/integrations/subagents/protocol-events.ts +32 -15
- package/src/integrations/subagents/protocol.ts +1 -1
- package/src/preflight.ts +0 -8
- package/src/prompt/main-workflow-notice.ts +8 -15
- package/src/prompt/step-task.ts +7 -6
- package/src/workflow-status/format-status.ts +5 -1
- package/src/workflow-status/render-step-detail.ts +94 -0
- package/src/workflow-status/types.ts +1 -0
- package/src/workflow-status/view.ts +49 -14
- package/agents/step.md +0 -32
- package/examples/mr-comments.workflow.yaml +0 -125
- package/examples/prompts/mr-comments/implement.md +0 -17
- package/examples/prompts/mr-comments/inspect.md +0 -5
- package/examples/prompts/mr-comments/plan.md +0 -54
- package/examples/prompts/mr-comments/verify.md +0 -9
- package/examples/settings.yaml +0 -27
- package/examples/starter-kit/steps/mr-review/publish.md +0 -48
- package/examples/starter-kit/steps/mr-review/review.md +0 -76
- package/examples/starter-kit/steps/mr-review/verify.md +0 -35
- package/src/config/validation/subagent.ts +0 -288
- package/src/harness/delegation-failure.ts +0 -248
- package/src/harness/delegation-recovery-validation.ts +0 -161
- package/src/harness/delegation-retry-policy.ts +0 -120
- package/src/integrations/subagents/client-delegation.ts +0 -181
- package/src/integrations/subagents/client-messages.ts +0 -66
- package/src/integrations/subagents/client-types.ts +0 -36
- package/src/integrations/subagents/diagnostic-format.ts +0 -45
- package/src/integrations/subagents/diagnostic-text.ts +0 -114
- package/src/integrations/subagents/diagnostic-types.ts +0 -83
- package/src/integrations/subagents/diagnostics.ts +0 -26
- package/src/integrations/subagents/failure-correlation.ts +0 -285
- package/src/integrations/subagents/failure-transcript.ts +0 -252
- package/src/integrations/subagents/hidden-bash-failure.ts +0 -98
- package/src/integrations/subagents/replay-audit.ts +0 -146
- package/src/integrations/subagents/replay-safety.ts +0 -67
- package/src/integrations/subagents/session-diagnostics.ts +0 -357
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import type { ExtensionContext } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import { loadAgentProfile } from '../agents/profile.ts';
|
|
2
3
|
import type { LoadedWorkflow, WorkflowStep } from '../config/types.ts';
|
|
3
4
|
import type { WorkflowRun } from '../engine/state.ts';
|
|
4
5
|
import { allowedOutcomes } from '../engine/transitions.ts';
|
|
5
6
|
import { digest } from '../digest.ts';
|
|
6
|
-
import { deriveSubagentSessionRoot } from '../integrations/subagents/diagnostics.ts';
|
|
7
7
|
import {
|
|
8
8
|
encodeChildPolicy,
|
|
9
9
|
type ChildStepPolicy,
|
|
10
10
|
type SubagentDelegationRequest,
|
|
11
11
|
} from '../integrations/subagents/protocol.ts';
|
|
12
|
-
import {
|
|
13
|
-
import { WORKFLOW_COMPLETION_PARAMETERS } from '../runtime/completion-tool.ts';
|
|
14
|
-
import { MAX_DELEGATION_RECOVERY_ATTEMPTS } from './delegation-retry-policy.ts';
|
|
12
|
+
import { buildDelegatedStepTask } from '../prompt.ts';
|
|
15
13
|
import type { WorkflowHarnessDependencies } from './dependencies.ts';
|
|
16
|
-
import type { ActiveDelegation
|
|
14
|
+
import type { ActiveDelegation } from './types.ts';
|
|
17
15
|
|
|
18
16
|
type DelegationPlanInput = {
|
|
19
17
|
workflow: LoadedWorkflow;
|
|
@@ -21,7 +19,6 @@ type DelegationPlanInput = {
|
|
|
21
19
|
step: WorkflowStep;
|
|
22
20
|
sessionEpoch: number;
|
|
23
21
|
latestContext: ExtensionContext | undefined;
|
|
24
|
-
recovery: DelegationRecovery | undefined;
|
|
25
22
|
};
|
|
26
23
|
|
|
27
24
|
export type DelegationPlan =
|
|
@@ -35,13 +32,6 @@ export type DelegationPlan =
|
|
|
35
32
|
request: SubagentDelegationRequest;
|
|
36
33
|
};
|
|
37
34
|
|
|
38
|
-
function delegationTranscriptBinding(
|
|
39
|
-
requestId: string,
|
|
40
|
-
policyDigest: string,
|
|
41
|
-
): string {
|
|
42
|
-
return `<pi-workflows-delegation-binding-v1>${requestId}:${policyDigest}</pi-workflows-delegation-binding-v1>`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
35
|
/**
|
|
46
36
|
* Creates one immutable subagent request from workflow state and injected
|
|
47
37
|
* identity/workspace effects.
|
|
@@ -55,12 +45,21 @@ export function createDelegationPlan(
|
|
|
55
45
|
| 'resolveWorkspaceDirectory'
|
|
56
46
|
>,
|
|
57
47
|
): DelegationPlan {
|
|
58
|
-
const { workflow, run, step, latestContext
|
|
59
|
-
const
|
|
60
|
-
if (!
|
|
48
|
+
const { workflow, run, step, latestContext } = input;
|
|
49
|
+
const agent = step.agent?.name;
|
|
50
|
+
if (!agent) {
|
|
51
|
+
return {
|
|
52
|
+
kind: 'invalid',
|
|
53
|
+
reason: `Step "${run.currentStepId}" has no agent profile`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
let agentProfile;
|
|
57
|
+
try {
|
|
58
|
+
agentProfile = loadAgentProfile(agent);
|
|
59
|
+
} catch (error) {
|
|
61
60
|
return {
|
|
62
61
|
kind: 'invalid',
|
|
63
|
-
reason:
|
|
62
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
64
63
|
};
|
|
65
64
|
}
|
|
66
65
|
if (!run.cwd) {
|
|
@@ -160,7 +159,7 @@ export function createDelegationPlan(
|
|
|
160
159
|
const policyDigest = digest({
|
|
161
160
|
version: 1,
|
|
162
161
|
requestId,
|
|
163
|
-
agent
|
|
162
|
+
agent,
|
|
164
163
|
runId: run.runId,
|
|
165
164
|
stepId: run.currentStepId,
|
|
166
165
|
stepDigest: run.currentStepDigest,
|
|
@@ -174,7 +173,7 @@ export function createDelegationPlan(
|
|
|
174
173
|
const policy: ChildStepPolicy = {
|
|
175
174
|
version: 1,
|
|
176
175
|
requestId,
|
|
177
|
-
agent
|
|
176
|
+
agent,
|
|
178
177
|
workflowId: workflow.definition.id,
|
|
179
178
|
runId: run.runId,
|
|
180
179
|
stepId: run.currentStepId,
|
|
@@ -195,22 +194,7 @@ export function createDelegationPlan(
|
|
|
195
194
|
...(step.gate ? { gateSubmitOutcome: step.gate.submitOutcome } : {}),
|
|
196
195
|
...(step.workspace ? { workspace: structuredClone(step.workspace) } : {}),
|
|
197
196
|
};
|
|
198
|
-
const
|
|
199
|
-
latestContext.sessionManager.getSessionFile(),
|
|
200
|
-
);
|
|
201
|
-
const transcriptTask = [
|
|
202
|
-
buildDelegatedStepTask(workflow, run, ''),
|
|
203
|
-
delegationTranscriptBinding(requestId, policyDigest),
|
|
204
|
-
...(recovery
|
|
205
|
-
? [
|
|
206
|
-
automaticRecoveryTask(
|
|
207
|
-
recovery.failures.map(({ reason }) => reason),
|
|
208
|
-
recovery.attempt,
|
|
209
|
-
MAX_DELEGATION_RECOVERY_ATTEMPTS,
|
|
210
|
-
),
|
|
211
|
-
]
|
|
212
|
-
: []),
|
|
213
|
-
].join('\n\n');
|
|
197
|
+
const task = buildDelegatedStepTask(workflow, run, '');
|
|
214
198
|
const active: ActiveDelegation = {
|
|
215
199
|
requestId,
|
|
216
200
|
runId: run.runId,
|
|
@@ -219,56 +203,18 @@ export function createDelegationPlan(
|
|
|
219
203
|
sessionEpoch: input.sessionEpoch,
|
|
220
204
|
resultDirectory: workspace.resultDirectory,
|
|
221
205
|
policy,
|
|
222
|
-
transcriptTask,
|
|
223
|
-
agent
|
|
224
|
-
...(trustedSessionRoot ? { trustedSessionRoot } : {}),
|
|
225
|
-
broadRecoveryAuthorized: subagent.retryToolFailures,
|
|
226
|
-
recoveryAttemptCount: recovery?.attempt ?? 0,
|
|
227
|
-
recoveryFailures: recovery?.failures ?? [],
|
|
206
|
+
transcriptTask: task,
|
|
207
|
+
agent,
|
|
228
208
|
};
|
|
229
209
|
const request: SubagentDelegationRequest = {
|
|
230
210
|
version: 1,
|
|
231
211
|
requestId,
|
|
232
|
-
agent
|
|
233
|
-
task: `${encodeChildPolicy(policy)}\n\n${
|
|
234
|
-
// Workflow steps are isolation boundaries. Parent/sibling transcripts are
|
|
235
|
-
// replaced by the explicit workflow handoff.
|
|
236
|
-
context: 'fresh',
|
|
212
|
+
agent,
|
|
213
|
+
task: `${encodeChildPolicy(policy)}\n\n${task}`,
|
|
237
214
|
cwd: delegationCwd,
|
|
238
|
-
timeoutMs:
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
output: false,
|
|
242
|
-
// TypeBox schemas are structurally JSON objects, but the upstream contract
|
|
243
|
-
// exposes a generic record.
|
|
244
|
-
outputSchema: WORKFLOW_COMPLETION_PARAMETERS as unknown as Record<
|
|
245
|
-
string,
|
|
246
|
-
unknown
|
|
247
|
-
>,
|
|
248
|
-
agentContract: { version: 1 },
|
|
249
|
-
artifacts: subagent.artifacts,
|
|
250
|
-
...(subagent.model ? { model: subagent.model } : {}),
|
|
251
|
-
...(subagent.turnBudget
|
|
252
|
-
? { turnBudget: structuredClone(subagent.turnBudget) }
|
|
253
|
-
: {}),
|
|
254
|
-
...(subagent.toolBudget
|
|
255
|
-
? {
|
|
256
|
-
toolBudget: {
|
|
257
|
-
hard: subagent.toolBudget.hard,
|
|
258
|
-
...(subagent.toolBudget.soft === undefined
|
|
259
|
-
? {}
|
|
260
|
-
: { soft: subagent.toolBudget.soft }),
|
|
261
|
-
...(subagent.toolBudget.block === undefined
|
|
262
|
-
? {}
|
|
263
|
-
: {
|
|
264
|
-
block:
|
|
265
|
-
subagent.toolBudget.block === '*'
|
|
266
|
-
? '*'
|
|
267
|
-
: [...subagent.toolBudget.block],
|
|
268
|
-
}),
|
|
269
|
-
},
|
|
270
|
-
}
|
|
271
|
-
: {}),
|
|
215
|
+
timeoutMs: 900_000,
|
|
216
|
+
...(agentProfile.model ? { model: agentProfile.model } : {}),
|
|
217
|
+
...(agentProfile.thinking ? { thinking: agentProfile.thinking } : {}),
|
|
272
218
|
};
|
|
273
219
|
|
|
274
220
|
return { kind: 'ready', active, request };
|
|
@@ -4,13 +4,10 @@ import {
|
|
|
4
4
|
type SubagentDelegationUpdate,
|
|
5
5
|
} from '../integrations/subagents/protocol.ts';
|
|
6
6
|
import { advanceRun } from '../engine/transitions.ts';
|
|
7
|
-
import {
|
|
8
|
-
attachSubagentTranscript,
|
|
9
|
-
recordCurrentStepResult,
|
|
10
|
-
} from '../engine/step-trace.ts';
|
|
7
|
+
import { recordCurrentStepResult } from '../engine/step-trace.ts';
|
|
11
8
|
import type { WorkflowStepResult } from '../runtime/step-result.ts';
|
|
12
9
|
import type { HarnessActionContext as FullHarnessActionContext } from './action-context.ts';
|
|
13
|
-
import type { ActiveDelegation
|
|
10
|
+
import type { ActiveDelegation } from './types.ts';
|
|
14
11
|
import { resolveStepEffects } from './step-effects.ts';
|
|
15
12
|
|
|
16
13
|
type HarnessActionContext = Pick<
|
|
@@ -18,7 +15,6 @@ type HarnessActionContext = Pick<
|
|
|
18
15
|
| 'activeDelegation'
|
|
19
16
|
| 'catalog'
|
|
20
17
|
| 'cleanupDelegation'
|
|
21
|
-
| 'delegationFailures'
|
|
22
18
|
| 'dependencies'
|
|
23
19
|
| 'finishDelegation'
|
|
24
20
|
| 'isSessionActive'
|
|
@@ -27,7 +23,6 @@ type HarnessActionContext = Pick<
|
|
|
27
23
|
| 'pauseForDelegationFailure'
|
|
28
24
|
| 'releaseMainAfterCancellation'
|
|
29
25
|
| 'retainUnconfirmedDelegation'
|
|
30
|
-
| 'retryDelegationAfterFailure'
|
|
31
26
|
| 'run'
|
|
32
27
|
| 'sessionEpoch'
|
|
33
28
|
| 'settleAfterTransition'
|
|
@@ -70,11 +65,22 @@ function handleDelegationUpdate(
|
|
|
70
65
|
): void {
|
|
71
66
|
if (this.activeDelegation !== active) return;
|
|
72
67
|
const progress = [
|
|
68
|
+
update.activity,
|
|
73
69
|
update.currentTool ? `tool ${update.currentTool}` : undefined,
|
|
74
70
|
update.toolCount !== undefined ? `${update.toolCount} calls` : undefined,
|
|
75
71
|
update.tokens !== undefined ? `${update.tokens} tokens` : undefined,
|
|
76
72
|
].filter((part): part is string => part !== undefined);
|
|
77
73
|
active.progress = progress.join(', ') || 'running';
|
|
74
|
+
if (update.detail) {
|
|
75
|
+
const previous = active.activityLog ?? [];
|
|
76
|
+
const replacesPreviousResponse =
|
|
77
|
+
update.detail.startsWith('response: ') &&
|
|
78
|
+
previous.at(-1)?.startsWith('response: ');
|
|
79
|
+
active.activityLog = [
|
|
80
|
+
...(replacesPreviousResponse ? previous.slice(0, -1) : previous),
|
|
81
|
+
update.detail,
|
|
82
|
+
].slice(-8);
|
|
83
|
+
}
|
|
78
84
|
this.updateStatus();
|
|
79
85
|
}
|
|
80
86
|
|
|
@@ -128,7 +134,6 @@ async function finishDelegation(
|
|
|
128
134
|
return;
|
|
129
135
|
}
|
|
130
136
|
this.activeDelegation = undefined;
|
|
131
|
-
let terminalFailure: DelegationFailureDetails | undefined;
|
|
132
137
|
let cleanupAttempted = false;
|
|
133
138
|
|
|
134
139
|
try {
|
|
@@ -144,80 +149,15 @@ async function finishDelegation(
|
|
|
144
149
|
return;
|
|
145
150
|
}
|
|
146
151
|
const terminalAt = this.dependencies.now();
|
|
147
|
-
if (
|
|
148
|
-
response.requestId === active.requestId &&
|
|
149
|
-
(response.agent === undefined || response.agent === active.agent) &&
|
|
150
|
-
response.sessionFile &&
|
|
151
|
-
active.trustedSessionRoot &&
|
|
152
|
-
typeof response.runId === 'string' &&
|
|
153
|
-
response.runId &&
|
|
154
|
-
response.childIndex === 0
|
|
155
|
-
) {
|
|
156
|
-
try {
|
|
157
|
-
this.run = attachSubagentTranscript(
|
|
158
|
-
this.run,
|
|
159
|
-
active.requestId,
|
|
160
|
-
{
|
|
161
|
-
trustedRoot: active.trustedSessionRoot,
|
|
162
|
-
sessionFile: response.sessionFile,
|
|
163
|
-
runId: response.runId,
|
|
164
|
-
childIndex: response.childIndex,
|
|
165
|
-
},
|
|
166
|
-
terminalAt,
|
|
167
|
-
);
|
|
168
|
-
} catch {
|
|
169
|
-
// Status evidence is best-effort and cannot alter terminal handling.
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
152
|
const workflow = this.catalog.workflows.get(this.run.workflowId);
|
|
173
153
|
const step = workflow?.definition.steps[this.run.currentStepId];
|
|
174
154
|
if (!workflow || !step) {
|
|
175
155
|
throw new Error('Active workflow configuration is unavailable');
|
|
176
156
|
}
|
|
177
|
-
if (response.status
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (!transcriptAudit.verified) {
|
|
181
|
-
throw new Error(
|
|
182
|
-
`Subagent "${active.agent}" completed without a verifiable terminal transcript: ${transcriptAudit.reason}`,
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
if (transcriptAudit.warning) {
|
|
186
|
-
throw new Error(
|
|
187
|
-
`Subagent "${active.agent}" completed with an unresolved post-completion watchdog warning: ${transcriptAudit.warning}`,
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
let recoveredTerminalFailure: DelegationFailureDetails | undefined;
|
|
192
|
-
if (
|
|
193
|
-
response.status !== 'completed' ||
|
|
194
|
-
this.delegationFailures.hasContradictoryCompletion(response)
|
|
195
|
-
) {
|
|
196
|
-
const failure = await this.delegationFailures.describeDelegationFailure(
|
|
197
|
-
active,
|
|
198
|
-
response,
|
|
199
|
-
);
|
|
200
|
-
terminalFailure = failure;
|
|
201
|
-
if (
|
|
202
|
-
response.status !== 'failed' ||
|
|
203
|
-
failure.diagnostic?.completionAfterFailure !== true
|
|
204
|
-
) {
|
|
205
|
-
throw new Error(failure.reason);
|
|
206
|
-
}
|
|
207
|
-
const projectionError = this.delegationFailures.recoveredProjectionError(
|
|
208
|
-
active,
|
|
209
|
-
response,
|
|
210
|
-
failure.diagnostic,
|
|
157
|
+
if (response.status !== 'completed') {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`Workflow worker "${active.agent}" ${response.status.replaceAll('_', ' ')}${response.error ? `: ${response.error}` : ''}`,
|
|
211
160
|
);
|
|
212
|
-
if (projectionError) {
|
|
213
|
-
throw new Error(
|
|
214
|
-
this.delegationFailures.rejectedRecoveryReason(
|
|
215
|
-
failure,
|
|
216
|
-
projectionError,
|
|
217
|
-
),
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
recoveredTerminalFailure = failure;
|
|
221
161
|
}
|
|
222
162
|
const requiredSkillWarning =
|
|
223
163
|
step.requires.skills.length > 0
|
|
@@ -233,15 +173,6 @@ async function finishDelegation(
|
|
|
233
173
|
try {
|
|
234
174
|
serializedResult = await this.dependencies.readDelegatedResult(active);
|
|
235
175
|
} catch (error) {
|
|
236
|
-
if (recoveredTerminalFailure) {
|
|
237
|
-
throw new Error(
|
|
238
|
-
this.delegationFailures.rejectedRecoveryReason(
|
|
239
|
-
recoveredTerminalFailure,
|
|
240
|
-
error,
|
|
241
|
-
),
|
|
242
|
-
{ cause: error },
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
176
|
if (hasErrorCode(error, 'ENOENT')) {
|
|
246
177
|
throw new Error(
|
|
247
178
|
`Subagent "${active.agent}" completed without producing the required correlated structured_output result`,
|
|
@@ -251,49 +182,12 @@ async function finishDelegation(
|
|
|
251
182
|
throw error;
|
|
252
183
|
}
|
|
253
184
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (recoveredTerminalFailure) {
|
|
260
|
-
throw new Error(
|
|
261
|
-
this.delegationFailures.rejectedRecoveryReason(
|
|
262
|
-
recoveredTerminalFailure,
|
|
263
|
-
error,
|
|
264
|
-
),
|
|
265
|
-
{ cause: error },
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
throw error;
|
|
269
|
-
}
|
|
270
|
-
if (
|
|
271
|
-
recoveredTerminalFailure?.diagnostic &&
|
|
272
|
-
!this.delegationFailures.completionMatchesResult(
|
|
273
|
-
recoveredTerminalFailure.diagnostic,
|
|
274
|
-
result,
|
|
275
|
-
active.policy,
|
|
276
|
-
)
|
|
277
|
-
) {
|
|
278
|
-
throw new Error(
|
|
279
|
-
this.delegationFailures.rejectedRecoveryReason(
|
|
280
|
-
recoveredTerminalFailure,
|
|
281
|
-
'structured_output transcript value does not match the correlated result',
|
|
282
|
-
),
|
|
283
|
-
);
|
|
284
|
-
}
|
|
185
|
+
const rawResult: unknown = JSON.parse(serializedResult);
|
|
186
|
+
const result: WorkflowStepResult = parseDelegatedStepResult(
|
|
187
|
+
rawResult,
|
|
188
|
+
active.policy,
|
|
189
|
+
);
|
|
285
190
|
const acceptedAt = terminalAt;
|
|
286
|
-
if (recoveredTerminalFailure) {
|
|
287
|
-
const isFalsePositive =
|
|
288
|
-
recoveredTerminalFailure.diagnostic?.correlation ===
|
|
289
|
-
'successful-output-before-completion';
|
|
290
|
-
this.latestContext?.ui.notify(
|
|
291
|
-
isFalsePositive
|
|
292
|
-
? `Accepted "${active.stepId}" because the trusted child transcript proved the terminal tool error was a false positive and produced a matching structured result`
|
|
293
|
-
: `Accepted "${active.stepId}" because the child resolved an earlier tool failure and produced a valid structured result`,
|
|
294
|
-
'warning',
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
191
|
if (step.gate?.submitOutcome === result.outcome) {
|
|
298
192
|
this.run = recordCurrentStepResult(this.run, result, acceptedAt);
|
|
299
193
|
await this.submitGate(
|
|
@@ -335,14 +229,7 @@ async function finishDelegation(
|
|
|
335
229
|
const reason = error instanceof Error ? error.message : String(error);
|
|
336
230
|
cleanupAttempted = true;
|
|
337
231
|
await this.cleanupDelegation(active);
|
|
338
|
-
|
|
339
|
-
const failureSummary = terminalFailure?.error
|
|
340
|
-
? `Subagent ${terminalFailure.status.replaceAll('_', ' ')}: ${terminalFailure.error}`
|
|
341
|
-
: terminalFailure
|
|
342
|
-
? `Subagent ${terminalFailure.status.replaceAll('_', ' ')}`
|
|
343
|
-
: reason;
|
|
344
|
-
this.pauseForDelegationFailure(reason, failureSummary);
|
|
345
|
-
}
|
|
232
|
+
this.pauseForDelegationFailure(reason);
|
|
346
233
|
} finally {
|
|
347
234
|
try {
|
|
348
235
|
if (!cleanupAttempted) await this.cleanupDelegation(active);
|
|
@@ -14,11 +14,6 @@ import {
|
|
|
14
14
|
createSubagentDelegationClient,
|
|
15
15
|
type SubagentDelegationClientController,
|
|
16
16
|
} from '../integrations/subagents/client.ts';
|
|
17
|
-
import {
|
|
18
|
-
auditCompletedDelegationTranscript,
|
|
19
|
-
readDelegationReplayAudit,
|
|
20
|
-
readToolFailureDiagnostic,
|
|
21
|
-
} from '../integrations/subagents/diagnostics.ts';
|
|
22
17
|
import {
|
|
23
18
|
createMainStepRuntime,
|
|
24
19
|
type MainStepRuntimeController,
|
|
@@ -61,9 +56,6 @@ export type WorkflowHarnessDependencies = {
|
|
|
61
56
|
readonly requestPlannotatorReview: typeof requestPlannotatorReview;
|
|
62
57
|
readonly requestPlannotatorReviewStatus: typeof requestPlannotatorReviewStatus;
|
|
63
58
|
readonly requestPromptGateReview: typeof requestPromptGateReview;
|
|
64
|
-
readonly readDelegationReplayAudit: typeof readDelegationReplayAudit;
|
|
65
|
-
readonly readToolFailureDiagnostic: typeof readToolFailureDiagnostic;
|
|
66
|
-
readonly auditCompletedDelegationTranscript: typeof auditCompletedDelegationTranscript;
|
|
67
59
|
readonly showWorkflowStatus: typeof showWorkflowStatus;
|
|
68
60
|
readonly createSubagentClient: (
|
|
69
61
|
pi: ExtensionAPI,
|
|
@@ -150,11 +142,8 @@ const DEFAULT_DEPENDENCIES: WorkflowHarnessDependencies = {
|
|
|
150
142
|
requestPlannotatorReview,
|
|
151
143
|
requestPlannotatorReviewStatus,
|
|
152
144
|
requestPromptGateReview,
|
|
153
|
-
readDelegationReplayAudit,
|
|
154
|
-
readToolFailureDiagnostic,
|
|
155
|
-
auditCompletedDelegationTranscript,
|
|
156
145
|
showWorkflowStatus,
|
|
157
|
-
createSubagentClient: (
|
|
146
|
+
createSubagentClient: () => createSubagentDelegationClient(),
|
|
158
147
|
createMainStepRuntime: (pi) => createMainStepRuntime({ pi }),
|
|
159
148
|
createMutationQueue: createSerialTaskQueue,
|
|
160
149
|
flushUnwrittenSession,
|
|
@@ -63,6 +63,7 @@ function workflowStatusSnapshot(
|
|
|
63
63
|
agent: this.activeDelegation.agent,
|
|
64
64
|
requestId: this.activeDelegation.requestId,
|
|
65
65
|
progress: this.activeDelegation.progress ?? 'starting',
|
|
66
|
+
activityLog: this.activeDelegation.activityLog ?? [],
|
|
66
67
|
};
|
|
67
68
|
} else if (this.mainSteps.activeStepId) {
|
|
68
69
|
execution = { kind: 'main' };
|
|
@@ -15,7 +15,7 @@ import type { MainStepExecution } from '../runtime/main-step-runtime.ts';
|
|
|
15
15
|
import type { WorkflowStepResult } from '../runtime/step-result.ts';
|
|
16
16
|
import type { HarnessActionContext as FullHarnessActionContext } from './action-context.ts';
|
|
17
17
|
import { createDelegationPlan } from './delegation-plan.ts';
|
|
18
|
-
import type {
|
|
18
|
+
import type { MainStepIdentity } from './types.ts';
|
|
19
19
|
import { resolveStepEffects } from './step-effects.ts';
|
|
20
20
|
|
|
21
21
|
type HarnessActionContext = Pick<
|
|
@@ -50,7 +50,6 @@ export type StepExecutionActions = {
|
|
|
50
50
|
launchCurrentStep: (
|
|
51
51
|
this: HarnessActionContext,
|
|
52
52
|
workflow: LoadedWorkflow,
|
|
53
|
-
recovery?: DelegationRecovery,
|
|
54
53
|
) => void;
|
|
55
54
|
launchMainStep: (
|
|
56
55
|
this: HarnessActionContext,
|
|
@@ -87,7 +86,6 @@ export type StepExecutionActions = {
|
|
|
87
86
|
function launchCurrentStep(
|
|
88
87
|
this: HarnessActionContext,
|
|
89
88
|
workflow: LoadedWorkflow,
|
|
90
|
-
recovery?: DelegationRecovery,
|
|
91
89
|
): void {
|
|
92
90
|
const run = this.run;
|
|
93
91
|
if (
|
|
@@ -106,11 +104,6 @@ function launchCurrentStep(
|
|
|
106
104
|
);
|
|
107
105
|
return;
|
|
108
106
|
}
|
|
109
|
-
if (!step.subagent) {
|
|
110
|
-
this.launchMainStep(workflow, run, step);
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
107
|
const plan = createDelegationPlan(
|
|
115
108
|
{
|
|
116
109
|
workflow,
|
|
@@ -118,7 +111,6 @@ function launchCurrentStep(
|
|
|
118
111
|
step,
|
|
119
112
|
sessionEpoch: this.sessionEpoch,
|
|
120
113
|
latestContext: this.latestContext,
|
|
121
|
-
recovery,
|
|
122
114
|
},
|
|
123
115
|
this.dependencies,
|
|
124
116
|
);
|
|
@@ -243,6 +235,7 @@ function launchMainStep(
|
|
|
243
235
|
outcomes: allowedOutcomes(workflow, run),
|
|
244
236
|
summaryMaxChars: workflow.definition.summaryMaxChars,
|
|
245
237
|
...(step.gate ? { gateSubmitOutcome: step.gate.submitOutcome } : {}),
|
|
238
|
+
...(step.workspace ? { workspace: structuredClone(step.workspace) } : {}),
|
|
246
239
|
onTrace: (lines, context) =>
|
|
247
240
|
this.queueMainStepLog(identity, lines, context),
|
|
248
241
|
onSettled: (result, context) =>
|
package/src/harness/types.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import type { ExtensionContext } from '@earendil-works/pi-coding-agent';
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
ChildStepPolicy,
|
|
5
|
-
SubagentDelegationStatus,
|
|
6
|
-
} from '../integrations/subagents/protocol.ts';
|
|
7
|
-
import type { WorkflowHarnessDependencies } from './dependencies.ts';
|
|
2
|
+
import type { ChildStepPolicy } from '../integrations/subagents/protocol.ts';
|
|
8
3
|
|
|
9
4
|
export type ActiveDelegation = {
|
|
10
5
|
requestId: string;
|
|
@@ -16,34 +11,11 @@ export type ActiveDelegation = {
|
|
|
16
11
|
policy: ChildStepPolicy;
|
|
17
12
|
transcriptTask: string;
|
|
18
13
|
agent: string;
|
|
19
|
-
trustedSessionRoot?: string;
|
|
20
|
-
broadRecoveryAuthorized: boolean;
|
|
21
|
-
recoveryAttemptCount: number;
|
|
22
|
-
recoveryFailures: ReadonlyArray<DelegationRecoveryFailure>;
|
|
23
14
|
progress?: string;
|
|
15
|
+
activityLog?: Array<string>;
|
|
24
16
|
cancelling?: boolean;
|
|
25
17
|
};
|
|
26
18
|
|
|
27
|
-
export type DelegationRecoveryBlocker =
|
|
28
|
-
| 'cancelled'
|
|
29
|
-
| 'detached'
|
|
30
|
-
| 'inconsistent-timeout'
|
|
31
|
-
| 'interrupted'
|
|
32
|
-
| 'reported-mutation'
|
|
33
|
-
| 'stopped';
|
|
34
|
-
|
|
35
|
-
export type DelegationFailureDetails = {
|
|
36
|
-
reason: string;
|
|
37
|
-
status: SubagentDelegationStatus;
|
|
38
|
-
error?: string;
|
|
39
|
-
exitCode?: number;
|
|
40
|
-
recoveryBlocker?: DelegationRecoveryBlocker;
|
|
41
|
-
diagnostic?: Awaited<
|
|
42
|
-
ReturnType<WorkflowHarnessDependencies['readToolFailureDiagnostic']>
|
|
43
|
-
>;
|
|
44
|
-
replayAudit?: DelegationReplayAudit;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
19
|
export type MainStepIdentity = {
|
|
48
20
|
requestId: string;
|
|
49
21
|
runId: string;
|
|
@@ -65,13 +37,3 @@ export type WorkflowStartContext = {
|
|
|
65
37
|
skills: () => ReadonlyArray<{ name: string }> | undefined;
|
|
66
38
|
waitForIdle: () => Promise<void>;
|
|
67
39
|
};
|
|
68
|
-
|
|
69
|
-
export type DelegationRecoveryFailure = {
|
|
70
|
-
readonly fingerprint: string;
|
|
71
|
-
readonly reason: string;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export type DelegationRecovery = {
|
|
75
|
-
readonly attempt: number;
|
|
76
|
-
readonly failures: ReadonlyArray<DelegationRecoveryFailure>;
|
|
77
|
-
};
|
package/src/harness.ts
CHANGED
|
@@ -24,10 +24,6 @@ import {
|
|
|
24
24
|
import type { MainStepRuntimeController } from './runtime/main-step-runtime.ts';
|
|
25
25
|
import type { SerialTaskQueueController } from './runtime/serial-task-queue.ts';
|
|
26
26
|
import type { WorkflowStepResult } from './runtime/step-result.ts';
|
|
27
|
-
import {
|
|
28
|
-
createDelegationFailureActions,
|
|
29
|
-
type DelegationFailureActions,
|
|
30
|
-
} from './harness/delegation-failure.ts';
|
|
31
27
|
import { createEmptyCatalog } from './harness/catalog.ts';
|
|
32
28
|
import {
|
|
33
29
|
createWorkflowHarnessDependencies,
|
|
@@ -36,8 +32,6 @@ import {
|
|
|
36
32
|
import type {
|
|
37
33
|
ActiveDelegation,
|
|
38
34
|
ActivePromptReview,
|
|
39
|
-
DelegationRecovery,
|
|
40
|
-
DelegationFailureDetails,
|
|
41
35
|
MainStepIdentity,
|
|
42
36
|
WorkflowStartContext,
|
|
43
37
|
} from './harness/types.ts';
|
|
@@ -79,7 +73,6 @@ const CORE_ACTIONS = createCoreActions();
|
|
|
79
73
|
export class WorkflowHarness implements WorkflowCommandController {
|
|
80
74
|
private readonly pi: ExtensionAPI;
|
|
81
75
|
private readonly dependencies: WorkflowHarnessDependencies;
|
|
82
|
-
private readonly delegationFailures: DelegationFailureActions;
|
|
83
76
|
private readonly subagents: SubagentDelegationClientController;
|
|
84
77
|
private readonly mainSteps: MainStepRuntimeController;
|
|
85
78
|
private catalog: WorkflowCatalog = createEmptyCatalog();
|
|
@@ -149,10 +142,8 @@ export class WorkflowHarness implements WorkflowCommandController {
|
|
|
149
142
|
LIFECYCLE_ACTIONS.registerLifecycle;
|
|
150
143
|
private readonly registerPolicy: () => void =
|
|
151
144
|
LIFECYCLE_ACTIONS.registerPolicy;
|
|
152
|
-
private readonly launchCurrentStep: (
|
|
153
|
-
|
|
154
|
-
recovery?: DelegationRecovery,
|
|
155
|
-
) => void = STEP_EXECUTION_ACTIONS.launchCurrentStep;
|
|
145
|
+
private readonly launchCurrentStep: (workflow: LoadedWorkflow) => void =
|
|
146
|
+
STEP_EXECUTION_ACTIONS.launchCurrentStep;
|
|
156
147
|
private readonly launchMainStep: (
|
|
157
148
|
workflow: LoadedWorkflow,
|
|
158
149
|
run: WorkflowRun,
|
|
@@ -200,11 +191,6 @@ export class WorkflowHarness implements WorkflowCommandController {
|
|
|
200
191
|
private readonly cleanupDelegation: (
|
|
201
192
|
active: ActiveDelegation,
|
|
202
193
|
) => Promise<void> = DELEGATION_CONTROL_ACTIONS.cleanupDelegation;
|
|
203
|
-
private readonly retryDelegationAfterFailure: (
|
|
204
|
-
active: ActiveDelegation,
|
|
205
|
-
failure: DelegationFailureDetails | undefined,
|
|
206
|
-
reason: string,
|
|
207
|
-
) => boolean = DELEGATION_CONTROL_ACTIONS.retryDelegationAfterFailure;
|
|
208
194
|
private readonly pauseForDelegationFailure: (
|
|
209
195
|
reason: string,
|
|
210
196
|
failureSummary?: string,
|
|
@@ -298,7 +284,6 @@ export class WorkflowHarness implements WorkflowCommandController {
|
|
|
298
284
|
) {
|
|
299
285
|
this.pi = pi;
|
|
300
286
|
this.dependencies = createWorkflowHarnessDependencies(dependencyOverrides);
|
|
301
|
-
this.delegationFailures = createDelegationFailureActions(this.dependencies);
|
|
302
287
|
this.statusShortcut = statusShortcut;
|
|
303
288
|
this.statusShortcutLabel = formatShortcutLabel(statusShortcut);
|
|
304
289
|
this.subagents = this.dependencies.createSubagentClient(pi);
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,6 @@ import type { KeyId } from '@earendil-works/pi-tui';
|
|
|
3
3
|
import { defaultUserWorkflowDirectory, loadSettings } from './config/load.ts';
|
|
4
4
|
import { WorkflowHarness } from './harness.ts';
|
|
5
5
|
import { registerSubagentChildRuntime } from './integrations/subagents/child-runtime.ts';
|
|
6
|
-
import { isSubagentRuntimeName } from './integrations/subagents/protocol.ts';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Asynchronous entry point accepted by Pi's extension loader.
|
|
@@ -25,7 +24,6 @@ export type PiWorkflowsExtensionDependencies = {
|
|
|
25
24
|
readonly loadSettings: typeof loadSettings;
|
|
26
25
|
readonly userWorkflowDirectory: () => string;
|
|
27
26
|
readonly runtimeEnvironment: () => PiWorkflowsRuntimeEnvironment;
|
|
28
|
-
readonly isSubagentRuntimeName: typeof isSubagentRuntimeName;
|
|
29
27
|
readonly registerChildRuntime: (pi: ExtensionAPI, childAgent: string) => void;
|
|
30
28
|
readonly createHarness: (pi: ExtensionAPI, statusShortcut: KeyId) => void;
|
|
31
29
|
};
|
|
@@ -34,10 +32,9 @@ const DEFAULT_DEPENDENCIES = {
|
|
|
34
32
|
loadSettings,
|
|
35
33
|
userWorkflowDirectory: defaultUserWorkflowDirectory,
|
|
36
34
|
runtimeEnvironment: (): PiWorkflowsRuntimeEnvironment => ({
|
|
37
|
-
isSubagentChild: process.env.
|
|
38
|
-
childAgent: process.env.
|
|
35
|
+
isSubagentChild: process.env.PI_WORKFLOWS_CHILD === '1',
|
|
36
|
+
childAgent: process.env.PI_WORKFLOWS_CHILD_AGENT?.trim(),
|
|
39
37
|
}),
|
|
40
|
-
isSubagentRuntimeName,
|
|
41
38
|
registerChildRuntime: (pi, childAgent): void => {
|
|
42
39
|
registerSubagentChildRuntime(pi, { childAgent });
|
|
43
40
|
},
|
|
@@ -59,10 +56,8 @@ export function createPiWorkflowsExtension(
|
|
|
59
56
|
return async (pi): Promise<void> => {
|
|
60
57
|
const environment = dependencies.runtimeEnvironment();
|
|
61
58
|
|
|
62
|
-
if (environment.isSubagentChild) {
|
|
63
|
-
|
|
64
|
-
dependencies.registerChildRuntime(pi, environment.childAgent);
|
|
65
|
-
}
|
|
59
|
+
if (environment.isSubagentChild && environment.childAgent) {
|
|
60
|
+
dependencies.registerChildRuntime(pi, environment.childAgent);
|
|
66
61
|
return;
|
|
67
62
|
}
|
|
68
63
|
|