@renseiai/agentfactory 0.8.5 → 0.8.7
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 +2 -2
- package/dist/src/governor/decision-engine.d.ts +7 -0
- package/dist/src/governor/decision-engine.d.ts.map +1 -1
- package/dist/src/governor/decision-engine.js +59 -1
- package/dist/src/governor/governor.d.ts +5 -1
- package/dist/src/governor/governor.d.ts.map +1 -1
- package/dist/src/governor/governor.js +6 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/orchestrator/activity-emitter.d.ts +3 -3
- package/dist/src/orchestrator/activity-emitter.d.ts.map +1 -1
- package/dist/src/orchestrator/activity-emitter.js +1 -1
- package/dist/src/orchestrator/detect-work-type.test.js +25 -16
- package/dist/src/orchestrator/index.d.ts +4 -0
- package/dist/src/orchestrator/index.d.ts.map +1 -1
- package/dist/src/orchestrator/index.js +1 -0
- package/dist/src/orchestrator/issue-tracker-client.d.ts +103 -0
- package/dist/src/orchestrator/issue-tracker-client.d.ts.map +1 -0
- package/dist/src/orchestrator/issue-tracker-client.js +8 -0
- package/dist/src/orchestrator/log-analyzer.d.ts +19 -4
- package/dist/src/orchestrator/log-analyzer.d.ts.map +1 -1
- package/dist/src/orchestrator/log-analyzer.js +26 -50
- package/dist/src/orchestrator/orchestrator-utils.test.js +3 -0
- package/dist/src/orchestrator/orchestrator.d.ts +4 -2
- package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/src/orchestrator/orchestrator.js +193 -115
- package/dist/src/orchestrator/parse-work-result.d.ts +1 -1
- package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -1
- package/dist/src/orchestrator/parse-work-result.js +3 -1
- package/dist/src/orchestrator/parse-work-result.test.js +9 -0
- package/dist/src/orchestrator/session-logger.d.ts +1 -1
- package/dist/src/orchestrator/session-logger.d.ts.map +1 -1
- package/dist/src/orchestrator/state-recovery.d.ts +1 -1
- package/dist/src/orchestrator/state-recovery.d.ts.map +1 -1
- package/dist/src/orchestrator/state-recovery.js +1 -0
- package/dist/src/orchestrator/state-types.d.ts +1 -1
- package/dist/src/orchestrator/state-types.d.ts.map +1 -1
- package/dist/src/orchestrator/types.d.ts +22 -2
- package/dist/src/orchestrator/types.d.ts.map +1 -1
- package/dist/src/orchestrator/work-types.d.ts +50 -0
- package/dist/src/orchestrator/work-types.d.ts.map +1 -0
- package/dist/src/orchestrator/work-types.js +20 -0
- package/dist/src/templates/registry.d.ts +1 -1
- package/dist/src/templates/registry.test.js +2 -2
- package/dist/src/templates/renderer.d.ts +1 -1
- package/dist/src/templates/types.d.ts +4 -2
- package/dist/src/templates/types.d.ts.map +1 -1
- package/dist/src/templates/types.js +1 -0
- package/dist/src/templates/types.test.js +4 -3
- package/dist/src/tools/index.d.ts +0 -3
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +0 -2
- package/dist/src/workflow/index.d.ts +14 -0
- package/dist/src/workflow/index.d.ts.map +1 -0
- package/dist/src/workflow/index.js +10 -0
- package/dist/src/workflow/transition-engine.d.ts +44 -0
- package/dist/src/workflow/transition-engine.d.ts.map +1 -0
- package/dist/src/workflow/transition-engine.js +106 -0
- package/dist/src/workflow/transition-engine.test.d.ts +2 -0
- package/dist/src/workflow/transition-engine.test.d.ts.map +1 -0
- package/dist/src/workflow/transition-engine.test.js +313 -0
- package/dist/src/workflow/workflow-loader.d.ts +21 -0
- package/dist/src/workflow/workflow-loader.d.ts.map +1 -0
- package/dist/src/workflow/workflow-loader.js +40 -0
- package/dist/src/workflow/workflow-loader.test.d.ts +2 -0
- package/dist/src/workflow/workflow-loader.test.d.ts.map +1 -0
- package/dist/src/workflow/workflow-loader.test.js +134 -0
- package/dist/src/workflow/workflow-registry.d.ts +56 -0
- package/dist/src/workflow/workflow-registry.d.ts.map +1 -0
- package/dist/src/workflow/workflow-registry.js +107 -0
- package/dist/src/workflow/workflow-registry.test.d.ts +2 -0
- package/dist/src/workflow/workflow-registry.test.d.ts.map +1 -0
- package/dist/src/workflow/workflow-registry.test.js +201 -0
- package/dist/src/workflow/workflow-types.d.ts +269 -0
- package/dist/src/workflow/workflow-types.d.ts.map +1 -0
- package/dist/src/workflow/workflow-types.js +88 -0
- package/dist/src/workflow/workflow-types.test.d.ts +2 -0
- package/dist/src/workflow/workflow-types.test.d.ts.map +1 -0
- package/dist/src/workflow/workflow-types.test.js +440 -0
- package/package.json +3 -4
- package/dist/src/linear-cli.d.ts +0 -38
- package/dist/src/linear-cli.d.ts.map +0 -1
- package/dist/src/linear-cli.js +0 -674
- package/dist/src/tools/linear-runner.d.ts +0 -34
- package/dist/src/tools/linear-runner.d.ts.map +0 -1
- package/dist/src/tools/linear-runner.js +0 -700
- package/dist/src/tools/plugins/linear.d.ts +0 -9
- package/dist/src/tools/plugins/linear.d.ts.map +0 -1
- package/dist/src/tools/plugins/linear.js +0 -138
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-types.test.d.ts","sourceRoot":"","sources":["../../../src/workflow/workflow-types.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { PhaseDefinitionSchema, TransitionDefinitionSchema, EscalationLadderRungSchema, EscalationConfigSchema, GateDefinitionSchema, ParallelismGroupDefinitionSchema, WorkflowDefinitionSchema, validateWorkflowDefinition, } from './workflow-types.js';
|
|
3
|
+
describe('PhaseDefinitionSchema', () => {
|
|
4
|
+
it('validates a minimal phase', () => {
|
|
5
|
+
const result = PhaseDefinitionSchema.parse({
|
|
6
|
+
name: 'development',
|
|
7
|
+
template: 'development',
|
|
8
|
+
});
|
|
9
|
+
expect(result.name).toBe('development');
|
|
10
|
+
expect(result.template).toBe('development');
|
|
11
|
+
expect(result.description).toBeUndefined();
|
|
12
|
+
expect(result.variants).toBeUndefined();
|
|
13
|
+
});
|
|
14
|
+
it('validates a phase with all optional fields', () => {
|
|
15
|
+
const result = PhaseDefinitionSchema.parse({
|
|
16
|
+
name: 'refinement',
|
|
17
|
+
description: 'Address rejection feedback',
|
|
18
|
+
template: 'refinement',
|
|
19
|
+
variants: {
|
|
20
|
+
'context-enriched': 'refinement-context-enriched',
|
|
21
|
+
'decompose': 'refinement-decompose',
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
expect(result.variants).toEqual({
|
|
25
|
+
'context-enriched': 'refinement-context-enriched',
|
|
26
|
+
'decompose': 'refinement-decompose',
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
it('rejects phase without name', () => {
|
|
30
|
+
expect(() => PhaseDefinitionSchema.parse({
|
|
31
|
+
template: 'development',
|
|
32
|
+
})).toThrow();
|
|
33
|
+
});
|
|
34
|
+
it('rejects phase without template', () => {
|
|
35
|
+
expect(() => PhaseDefinitionSchema.parse({
|
|
36
|
+
name: 'development',
|
|
37
|
+
})).toThrow();
|
|
38
|
+
});
|
|
39
|
+
it('rejects empty name', () => {
|
|
40
|
+
expect(() => PhaseDefinitionSchema.parse({
|
|
41
|
+
name: '',
|
|
42
|
+
template: 'development',
|
|
43
|
+
})).toThrow();
|
|
44
|
+
});
|
|
45
|
+
it('rejects empty template', () => {
|
|
46
|
+
expect(() => PhaseDefinitionSchema.parse({
|
|
47
|
+
name: 'development',
|
|
48
|
+
template: '',
|
|
49
|
+
})).toThrow();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('TransitionDefinitionSchema', () => {
|
|
53
|
+
it('validates a minimal transition', () => {
|
|
54
|
+
const result = TransitionDefinitionSchema.parse({
|
|
55
|
+
from: 'Backlog',
|
|
56
|
+
to: 'development',
|
|
57
|
+
});
|
|
58
|
+
expect(result.from).toBe('Backlog');
|
|
59
|
+
expect(result.to).toBe('development');
|
|
60
|
+
expect(result.condition).toBeUndefined();
|
|
61
|
+
expect(result.priority).toBeUndefined();
|
|
62
|
+
});
|
|
63
|
+
it('validates a transition with condition and priority', () => {
|
|
64
|
+
const result = TransitionDefinitionSchema.parse({
|
|
65
|
+
from: 'Icebox',
|
|
66
|
+
to: 'research',
|
|
67
|
+
condition: '{{ not researchCompleted }}',
|
|
68
|
+
priority: 10,
|
|
69
|
+
});
|
|
70
|
+
expect(result.condition).toBe('{{ not researchCompleted }}');
|
|
71
|
+
expect(result.priority).toBe(10);
|
|
72
|
+
});
|
|
73
|
+
it('rejects transition without from', () => {
|
|
74
|
+
expect(() => TransitionDefinitionSchema.parse({
|
|
75
|
+
to: 'development',
|
|
76
|
+
})).toThrow();
|
|
77
|
+
});
|
|
78
|
+
it('rejects transition without to', () => {
|
|
79
|
+
expect(() => TransitionDefinitionSchema.parse({
|
|
80
|
+
from: 'Backlog',
|
|
81
|
+
})).toThrow();
|
|
82
|
+
});
|
|
83
|
+
it('rejects empty from', () => {
|
|
84
|
+
expect(() => TransitionDefinitionSchema.parse({
|
|
85
|
+
from: '',
|
|
86
|
+
to: 'development',
|
|
87
|
+
})).toThrow();
|
|
88
|
+
});
|
|
89
|
+
it('rejects empty to', () => {
|
|
90
|
+
expect(() => TransitionDefinitionSchema.parse({
|
|
91
|
+
from: 'Backlog',
|
|
92
|
+
to: '',
|
|
93
|
+
})).toThrow();
|
|
94
|
+
});
|
|
95
|
+
it('rejects non-integer priority', () => {
|
|
96
|
+
expect(() => TransitionDefinitionSchema.parse({
|
|
97
|
+
from: 'Backlog',
|
|
98
|
+
to: 'development',
|
|
99
|
+
priority: 1.5,
|
|
100
|
+
})).toThrow();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe('EscalationLadderRungSchema', () => {
|
|
104
|
+
it('validates a ladder rung', () => {
|
|
105
|
+
const result = EscalationLadderRungSchema.parse({
|
|
106
|
+
cycle: 1,
|
|
107
|
+
strategy: 'normal',
|
|
108
|
+
});
|
|
109
|
+
expect(result.cycle).toBe(1);
|
|
110
|
+
expect(result.strategy).toBe('normal');
|
|
111
|
+
});
|
|
112
|
+
it('accepts cycle 0', () => {
|
|
113
|
+
const result = EscalationLadderRungSchema.parse({
|
|
114
|
+
cycle: 0,
|
|
115
|
+
strategy: 'normal',
|
|
116
|
+
});
|
|
117
|
+
expect(result.cycle).toBe(0);
|
|
118
|
+
});
|
|
119
|
+
it('rejects negative cycle', () => {
|
|
120
|
+
expect(() => EscalationLadderRungSchema.parse({
|
|
121
|
+
cycle: -1,
|
|
122
|
+
strategy: 'normal',
|
|
123
|
+
})).toThrow();
|
|
124
|
+
});
|
|
125
|
+
it('rejects empty strategy', () => {
|
|
126
|
+
expect(() => EscalationLadderRungSchema.parse({
|
|
127
|
+
cycle: 1,
|
|
128
|
+
strategy: '',
|
|
129
|
+
})).toThrow();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
describe('EscalationConfigSchema', () => {
|
|
133
|
+
it('validates a full escalation config', () => {
|
|
134
|
+
const result = EscalationConfigSchema.parse({
|
|
135
|
+
ladder: [
|
|
136
|
+
{ cycle: 1, strategy: 'normal' },
|
|
137
|
+
{ cycle: 2, strategy: 'context-enriched' },
|
|
138
|
+
{ cycle: 3, strategy: 'decompose' },
|
|
139
|
+
{ cycle: 4, strategy: 'escalate-human' },
|
|
140
|
+
],
|
|
141
|
+
circuitBreaker: {
|
|
142
|
+
maxSessionsPerIssue: 8,
|
|
143
|
+
maxSessionsPerPhase: 3,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
expect(result.ladder).toHaveLength(4);
|
|
147
|
+
expect(result.circuitBreaker.maxSessionsPerIssue).toBe(8);
|
|
148
|
+
expect(result.circuitBreaker.maxSessionsPerPhase).toBe(3);
|
|
149
|
+
});
|
|
150
|
+
it('validates config without optional maxSessionsPerPhase', () => {
|
|
151
|
+
const result = EscalationConfigSchema.parse({
|
|
152
|
+
ladder: [{ cycle: 1, strategy: 'normal' }],
|
|
153
|
+
circuitBreaker: {
|
|
154
|
+
maxSessionsPerIssue: 5,
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
expect(result.circuitBreaker.maxSessionsPerPhase).toBeUndefined();
|
|
158
|
+
});
|
|
159
|
+
it('rejects empty ladder', () => {
|
|
160
|
+
expect(() => EscalationConfigSchema.parse({
|
|
161
|
+
ladder: [],
|
|
162
|
+
circuitBreaker: { maxSessionsPerIssue: 8 },
|
|
163
|
+
})).toThrow();
|
|
164
|
+
});
|
|
165
|
+
it('rejects zero maxSessionsPerIssue', () => {
|
|
166
|
+
expect(() => EscalationConfigSchema.parse({
|
|
167
|
+
ladder: [{ cycle: 1, strategy: 'normal' }],
|
|
168
|
+
circuitBreaker: { maxSessionsPerIssue: 0 },
|
|
169
|
+
})).toThrow();
|
|
170
|
+
});
|
|
171
|
+
it('rejects negative maxSessionsPerIssue', () => {
|
|
172
|
+
expect(() => EscalationConfigSchema.parse({
|
|
173
|
+
ladder: [{ cycle: 1, strategy: 'normal' }],
|
|
174
|
+
circuitBreaker: { maxSessionsPerIssue: -1 },
|
|
175
|
+
})).toThrow();
|
|
176
|
+
});
|
|
177
|
+
it('rejects zero maxSessionsPerPhase', () => {
|
|
178
|
+
expect(() => EscalationConfigSchema.parse({
|
|
179
|
+
ladder: [{ cycle: 1, strategy: 'normal' }],
|
|
180
|
+
circuitBreaker: { maxSessionsPerIssue: 8, maxSessionsPerPhase: 0 },
|
|
181
|
+
})).toThrow();
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
describe('GateDefinitionSchema', () => {
|
|
185
|
+
it('validates a signal gate', () => {
|
|
186
|
+
const result = GateDefinitionSchema.parse({
|
|
187
|
+
name: 'human-review',
|
|
188
|
+
description: 'Wait for human approval',
|
|
189
|
+
type: 'signal',
|
|
190
|
+
trigger: { source: 'comment', match: 'RESUME' },
|
|
191
|
+
timeout: { duration: '4h', action: 'escalate' },
|
|
192
|
+
appliesTo: ['refinement'],
|
|
193
|
+
});
|
|
194
|
+
expect(result.name).toBe('human-review');
|
|
195
|
+
expect(result.type).toBe('signal');
|
|
196
|
+
expect(result.timeout?.action).toBe('escalate');
|
|
197
|
+
expect(result.appliesTo).toEqual(['refinement']);
|
|
198
|
+
});
|
|
199
|
+
it('validates a timer gate', () => {
|
|
200
|
+
const result = GateDefinitionSchema.parse({
|
|
201
|
+
name: 'scheduled-release',
|
|
202
|
+
type: 'timer',
|
|
203
|
+
trigger: { cron: '0 9 * * 1-5' },
|
|
204
|
+
});
|
|
205
|
+
expect(result.type).toBe('timer');
|
|
206
|
+
expect(result.timeout).toBeUndefined();
|
|
207
|
+
});
|
|
208
|
+
it('validates a webhook gate', () => {
|
|
209
|
+
const result = GateDefinitionSchema.parse({
|
|
210
|
+
name: 'external-approval',
|
|
211
|
+
type: 'webhook',
|
|
212
|
+
trigger: { endpoint: '/api/approve' },
|
|
213
|
+
timeout: { duration: '24h', action: 'fail' },
|
|
214
|
+
});
|
|
215
|
+
expect(result.type).toBe('webhook');
|
|
216
|
+
});
|
|
217
|
+
it('rejects invalid gate type', () => {
|
|
218
|
+
expect(() => GateDefinitionSchema.parse({
|
|
219
|
+
name: 'test',
|
|
220
|
+
type: 'invalid',
|
|
221
|
+
trigger: {},
|
|
222
|
+
})).toThrow();
|
|
223
|
+
});
|
|
224
|
+
it('rejects invalid timeout action', () => {
|
|
225
|
+
expect(() => GateDefinitionSchema.parse({
|
|
226
|
+
name: 'test',
|
|
227
|
+
type: 'signal',
|
|
228
|
+
trigger: {},
|
|
229
|
+
timeout: { duration: '1h', action: 'invalid' },
|
|
230
|
+
})).toThrow();
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
describe('ParallelismGroupDefinitionSchema', () => {
|
|
234
|
+
it('validates a fan-out group', () => {
|
|
235
|
+
const result = ParallelismGroupDefinitionSchema.parse({
|
|
236
|
+
name: 'sub-issue-fan-out',
|
|
237
|
+
description: 'Coordinate sub-issues in parallel',
|
|
238
|
+
phases: ['development'],
|
|
239
|
+
strategy: 'fan-out',
|
|
240
|
+
maxConcurrent: 5,
|
|
241
|
+
waitForAll: true,
|
|
242
|
+
});
|
|
243
|
+
expect(result.name).toBe('sub-issue-fan-out');
|
|
244
|
+
expect(result.strategy).toBe('fan-out');
|
|
245
|
+
expect(result.maxConcurrent).toBe(5);
|
|
246
|
+
expect(result.waitForAll).toBe(true);
|
|
247
|
+
});
|
|
248
|
+
it('validates a minimal group', () => {
|
|
249
|
+
const result = ParallelismGroupDefinitionSchema.parse({
|
|
250
|
+
name: 'parallel-qa',
|
|
251
|
+
phases: ['qa'],
|
|
252
|
+
strategy: 'race',
|
|
253
|
+
});
|
|
254
|
+
expect(result.maxConcurrent).toBeUndefined();
|
|
255
|
+
expect(result.waitForAll).toBeUndefined();
|
|
256
|
+
});
|
|
257
|
+
it('rejects empty phases array', () => {
|
|
258
|
+
expect(() => ParallelismGroupDefinitionSchema.parse({
|
|
259
|
+
name: 'test',
|
|
260
|
+
phases: [],
|
|
261
|
+
strategy: 'fan-out',
|
|
262
|
+
})).toThrow();
|
|
263
|
+
});
|
|
264
|
+
it('rejects invalid strategy', () => {
|
|
265
|
+
expect(() => ParallelismGroupDefinitionSchema.parse({
|
|
266
|
+
name: 'test',
|
|
267
|
+
phases: ['dev'],
|
|
268
|
+
strategy: 'invalid',
|
|
269
|
+
})).toThrow();
|
|
270
|
+
});
|
|
271
|
+
it('rejects zero maxConcurrent', () => {
|
|
272
|
+
expect(() => ParallelismGroupDefinitionSchema.parse({
|
|
273
|
+
name: 'test',
|
|
274
|
+
phases: ['dev'],
|
|
275
|
+
strategy: 'fan-out',
|
|
276
|
+
maxConcurrent: 0,
|
|
277
|
+
})).toThrow();
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
describe('WorkflowDefinitionSchema', () => {
|
|
281
|
+
it('validates a minimal workflow definition', () => {
|
|
282
|
+
const result = WorkflowDefinitionSchema.parse({
|
|
283
|
+
apiVersion: 'v1.1',
|
|
284
|
+
kind: 'WorkflowDefinition',
|
|
285
|
+
metadata: { name: 'test-workflow' },
|
|
286
|
+
phases: [
|
|
287
|
+
{ name: 'development', template: 'development' },
|
|
288
|
+
],
|
|
289
|
+
transitions: [
|
|
290
|
+
{ from: 'Backlog', to: 'development' },
|
|
291
|
+
],
|
|
292
|
+
});
|
|
293
|
+
expect(result.apiVersion).toBe('v1.1');
|
|
294
|
+
expect(result.kind).toBe('WorkflowDefinition');
|
|
295
|
+
expect(result.metadata.name).toBe('test-workflow');
|
|
296
|
+
expect(result.phases).toHaveLength(1);
|
|
297
|
+
expect(result.transitions).toHaveLength(1);
|
|
298
|
+
expect(result.escalation).toBeUndefined();
|
|
299
|
+
expect(result.gates).toBeUndefined();
|
|
300
|
+
expect(result.parallelism).toBeUndefined();
|
|
301
|
+
});
|
|
302
|
+
it('validates a full workflow definition with all sections', () => {
|
|
303
|
+
const result = WorkflowDefinitionSchema.parse({
|
|
304
|
+
apiVersion: 'v1.1',
|
|
305
|
+
kind: 'WorkflowDefinition',
|
|
306
|
+
metadata: {
|
|
307
|
+
name: 'full-workflow',
|
|
308
|
+
description: 'Complete workflow with all features',
|
|
309
|
+
},
|
|
310
|
+
phases: [
|
|
311
|
+
{ name: 'dev', template: 'development' },
|
|
312
|
+
{
|
|
313
|
+
name: 'refinement',
|
|
314
|
+
template: 'refinement',
|
|
315
|
+
variants: { 'context-enriched': 'refinement-context-enriched' },
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
transitions: [
|
|
319
|
+
{ from: 'Backlog', to: 'dev' },
|
|
320
|
+
{ from: 'Rejected', to: 'refinement', condition: '{{ true }}', priority: 5 },
|
|
321
|
+
],
|
|
322
|
+
escalation: {
|
|
323
|
+
ladder: [
|
|
324
|
+
{ cycle: 1, strategy: 'normal' },
|
|
325
|
+
{ cycle: 4, strategy: 'escalate-human' },
|
|
326
|
+
],
|
|
327
|
+
circuitBreaker: { maxSessionsPerIssue: 8, maxSessionsPerPhase: 3 },
|
|
328
|
+
},
|
|
329
|
+
gates: [
|
|
330
|
+
{
|
|
331
|
+
name: 'approval',
|
|
332
|
+
type: 'signal',
|
|
333
|
+
trigger: { source: 'comment' },
|
|
334
|
+
timeout: { duration: '4h', action: 'escalate' },
|
|
335
|
+
},
|
|
336
|
+
],
|
|
337
|
+
parallelism: [
|
|
338
|
+
{
|
|
339
|
+
name: 'fan-out',
|
|
340
|
+
phases: ['dev'],
|
|
341
|
+
strategy: 'fan-out',
|
|
342
|
+
maxConcurrent: 3,
|
|
343
|
+
},
|
|
344
|
+
],
|
|
345
|
+
});
|
|
346
|
+
expect(result.phases).toHaveLength(2);
|
|
347
|
+
expect(result.transitions).toHaveLength(2);
|
|
348
|
+
expect(result.escalation?.ladder).toHaveLength(2);
|
|
349
|
+
expect(result.gates).toHaveLength(1);
|
|
350
|
+
expect(result.parallelism).toHaveLength(1);
|
|
351
|
+
});
|
|
352
|
+
it('accepts empty phases and transitions arrays', () => {
|
|
353
|
+
const result = WorkflowDefinitionSchema.parse({
|
|
354
|
+
apiVersion: 'v1.1',
|
|
355
|
+
kind: 'WorkflowDefinition',
|
|
356
|
+
metadata: { name: 'empty' },
|
|
357
|
+
phases: [],
|
|
358
|
+
transitions: [],
|
|
359
|
+
});
|
|
360
|
+
expect(result.phases).toHaveLength(0);
|
|
361
|
+
expect(result.transitions).toHaveLength(0);
|
|
362
|
+
});
|
|
363
|
+
it('rejects invalid apiVersion', () => {
|
|
364
|
+
expect(() => WorkflowDefinitionSchema.parse({
|
|
365
|
+
apiVersion: 'v1',
|
|
366
|
+
kind: 'WorkflowDefinition',
|
|
367
|
+
metadata: { name: 'test' },
|
|
368
|
+
phases: [],
|
|
369
|
+
transitions: [],
|
|
370
|
+
})).toThrow();
|
|
371
|
+
});
|
|
372
|
+
it('rejects invalid kind', () => {
|
|
373
|
+
expect(() => WorkflowDefinitionSchema.parse({
|
|
374
|
+
apiVersion: 'v1.1',
|
|
375
|
+
kind: 'WorkflowTemplate',
|
|
376
|
+
metadata: { name: 'test' },
|
|
377
|
+
phases: [],
|
|
378
|
+
transitions: [],
|
|
379
|
+
})).toThrow();
|
|
380
|
+
});
|
|
381
|
+
it('rejects missing metadata.name', () => {
|
|
382
|
+
expect(() => WorkflowDefinitionSchema.parse({
|
|
383
|
+
apiVersion: 'v1.1',
|
|
384
|
+
kind: 'WorkflowDefinition',
|
|
385
|
+
metadata: {},
|
|
386
|
+
phases: [],
|
|
387
|
+
transitions: [],
|
|
388
|
+
})).toThrow();
|
|
389
|
+
});
|
|
390
|
+
it('rejects empty metadata.name', () => {
|
|
391
|
+
expect(() => WorkflowDefinitionSchema.parse({
|
|
392
|
+
apiVersion: 'v1.1',
|
|
393
|
+
kind: 'WorkflowDefinition',
|
|
394
|
+
metadata: { name: '' },
|
|
395
|
+
phases: [],
|
|
396
|
+
transitions: [],
|
|
397
|
+
})).toThrow();
|
|
398
|
+
});
|
|
399
|
+
it('rejects missing phases', () => {
|
|
400
|
+
expect(() => WorkflowDefinitionSchema.parse({
|
|
401
|
+
apiVersion: 'v1.1',
|
|
402
|
+
kind: 'WorkflowDefinition',
|
|
403
|
+
metadata: { name: 'test' },
|
|
404
|
+
transitions: [],
|
|
405
|
+
})).toThrow();
|
|
406
|
+
});
|
|
407
|
+
it('rejects missing transitions', () => {
|
|
408
|
+
expect(() => WorkflowDefinitionSchema.parse({
|
|
409
|
+
apiVersion: 'v1.1',
|
|
410
|
+
kind: 'WorkflowDefinition',
|
|
411
|
+
metadata: { name: 'test' },
|
|
412
|
+
phases: [],
|
|
413
|
+
})).toThrow();
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
describe('validateWorkflowDefinition', () => {
|
|
417
|
+
it('returns validated workflow for valid input', () => {
|
|
418
|
+
const result = validateWorkflowDefinition({
|
|
419
|
+
apiVersion: 'v1.1',
|
|
420
|
+
kind: 'WorkflowDefinition',
|
|
421
|
+
metadata: { name: 'test' },
|
|
422
|
+
phases: [{ name: 'dev', template: 'development' }],
|
|
423
|
+
transitions: [{ from: 'Backlog', to: 'dev' }],
|
|
424
|
+
});
|
|
425
|
+
expect(result.metadata.name).toBe('test');
|
|
426
|
+
});
|
|
427
|
+
it('includes file path in error message when provided', () => {
|
|
428
|
+
try {
|
|
429
|
+
validateWorkflowDefinition({ invalid: true }, '/path/to/workflow.yaml');
|
|
430
|
+
expect.fail('should throw');
|
|
431
|
+
}
|
|
432
|
+
catch (error) {
|
|
433
|
+
expect(error.message).toContain('/path/to/workflow.yaml');
|
|
434
|
+
expect(error.message).toContain('Invalid workflow definition');
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
it('throws ZodError directly when no file path provided', () => {
|
|
438
|
+
expect(() => validateWorkflowDefinition({ invalid: true })).toThrow();
|
|
439
|
+
});
|
|
440
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@renseiai/agentfactory",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Multi-agent fleet management for coding agents — orchestrator, providers, crash recovery",
|
|
6
6
|
"author": "Rensei AI (https://rensei.ai)",
|
|
@@ -49,14 +49,13 @@
|
|
|
49
49
|
"dotenv": "^17.2.3",
|
|
50
50
|
"handlebars": "^4.7.8",
|
|
51
51
|
"yaml": "^2.8.2",
|
|
52
|
-
"zod": "^4.3.6"
|
|
53
|
-
"@renseiai/agentfactory-linear": "0.8.5"
|
|
52
|
+
"zod": "^4.3.6"
|
|
54
53
|
},
|
|
55
54
|
"devDependencies": {
|
|
56
55
|
"@types/node": "^22.5.4",
|
|
57
56
|
"typescript": "^5.7.3",
|
|
58
57
|
"vitest": "^3.2.3",
|
|
59
|
-
"@renseiai/create-agentfactory-app": "0.8.
|
|
58
|
+
"@renseiai/create-agentfactory-app": "0.8.7"
|
|
60
59
|
},
|
|
61
60
|
"scripts": {
|
|
62
61
|
"build": "tsc",
|
package/dist/src/linear-cli.d.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* @deprecated Use `af-linear` from `@renseiai/agentfactory-cli` instead.
|
|
4
|
-
* This file is kept for backwards compatibility. The canonical implementation
|
|
5
|
-
* is in packages/cli/src/lib/linear-runner.ts.
|
|
6
|
-
*
|
|
7
|
-
* Linear CLI - Command-line interface for Linear Agent SDK
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* pnpm af-linear <command> [options]
|
|
11
|
-
*
|
|
12
|
-
* Commands:
|
|
13
|
-
* get-issue <id> Get issue details
|
|
14
|
-
* create-issue Create a new issue
|
|
15
|
-
* update-issue <id> Update an existing issue
|
|
16
|
-
* list-comments <issueId> List comments on an issue
|
|
17
|
-
* create-comment <issueId> Create a comment on an issue
|
|
18
|
-
* list-backlog-issues List backlog issues for a project
|
|
19
|
-
* list-unblocked-backlog List unblocked backlog issues
|
|
20
|
-
* check-blocked <id> Check if an issue is blocked
|
|
21
|
-
* add-relation <id> <id> Create relation between issues
|
|
22
|
-
* list-relations <id> List relations for an issue
|
|
23
|
-
* remove-relation <id> Remove a relation by ID
|
|
24
|
-
* list-sub-issues <id> List sub-issues of a parent issue
|
|
25
|
-
* list-sub-issue-statuses <id> List sub-issue statuses (lightweight)
|
|
26
|
-
* update-sub-issue <id> Update sub-issue status with comment
|
|
27
|
-
* check-deployment <PR> Check Vercel deployment status for a PR
|
|
28
|
-
*
|
|
29
|
-
* Array Values:
|
|
30
|
-
* --labels accepts comma-separated: --labels "Bug,Feature"
|
|
31
|
-
* For values with commas, use JSON: --labels '["Bug", "UI, UX"]'
|
|
32
|
-
* Text fields (--description, --title, --body) preserve commas.
|
|
33
|
-
*
|
|
34
|
-
* Environment:
|
|
35
|
-
* LINEAR_API_KEY Required API key for authentication
|
|
36
|
-
*/
|
|
37
|
-
import 'dotenv/config';
|
|
38
|
-
//# sourceMappingURL=linear-cli.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"linear-cli.d.ts","sourceRoot":"","sources":["../../src/linear-cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,eAAe,CAAA"}
|