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.
- package/README.md +377 -371
- package/configs/agent-health-rules.yaml +12 -1
- package/configs/pipeline.yaml +6 -6
- package/package.json +1 -1
- package/src/lib/agent-spawner.mjs +47 -6
- package/src/lib/error-classifier.mjs +311 -274
- package/src/runner.mjs +215 -58
- package/src/skills/coach/tests/cases/TC-COACH-001/current/meta.json +93 -93
- package/src/skills/coach/tests/cases/TC-COACH-002/current/meta.json +93 -93
- package/src/skills/create-plan/SKILL.md +1 -0
- package/src/skills/create-plan/knowledge/test-hygiene.md +47 -0
- package/src/skills/decompose-plan/tests/cases/TC-DECOMPOSE-PLAN-005/current/meta.json +113 -113
- package/src/skills/execute-task/tests/cases/TC-EXECUTE-TASK-001/current/meta.json +87 -87
- package/src/skills/execute-task/tests/cases/TC-EXECUTE-TASK-005/current/meta.json +87 -87
- package/src/skills/review-result/SKILL.md +1 -0
- package/src/skills/review-result/knowledge/test-hygiene.md +44 -0
- package/src/skills/review-result/scripts/verify-artifacts.js +115 -2
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/claude-sonnet/trial-1.md +7 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/claude-sonnet/trial-2.md +7 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/claude-sonnet/trial-3.md +7 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/judge.json +163 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-deepseek/trial-1.md +5 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-deepseek/trial-2.md +5 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-deepseek/trial-3.md +11 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-glm/trial-1.md +16 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-glm/trial-2.md +18 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-glm/trial-3.md +17 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-minimax/trial-1.md +17 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-minimax/trial-2.md +31 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/kilo-minimax/trial-3.md +5 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003/current/meta.json +115 -0
- package/src/skills/review-result/tests/cases/TC-REVIEW-RESULT-003-test-isolation.yaml +50 -0
- package/src/skills/review-result/tests/fixtures/QA-904-test-isolation-violation/QA-904.md +51 -0
- package/src/skills/review-result/tests/fixtures/QA-904-test-isolation-violation/example-test.mjs +36 -0
- package/src/skills/review-result/tests/index.yaml +5 -0
- 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
|
package/configs/pipeline.yaml
CHANGED
|
@@ -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
|
@@ -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
|
|
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
|
}
|