expxagents 0.24.0 → 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.
|
@@ -19,7 +19,14 @@ For each pipeline step:
|
|
|
19
19
|
|
|
20
20
|
#### Agent Steps (type: agent or default)
|
|
21
21
|
|
|
22
|
-
1. **
|
|
22
|
+
1. **Update state.json BEFORE executing:**
|
|
23
|
+
- Set `status` → `running`
|
|
24
|
+
- Set `step.current` → current step number (1-based)
|
|
25
|
+
- Set `step.label` → step label from squad.yaml
|
|
26
|
+
- Set agent `status` → `working`
|
|
27
|
+
- **CRITICAL: Set agent `stepIndex`** → current step number (1-based). This triggers the dashboard kanban to move the previous task to "done" and the current task to "executing". Without this update, kanban tasks won't transition.
|
|
28
|
+
- Set agent `stepLabel` → step label
|
|
29
|
+
- Write state.json immediately (before doing any work)
|
|
23
30
|
2. **Build context:**
|
|
24
31
|
- Agent prompt from `agents/<agent-id>.md`
|
|
25
32
|
- Previous agent output (if `deliverFrom` is set)
|
|
@@ -30,12 +37,41 @@ For each pipeline step:
|
|
|
30
37
|
- `inline` execution: run in current context
|
|
31
38
|
- `subagent` execution: spawn background process
|
|
32
39
|
4. **Save output** to `output/step-XX.md`
|
|
33
|
-
5. **Update state.json:**
|
|
40
|
+
5. **Update state.json AFTER executing:**
|
|
34
41
|
- Agent status → `delivering` (if handoff) or `done`
|
|
35
42
|
- Handoff message
|
|
36
|
-
-
|
|
43
|
+
- Write state.json immediately
|
|
37
44
|
6. **Handoff delay** (2 seconds for dashboard animation)
|
|
38
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
|
+
|
|
39
75
|
#### Checkpoint Steps (type: checkpoint)
|
|
40
76
|
|
|
41
77
|
1. **Set status** → `checkpoint` in state.json
|
|
@@ -50,28 +86,50 @@ For each pipeline step:
|
|
|
50
86
|
|
|
51
87
|
### State Management (state.json)
|
|
52
88
|
|
|
89
|
+
**IMPORTANT:** The dashboard file watcher monitors state.json for changes. When agent `stepIndex` changes, it automatically updates the kanban board (previous step → done, current step → executing). You MUST update `stepIndex` on each agent before starting their step.
|
|
90
|
+
|
|
53
91
|
```json
|
|
54
92
|
{
|
|
55
93
|
"squad": "<squad-code>",
|
|
56
94
|
"status": "running",
|
|
57
|
-
"step": { "current":
|
|
95
|
+
"step": { "current": 2, "total": 5, "label": "step-02-writing" },
|
|
58
96
|
"agents": [
|
|
59
97
|
{
|
|
60
98
|
"id": "researcher",
|
|
61
99
|
"name": "Angela Researcher",
|
|
62
100
|
"icon": "magnifying-glass",
|
|
63
|
-
"status": "
|
|
101
|
+
"status": "done",
|
|
102
|
+
"stepIndex": 1,
|
|
103
|
+
"stepLabel": "step-01-research",
|
|
64
104
|
"desk": { "col": 1, "row": 1 },
|
|
105
|
+
"deliverTo": "writer",
|
|
106
|
+
"message": "Research complete"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"id": "writer",
|
|
110
|
+
"name": "Bruno Writer",
|
|
111
|
+
"icon": "pen",
|
|
112
|
+
"status": "working",
|
|
113
|
+
"stepIndex": 2,
|
|
114
|
+
"stepLabel": "step-02-writing",
|
|
115
|
+
"desk": { "col": 2, "row": 1 },
|
|
65
116
|
"deliverTo": null,
|
|
66
117
|
"message": ""
|
|
67
118
|
}
|
|
68
119
|
],
|
|
69
120
|
"handoff": null,
|
|
70
121
|
"startedAt": "2026-03-13T00:00:00Z",
|
|
71
|
-
"updatedAt": "2026-03-13T00:00:
|
|
122
|
+
"updatedAt": "2026-03-13T00:00:05Z"
|
|
72
123
|
}
|
|
73
124
|
```
|
|
74
125
|
|
|
126
|
+
**Key fields for kanban integration:**
|
|
127
|
+
- `step.current` — global step number (1-based)
|
|
128
|
+
- `agent.stepIndex` — the step number this agent is currently on (1-based). Changes to this field trigger kanban task transitions
|
|
129
|
+
- `agent.stepLabel` — label of the current step
|
|
130
|
+
- `agent.status` — `working` | `delivering` | `done` | `idle` | `checkpoint`
|
|
131
|
+
```
|
|
132
|
+
|
|
75
133
|
### Output Versioning
|
|
76
134
|
|
|
77
135
|
- First run: `output/v1/`
|
|
@@ -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,
|