workflow-ai 1.0.65 → 1.0.66

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.
Files changed (36) hide show
  1. package/README.md +377 -371
  2. package/configs/agent-health-rules.yaml +12 -1
  3. package/configs/pipeline.yaml +6 -6
  4. package/package.json +1 -1
  5. package/src/lib/agent-spawner.mjs +47 -6
  6. package/src/lib/error-classifier.mjs +311 -274
  7. package/src/runner.mjs +215 -58
  8. package/src/skills/coach/tests/cases/TC-COACH-001/current/meta.json +93 -93
  9. package/src/skills/coach/tests/cases/TC-COACH-002/current/meta.json +93 -93
  10. package/src/skills/create-plan/SKILL.md +1 -0
  11. package/src/skills/create-plan/knowledge/test-hygiene.md +47 -0
  12. package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/meta.json +113 -113
  13. package/src/skills/execute-task/tests/cases/TC-EXECUTE-TASK-001/current/meta.json +87 -87
  14. package/src/skills/execute-task/tests/cases/TC-EXECUTE-TASK-005/current/meta.json +87 -87
  15. package/src/skills/review-result/SKILL.md +1 -0
  16. package/src/skills/review-result/knowledge/test-hygiene.md +44 -0
  17. package/src/skills/review-result/scripts/verify-artifacts.js +115 -2
  18. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/claude-sonnet/trial-1.md +7 -0
  19. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/claude-sonnet/trial-2.md +7 -0
  20. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/claude-sonnet/trial-3.md +7 -0
  21. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/judge.json +163 -0
  22. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-deepseek/trial-1.md +5 -0
  23. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-deepseek/trial-2.md +5 -0
  24. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-deepseek/trial-3.md +11 -0
  25. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-glm/trial-1.md +16 -0
  26. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-glm/trial-2.md +18 -0
  27. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-glm/trial-3.md +17 -0
  28. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-minimax/trial-1.md +17 -0
  29. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-minimax/trial-2.md +31 -0
  30. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-minimax/trial-3.md +5 -0
  31. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/meta.json +115 -0
  32. package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003-test-isolation.yaml +50 -0
  33. package/src/skills/review-result/tests/fixtures/QA-904-test-isolation-violation/QA-904.md +51 -0
  34. package/src/skills/review-result/tests/fixtures/QA-904-test-isolation-violation/example-test.mjs +36 -0
  35. package/src/skills/review-result/tests/index.yaml +5 -0
  36. package/src/skills/review-result/tests/rubrics/test-isolation.md +20 -0
@@ -61,4 +61,15 @@ agents:
61
61
  class: "transient"
62
62
  ttl: "15m"
63
63
  pattern: "deepseek.*unavailable|upstream error"
64
- exit_codes: "any"
64
+ exit_codes: "any"
65
+
66
+ kilo-glm:
67
+ rules:
68
+ - id: "zai-usage-limit"
69
+ class: "unavailable"
70
+ ttl: "5h"
71
+ pattern: "Usage limit reached for \\d+ hour|api\\.z\\.ai.*\"statusCode\":429|AI_APICallError.*z\\.ai"
72
+ exit_codes: "any"
73
+
74
+ kilo-glm-air:
75
+ extends: kilo-glm
@@ -64,42 +64,42 @@ pipeline:
64
64
 
65
65
  kilo-code:
66
66
  command: "kilo"
67
- args: ["-m", "kilo/kilo-auto/free", "--agent", "orchestrator", "run"]
67
+ args: ["-m", "kilo/kilo-auto/free", "--agent", "orchestrator", "--print-logs", "--log-level", "ERROR", "run"]
68
68
  workdir: "."
69
69
  capabilities: [text]
70
70
  description: "Kilo мульти-режимный (architect, code, debug)"
71
71
 
72
72
  kilo-glm:
73
73
  command: "kilo"
74
- args: ["-m", "zai/glm-5.1", "--agent", "code", "run"]
74
+ args: ["-m", "zai/glm-5.1", "--agent", "code", "--print-logs", "--log-level", "ERROR", "run"]
75
75
  workdir: "."
76
76
  capabilities: [text]
77
77
  description: "Kilo GLM"
78
78
 
79
79
  kilo-glm-air:
80
80
  command: "kilo"
81
- args: ["-m", "zai/glm-4.5-air", "--agent", "code", "run"]
81
+ args: ["-m", "zai/glm-4.5-air", "--agent", "code", "--print-logs", "--log-level", "ERROR", "run"]
82
82
  workdir: "."
83
83
  capabilities: [text]
84
84
  description: "Kilo GLM air"
85
85
 
86
86
  kilo-deepseek:
87
87
  command: "kilo"
88
- args: ["-m", "deepseek/deepseek-reasoner", "--agent", "code", "run"]
88
+ args: ["-m", "deepseek/deepseek-reasoner", "--agent", "code", "--print-logs", "--log-level", "ERROR", "run"]
89
89
  workdir: "."
90
90
  capabilities: [text]
91
91
  description: "Kilo deepseek"
92
92
 
93
93
  kilo-minimax:
94
94
  command: "kilo"
95
- args: ["-m", "kilo/minimax/minimax-m2.7", "--agent", "code", "run"]
95
+ args: ["-m", "kilo/minimax/minimax-m2.7", "--agent", "code", "--print-logs", "--log-level", "ERROR", "run"]
96
96
  workdir: "."
