expxagents 0.24.1 → 0.24.2
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.
|
@@ -43,6 +43,35 @@ For each pipeline step:
|
|
|
43
43
|
- Write state.json immediately
|
|
44
44
|
6. **Handoff delay** (2 seconds for dashboard animation)
|
|
45
45
|
|
|
46
|
+
#### Step Failure Handling (on_fail)
|
|
47
|
+
|
|
48
|
+
Each step can define `on_fail` in squad.yaml:
|
|
49
|
+
|
|
50
|
+
- `abort` (default) — stop the pipeline immediately
|
|
51
|
+
- `skip` — log the error, mark agent as done with skip message, continue to next step
|
|
52
|
+
- `retry` — retry the step up to `max_retries` times (default: 2), then abort
|
|
53
|
+
|
|
54
|
+
Example in squad.yaml:
|
|
55
|
+
```yaml
|
|
56
|
+
pipeline:
|
|
57
|
+
steps:
|
|
58
|
+
- id: step-01
|
|
59
|
+
agent: transcriber
|
|
60
|
+
label: Extract transcript
|
|
61
|
+
on_fail: skip # continue pipeline if transcript fails
|
|
62
|
+
- id: step-02
|
|
63
|
+
agent: writer
|
|
64
|
+
label: Write article
|
|
65
|
+
on_fail: retry
|
|
66
|
+
max_retries: 3 # try up to 4 times total
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
When a step is skipped:
|
|
70
|
+
1. Set agent status → `done`
|
|
71
|
+
2. Set agent message → "Skipped: [error reason]"
|
|
72
|
+
3. Write state.json
|
|
73
|
+
4. Continue to next step (the next agent may need to handle missing input)
|
|
74
|
+
|
|
46
75
|
#### Checkpoint Steps (type: checkpoint)
|
|
47
76
|
|
|
48
77
|
1. **Set status** → `checkpoint` in state.json
|
|
@@ -178,6 +178,7 @@ export async function runCommand(name) {
|
|
|
178
178
|
console.log(`Pipeline: ${config.squad.pipeline.steps.length} steps\n`);
|
|
179
179
|
}
|
|
180
180
|
const steps = config.squad.pipeline.steps;
|
|
181
|
+
const stepAttempts = new Map();
|
|
181
182
|
for (let i = 0; i < steps.length; i++) {
|
|
182
183
|
const step = steps[i];
|
|
183
184
|
const stepNumber = i + 1;
|
|
@@ -210,7 +211,32 @@ export async function runCommand(name) {
|
|
|
210
211
|
output = await runWithProvider(prompt, agent, squadDir, spawnClaudeCode);
|
|
211
212
|
}
|
|
212
213
|
catch (err) {
|
|
213
|
-
|
|
214
|
+
const failAction = step.onFail ?? 'abort';
|
|
215
|
+
console.error(`\nStep ${stepNumber} failed: ${err.message} [on_fail: ${failAction}]`);
|
|
216
|
+
if (failAction === 'retry') {
|
|
217
|
+
const maxRetries = step.maxRetries ?? 2;
|
|
218
|
+
const attempt = stepAttempts.get(step.id) ?? 0;
|
|
219
|
+
if (attempt < maxRetries) {
|
|
220
|
+
stepAttempts.set(step.id, attempt + 1);
|
|
221
|
+
console.log(`\nRetrying step ${stepNumber} (attempt ${attempt + 2}/${maxRetries + 1})...\n`);
|
|
222
|
+
state = updateAgentStatus(state, step.agent, 'idle');
|
|
223
|
+
writeState(squadDir, state);
|
|
224
|
+
i--;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
console.error(`\nStep ${stepNumber} failed after ${maxRetries + 1} attempts. Aborting.`);
|
|
228
|
+
}
|
|
229
|
+
if (failAction === 'skip') {
|
|
230
|
+
console.log(`\nSkipping step ${stepNumber} (on_fail: skip). Continuing pipeline.\n`);
|
|
231
|
+
state = updateAgentStatus(state, step.agent, 'done');
|
|
232
|
+
state = {
|
|
233
|
+
...state,
|
|
234
|
+
agents: state.agents.map((a) => a.id === step.agent ? { ...a, message: `Skipped: ${err.message}` } : a),
|
|
235
|
+
};
|
|
236
|
+
writeState(squadDir, state);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
// Default: abort
|
|
214
240
|
state = updateAgentStatus(state, step.agent, 'idle');
|
|
215
241
|
state = setSquadStatus(state, 'idle');
|
|
216
242
|
writeState(squadDir, state);
|
|
@@ -122,6 +122,14 @@ export function loadSquad(squadDir) {
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
const mcps = Array.isArray(squad.mcps) ? squad.mcps : [];
|
|
125
|
+
const mappedSteps = steps.map((s) => ({
|
|
126
|
+
id: s.id,
|
|
127
|
+
agent: s.agent,
|
|
128
|
+
label: s.label,
|
|
129
|
+
deliverFrom: s.deliverFrom ?? s.deliver_from,
|
|
130
|
+
onFail: ['skip', 'retry', 'abort'].includes(s.on_fail) ? s.on_fail : undefined,
|
|
131
|
+
maxRetries: typeof s.max_retries === 'number' ? s.max_retries : undefined,
|
|
132
|
+
}));
|
|
125
133
|
return {
|
|
126
134
|
squad: {
|
|
127
135
|
code: squad.code,
|
|
@@ -134,7 +142,7 @@ export function loadSquad(squadDir) {
|
|
|
134
142
|
icon: squad.icon,
|
|
135
143
|
agents,
|
|
136
144
|
skills,
|
|
137
|
-
pipeline: { steps },
|
|
145
|
+
pipeline: { steps: mappedSteps },
|
|
138
146
|
schedule,
|
|
139
147
|
chain,
|
|
140
148
|
webhooks,
|