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
- console.error(`\nStep ${stepNumber} failed: ${err.message}`);
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);
@@ -12,6 +12,8 @@ export interface PipelineStep {
12
12
  agent: string;
13
13
  label: string;
14
14
  deliverFrom?: string;
15
+ onFail?: 'skip' | 'retry' | 'abort';
16
+ maxRetries?: number;
15
17
  }
16
18
  export interface ScheduleConfig {
17
19
  enabled: boolean;
@@ -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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expxagents",
3
- "version": "0.24.1",
3
+ "version": "0.24.2",
4
4
  "description": "Multi-agent orchestration platform for AI squads",
5
5
  "author": "ExpxAgents",
6
6
  "license": "MIT",