97
97
  capabilities: [text]
98
98
  description: "Kilo minimax"
99
99
 
100
100
  kilo-free:
101
101
  command: "kilo"
102
- args: ["-m", "kilo/kilo-auto/free", "--agent", "code", "run"]
102
+ args: ["-m", "kilo/kilo-auto/free", "--agent", "code", "--print-logs", "--log-level", "ERROR", "run"]
103
103
  workdir: "."
104
104
  capabilities: [text]
105
105
  description: "Kilo free"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workflow-ai",
3
- "version": "1.0.65",
3
+ "version": "1.0.66",
4
4
  "description": "AI Agent Workflow Coordinator — kanban-based pipeline for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { spawn, execSync } from 'child_process';
4
4
  import path from 'path';
5
+ import { loadRules, scanStderrForFatalRule } from './error-classifier.mjs';
5
6
 
6
7
  const ResultParser = {
7
8
  STATUS_ALIASES: {
@@ -149,13 +150,21 @@ export async function spawnAgent(agentConfig, prompt, options = {}) {
149
150
  stageId = 'unknown',
150
151
  skillId = null,
151
152
  projectRoot = process.cwd(),
152
- currentChildRef = null
153
+ currentChildRef = null,
154
+ agentId = null,
155
+ healthRules = null
153
156
  } = options;
154
157
 
155
158
  return new Promise((resolve, reject) => {
156
159
  const args = [...agentConfig.args];
157
160
  const finalPrompt = prompt;
158
161
 
162
+ // Для онлайн-детекции фатальных stderr-паттернов
163
+ const rules = agentId
164
+ ? (healthRules || (() => { try { return loadRules(projectRoot); } catch { return null; } })())
165
+ : null;
166
+ const hasAgentRules = Boolean(rules && agentId && rules.agents.get(agentId)?.length);
167
+
159
168
  const useShell = process.platform === 'win32' && agentConfig.command !== 'node';
160
169
  const useStdin = useShell && finalPrompt.includes('\n');
161
170
 
@@ -196,14 +205,20 @@ export async function spawnAgent(agentConfig, prompt, options = {}) {
196
205
  let stdout = '';
197
206
  let stderr = '';
198
207
  let timedOut = false;
208
+ let earlyKilled = false;
209
+ let lastScanSize = 0;
199
210
 
200
- const timeoutId = setTimeout(() => {
201
- timedOut = true;
211
+ const killChild = () => {
202
212
  if (process.platform === 'win32' && child.pid) {
203
213
  try { execSync(`taskkill /pid ${child.pid} /T /F`, { stdio: 'pipe' }); } catch {}
204
214
  } else {
205
- child.kill('SIGTERM');
215
+ try { child.kill('SIGTERM'); } catch {}
206
216
  }
217
+ };
218
+
219
+ const timeoutId = setTimeout(() => {
220
+ timedOut = true;
221
+ killChild();
207
222
  if (logger) {
208
223
  logger.timeout(stageId, timeout);
209
224
  }
@@ -243,6 +258,32 @@ export async function spawnAgent(agentConfig, prompt, options = {}) {
243
258
  child.stderr.on('data', (data) => {
244
259
  stderr += data.toString();
245
260
  process.stderr.write(data);
261
+
262
+ if (!hasAgentRules || earlyKilled || timedOut) return;
263
+ // Throttle: первый скан всегда, последующие — только после 200+ новых байт.
264
+ if (lastScanSize > 0 && stderr.length - lastScanSize < 200) return;
265
+ lastScanSize = stderr.length;
266
+ const match = scanStderrForFatalRule(rules, agentId, stderr);
267
+ if (!match) return;
268
+
269
+ earlyKilled = true;
270
+ clearTimeout(timeoutId);
271
+ if (logger) {
272
+ logger.error?.(
273
+ `Fatal stderr pattern matched for ${agentId} (rule=${match.rule_id}, class=${match.class}). Killing process.`,
274
+ stageId
275
+ );
276
+ }
277
+ killChild();
278
+ const err = new Error(
279
+ `Agent "${agentId}" killed early: ${match.rule_id} (class=${match.class})`
280
+ );
281
+ err.code = 'EARLY_KILL';
282
+ err.exitCode = -1;
283
+ err.stderr = stderr;
284
+ err.earlyKill = true;
285
+ err.rule = match;
286
+ reject(err);
246
287
  });
247
288
 
248
289
  child.on('close', (code) => {
@@ -264,7 +305,7 @@ export async function spawnAgent(agentConfig, prompt, options = {}) {
264
305
  }
265
306
  process.stdout.write('\n');
266
307
 
267
- if (timedOut) return;
308
+ if (timedOut || earlyKilled) return;
268
309
 
269
310
  if (logger) {
270
311
  logger.cliCall(agentConfig.command, args, code);
@@ -324,7 +365,7 @@ export async function spawnAgent(agentConfig, prompt, options = {}) {
324
365
 
325
366
  child.on('error', (err) => {
326
367
  clearTimeout(timeoutId);
327
- if (!timedOut) {
368
+ if (!timedOut && !earlyKilled) {
328
369
  if (logger) {
329
370
  logger.error(`CLI error: ${err.message}`, stageId);
330
371
  }