amsd-pipeline-shared 0.5.7 → 0.5.8
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.
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
"node": {
|
|
4
4
|
"runtimeNames": ["TypeError", "ReferenceError", "SyntaxError", "RangeError"],
|
|
5
5
|
"runtimeMessage": "Unexpected token|is not valid JSON|Unexpected end of JSON|Cannot read propert|Cannot set propert|is not a function|is not defined|is not iterable|Cannot destructure|is not a constructor|Invalid array length|Maximum call stack",
|
|
6
|
-
"external": "\\bE(NOSPC|ACCES|PERM|MFILE|NOMEM|CONNRESET|CONNREFUSED|TIMEDOUT|NOTFOUND|AI_AGAIN|PIPE)\\b|bounded:
|
|
6
|
+
"external": "\\bE(NOSPC|ACCES|PERM|MFILE|NOMEM|CONNRESET|CONNREFUSED|TIMEDOUT|NOTFOUND|AI_AGAIN|PIPE)\\b|bounded: REFUSED|exited (137|143)\\b|\\b(401|403|429|500|502|503|529)\\b|overloaded|rate.?limit|credit|quota|<!DOCTYPE|<html|out of memory|killed",
|
|
7
|
+
"ownBudget": "bounded: cap=\\d+MB",
|
|
7
8
|
"generalRefusal": "refused|usage:",
|
|
8
9
|
"internalFrame": "^node:",
|
|
9
10
|
"dependencyDir": "node_modules"
|
|
@@ -19,12 +19,16 @@ function signaturesFor(runtime, injected) {
|
|
|
19
19
|
const all = injected || JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', '..', 'config', 'doctor-signatures.json'), 'utf8'));
|
|
20
20
|
const s = all[runtime];
|
|
21
21
|
if (!s) throw new Error(`no signatures for runtime "${runtime}" (config/doctor-signatures.json knows: ${Object.keys(all).filter((k) => !k.startsWith('$')).join(', ')})`);
|
|
22
|
-
return { runtimeNames: new Set(s.runtimeNames), runtimeMessage: rxOf(s.runtimeMessage), external: rxOf(s.external), generalRefusal: rxOf(s.generalRefusal),
|
|
22
|
+
return { runtimeNames: new Set(s.runtimeNames), runtimeMessage: rxOf(s.runtimeMessage), external: rxOf(s.external), ownBudget: s.ownBudget ? rxOf(s.ownBudget) : /$^/, generalRefusal: rxOf(s.generalRefusal),
|
|
23
23
|
internalFrame: rxOf(s.internalFrame), dependencyDir: s.dependencyDir };
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function classify({ profile, portRoot, res, crash, aborted, since, signatures, evidence }) {
|
|
27
27
|
const sig = signaturesFor(profile.classify.runtime, signatures);
|
|
28
|
+
const takesMost = profile.classify.policy === 'all-but-refusals';
|
|
29
|
+
// WHAT IS EXTERNAL: the machine, the network, the model service, a kill. Under "all-but-refusals" a kill that happened inside the pipeline's OWN memory budget (the bounded wrapper's start banner is in the text:
|
|
30
|
+
// the cap the pipeline chose, so what ran inside it is the pipeline's to bound — run 11, jest workers inside a Writer session's cap) is not external
|
|
31
|
+
const isExternal = (text) => sig.external.test(text) && !(takesMost && sig.ownBudget.test(text));
|
|
28
32
|
if (!res || typeof res !== 'object') return no('no-result', 'the leg gave no result');
|
|
29
33
|
if (aborted) return no('aborted', 'the operator or a watcher ended the leg');
|
|
30
34
|
// AN AGENT-CAUSED ABORT (operator, 2026-10-20: look for the agentic solution first): a failed leg whose reason is one of the pipeline's own agent-caused
|
|
@@ -39,7 +43,7 @@ function classify({ profile, portRoot, res, crash, aborted, since, signatures, e
|
|
|
39
43
|
// has a number and its reason and matches none. The marked line is still refused if it names the machine, the network, the model service or a kill.
|
|
40
44
|
const line = evidence && profile.classify.faultMarks.length ? String(evidence).split('\n').find((l) => profile.classify.faultMarks.some((r) => r.test(l))) : null;
|
|
41
45
|
if (line) {
|
|
42
|
-
if (
|
|
46
|
+
if (isExternal(line)) return no('external', `the failing line also names the machine, the network, the model service or a kill — not the pipeline: ${line.slice(0, 160)}`);
|
|
43
47
|
return { call: true, kind: 'fault', layer: 'undecided', why: `the leg failed without a verdict: ${line.trim().slice(0, 200)}` };
|
|
44
48
|
}
|
|
45
49
|
// MOST FAILURES (profile classify.policy "all-but-refusals"): the pipeline's own refusals are its decision and what names the machine, the network, the model service or a kill is not the code —
|
|
@@ -48,21 +52,21 @@ function classify({ profile, portRoot, res, crash, aborted, since, signatures, e
|
|
|
48
52
|
const lines = String(evidence || '').split('\n').filter((l) => l.trim());
|
|
49
53
|
const window = [reason, ...lines.slice(-25)].filter(Boolean).join('\n');
|
|
50
54
|
if (!window.trim()) return no('not-a-crash', 'the leg failed with no reason and no evidence — nothing to investigate');
|
|
51
|
-
if (
|
|
55
|
+
if (isExternal(window)) return no('external', 'the end of the failure also names the machine, the network, the model service or a kill — not the pipeline');
|
|
52
56
|
if (profile.classify.refusals.some((r) => r.test(window))) return no('refusal', 'the pipeline stopped on purpose (its own refusal or pre-condition)');
|
|
53
57
|
const last = [...lines].reverse().find((l) => /fail|HALT|block|error/i.test(l)) || reason;
|
|
54
58
|
return { call: true, kind: 'failure', layer: 'undecided', why: `the leg failed (${reason || 'no reason'}): ${String(last).trim().slice(0, 200)}` };
|
|
55
59
|
}
|
|
56
60
|
return no('not-a-crash', 'the leg failed on a verdict that names no agent-caused message — reported, not repaired');
|
|
57
61
|
}
|
|
58
|
-
if (
|
|
62
|
+
if (isExternal(reason)) return no('external', 'the agent-caused message also names the machine, the network, the model service or a kill — not the pipeline');
|
|
59
63
|
return { call: true, kind: 'agentic-abort', layer: 'agentic', why: `an agent caused the abort: ${reason.slice(0, 200)}` };
|
|
60
64
|
}
|
|
61
65
|
if (res.status !== 'crashed') return no(res.status === 'no-answer' ? 'no-record' : 'not-a-crash', `the leg ended ${res.status}, not crashed — a kill, a verdict or a pause is reported, not repaired`);
|
|
62
66
|
if (!crash || typeof crash !== 'object') return no('no-record', 'no crash record: nothing proves a defect in the code');
|
|
63
67
|
if (!crash.at || !since || Date.parse(crash.at) < Date.parse(since)) return no('stale-record', 'the crash record is older than this leg');
|
|
64
68
|
const text = `${crash.name || ''}: ${crash.message || ''}`;
|
|
65
|
-
if (
|
|
69
|
+
if (isExternal(text)) return no('external', 'the failure names the machine, the network, the model service or a kill — not the code');
|
|
66
70
|
if (sig.generalRefusal.test(text) || profile.classify.refusals.some((r) => r.test(text))) return no('refusal', 'the pipeline stopped on purpose');
|
|
67
71
|
const site = (crash.frames || []).find((f) => f && typeof f.file === 'string' && !sig.internalFrame.test(f.file)) || null;
|
|
68
72
|
if (!site) return no('outside-port', 'no frame of the stack is in any file');
|
|
@@ -71,6 +75,8 @@ function classify({ profile, portRoot, res, crash, aborted, since, signatures, e
|
|
|
71
75
|
const inPort = !!portRoot && !rel.startsWith('..') && !path.isAbsolute(rel) && profile.scope.repairDirs.includes(parts[0]) && !parts.includes(sig.dependencyDir);
|
|
72
76
|
if (!inPort) return no('outside-port', `the throw site ${site.file}:${site.line} is not the component's own code`);
|
|
73
77
|
if (crash.fault === true) return { call: true, kind: 'fault', layer: 'undecided', why: `a fault the component raised: ${String(crash.message).slice(0, 160)} — at ${rel}:${site.line}${crash.step ? ` (step: ${crash.step})` : ''}` };
|
|
78
|
+
// under "all-but-refusals" an Error thrown by the component's own code is the doctor's even without a runtime-exception mark (run 11: `the live session for writer exited null`)
|
|
79
|
+
if (takesMost && !sig.runtimeNames.has(crash.name) && !sig.runtimeMessage.test(crash.message || '')) return { call: true, kind: 'crash', layer: 'undecided', why: `${crash.name}: ${String(crash.message).slice(0, 160)} — thrown at ${rel}:${site.line}${crash.step ? ` (step: ${crash.step})` : ''}` };
|
|
74
80
|
if (!sig.runtimeNames.has(crash.name) && !sig.runtimeMessage.test(crash.message || '')) return no('ambiguous', `an Error in ${rel}:${site.line} with no sign of a code defect — reported`);
|
|
75
81
|
return { call: true, kind: 'port-defect', layer: 'code', why: `${crash.name}: ${String(crash.message).slice(0, 160)} — thrown at ${rel}:${site.line}${crash.step ? ` (step: ${crash.step})` : ''}` };
|
|
76
82
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amsd-pipeline-shared",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
4
4
|
"description": "The AMSD pipeline's shared components, one source for the bash pipeline and its Claude workflows: the model-call gateway (llm-handler.sh — retry, ladder, self-heal, cost) and the libraries it runs.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"bin": {
|