@ai-dossier/sched 0.66.0 → 0.67.1
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 +42 -0
- package/dist/engine-alert.d.ts +36 -0
- package/dist/engine-alert.d.ts.map +1 -0
- package/dist/engine-alert.js +163 -0
- package/dist/engine-alert.js.map +1 -0
- package/dist/engine-exit.d.ts +70 -0
- package/dist/engine-exit.d.ts.map +1 -0
- package/dist/engine-exit.js +206 -0
- package/dist/engine-exit.js.map +1 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +3 -0
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -5
- package/dist/index.js.map +1 -1
- package/dist/persist.d.ts +23 -0
- package/dist/persist.d.ts.map +1 -1
- package/dist/persist.js +54 -2
- package/dist/persist.js.map +1 -1
- package/dist/project.d.ts +5 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +7 -3
- package/dist/project.js.map +1 -1
- package/dist/status.d.ts +21 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +47 -5
- package/dist/status.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -423,6 +423,48 @@ when a tick does nothing (`nothing to do`) — a log that stops growing while
|
|
|
423
423
|
the failure mode #679 fixed: the engine exited cleanly after its first tick because its
|
|
424
424
|
inter-tick sleep handles were unref'd.
|
|
425
425
|
|
|
426
|
+
### Engine exit logging, heartbeat and crash alerts (#945)
|
|
427
|
+
|
|
428
|
+
The engine must never die silently. Every exit the process can observe writes a final
|
|
429
|
+
`engine-exit` journal line with its `reason` (every cause is kept: a crash during a stop
|
|
430
|
+
adds a second line with its stack):
|
|
431
|
+
|
|
432
|
+
| reason | cause | lease |
|
|
433
|
+
|---|---|---|
|
|
434
|
+
| `signal:SIGTERM` / `SIGINT` | graceful stop: finish the tick, agents keep running. A `engine-stopping.json` marker is written first; a second signal exits at once (`signal-repeat`); a hard deadline (120 s) forces `stop-timeout` if the stop never completes | released |
|
|
435
|
+
| `signal-ignored:SIGHUP` | SIGHUP never stops the engine (nohup / a closing terminal must not kill an unattended engine); journaled, engine keeps running | kept |
|
|
436
|
+
| `normal` | the loop returned after a stop request (or `--once` finished its tick) | released |
|
|
437
|
+
| `stop-timeout` | a requested stop was still running at the deadline; forced exit | released |
|
|
438
|
+
| `uncaught-exception` / `unhandled-rejection` | crash, stack in `detail`, exit code 70 | **kept** as the crash marker |
|
|
439
|
+
| `process-exit` | a bare `process.exit()` nothing else logged | released |
|
|
440
|
+
| *(no line)* | SIGKILL / OOM killer — unobservable | left stale |
|
|
441
|
+
|
|
442
|
+
The exit logger is installed immediately after the lease is acquired, so no later startup
|
|
443
|
+
step can exit unlogged. A tick that is blocked in a synchronous call cannot run signal
|
|
444
|
+
handlers, so every synchronous exec on the tick path is timeout-bounded (`defaultExec`: 120 s;
|
|
445
|
+
the engine's own execs have explicit budgets) and the stop is therefore bounded too.
|
|
446
|
+
|
|
447
|
+
The lease's `updated_at` is a heartbeat rewritten (compare-and-swap on the lease id) every
|
|
448
|
+
tick; `sched status` shows it. A live pid whose heartbeat is older than the hung threshold
|
|
449
|
+
(10 x the reconcile interval, never under 5 min; `$DOSSIER_SCHED_HUNG_INTERVALS` overrides
|
|
450
|
+
the multiplier) with unfinished work is an `engine-hung` warning/alert. Visible alerts are
|
|
451
|
+
journaled, printed to stderr, and posted as a comment when a tracking issue is given
|
|
452
|
+
(`--alert-issue <n>` or `$DOSSIER_SCHED_ALERT_ISSUE`; validated at `sched start` startup):
|
|
453
|
+
|
|
454
|
+
- `engine-restarted-after-crash`: a `sched start` reclaimed a lease whose holder died without
|
|
455
|
+
releasing it (crash, SIGKILL, OOM). If the previous engine had begun a stop (marker for the
|
|
456
|
+
same pid) it is reported as `stop-interrupted` (a wedged stop), not a crash.
|
|
457
|
+
- `stale-engine-lease-alert`: the lease holder is dead (`reason=stale-lease`) or alive but
|
|
458
|
+
hung (`reason=engine-hung`) while work is unfinished. Once per episode (an `O_EXCL` marker
|
|
459
|
+
file, so racing watchers cannot double-alert); raised by a watcher — `sched status --alert`
|
|
460
|
+
(cron-able). An unwritable store never makes the watcher throw.
|
|
461
|
+
|
|
462
|
+
**#920's silent exit:** the log ended in `nothing to do`, which is just an idle tick's line.
|
|
463
|
+
Only SIGINT had a handler, so a SIGTERM/SIGHUP (another session's restart, a closing
|
|
464
|
+
terminal) killed the engine with no line and no lease release. That is now logged; if the
|
|
465
|
+
2026-09-29 cause was SIGKILL/OOM, the missing `engine-exit` plus the stale lease is now the
|
|
466
|
+
documented signature (`journalctl -k` for the OOM window).
|
|
467
|
+
|
|
426
468
|
### Zombie-run fencing (#504)
|
|
427
469
|
|
|
428
470
|
The ladder redispatches the SAME run, so a takeover inherits the run id and its milestone
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visible engine-death alerts (#945 ask 2, #940 ask 2). `sched status` showing
|
|
3
|
+
* a `stale-engine-lease` warning is not an alert — nobody runs it. These
|
|
4
|
+
* helpers journal a durable event AND hand a message to a pluggable notifier
|
|
5
|
+
* (the CLI wires stderr + an optional GitHub comment on a tracking issue), so a
|
|
6
|
+
* dead or hung engine is visible without anyone looking.
|
|
7
|
+
*
|
|
8
|
+
* Triggers, all deduplicated:
|
|
9
|
+
* - `checkStaleLeaseAlert`: lease holder dead + unfinished work → ONE alert
|
|
10
|
+
* per stale episode; lease holder alive but its heartbeat older than the
|
|
11
|
+
* hung threshold → ONE `engine-hung` alert per episode. An episode is
|
|
12
|
+
* remembered by an `O_EXCL` marker file, so two concurrent watchers cannot
|
|
13
|
+
* both alert. Called by watchers (`sched status --alert`, cron-able).
|
|
14
|
+
* - `reportCrashRestart`: a new `sched start` reclaimed a dead holder's lease
|
|
15
|
+
* → `engine-restarted-after-crash` (or `engine-restarted-after-stop-timeout`
|
|
16
|
+
* when the previous engine had begun a graceful stop first).
|
|
17
|
+
*
|
|
18
|
+
* A watcher runs unattended: an unwritable store or journal downgrades to a
|
|
19
|
+
* stderr warning (and possibly a repeat alert) — it never throws.
|
|
20
|
+
*/
|
|
21
|
+
import type { Journal } from './journal';
|
|
22
|
+
import type { ReclaimedEngineLease, SchedStore } from './persist';
|
|
23
|
+
export interface EngineAlert {
|
|
24
|
+
kind: 'stale-engine-lease' | 'engine-hung' | 'engine-restarted-after-crash' | 'engine-restarted-after-stop-timeout';
|
|
25
|
+
message: string;
|
|
26
|
+
}
|
|
27
|
+
export type AlertNotifier = (alert: EngineAlert) => void;
|
|
28
|
+
export type StaleLeaseAlertOutcome = 'no-lease' | 'alive' | 'no-work' | 'already-alerted' | 'alerted';
|
|
29
|
+
export declare function checkStaleLeaseAlert(store: SchedStore, journal: Journal, notify: AlertNotifier, now?: Date, opts?: {
|
|
30
|
+
hungAfterMs?: number;
|
|
31
|
+
}): StaleLeaseAlertOutcome;
|
|
32
|
+
export declare function reportCrashRestart(journal: Journal, reclaimed: ReclaimedEngineLease, notify: AlertNotifier, now?: Date, stopping?: {
|
|
33
|
+
signal: string;
|
|
34
|
+
at: string;
|
|
35
|
+
} | null): void;
|
|
36
|
+
//# sourceMappingURL=engine-alert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine-alert.d.ts","sourceRoot":"","sources":["../src/engine-alert.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGlE,MAAM,WAAW,WAAW;IAC1B,IAAI,EACA,oBAAoB,GACpB,aAAa,GACb,8BAA8B,GAC9B,qCAAqC,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;AAKzD,MAAM,MAAM,sBAAsB,GAC9B,UAAU,GACV,OAAO,GACP,SAAS,GACT,iBAAiB,GACjB,SAAS,CAAC;AA+Bd,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,aAAa,EACrB,GAAG,GAAE,IAAiB,EACtB,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,sBAAsB,CAkDxB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,oBAAoB,EAC/B,MAAM,EAAE,aAAa,EACrB,GAAG,GAAE,IAAiB,EACtB,QAAQ,CAAC,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAC/C,IAAI,CAuBN"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Visible engine-death alerts (#945 ask 2, #940 ask 2). `sched status` showing
|
|
4
|
+
* a `stale-engine-lease` warning is not an alert — nobody runs it. These
|
|
5
|
+
* helpers journal a durable event AND hand a message to a pluggable notifier
|
|
6
|
+
* (the CLI wires stderr + an optional GitHub comment on a tracking issue), so a
|
|
7
|
+
* dead or hung engine is visible without anyone looking.
|
|
8
|
+
*
|
|
9
|
+
* Triggers, all deduplicated:
|
|
10
|
+
* - `checkStaleLeaseAlert`: lease holder dead + unfinished work → ONE alert
|
|
11
|
+
* per stale episode; lease holder alive but its heartbeat older than the
|
|
12
|
+
* hung threshold → ONE `engine-hung` alert per episode. An episode is
|
|
13
|
+
* remembered by an `O_EXCL` marker file, so two concurrent watchers cannot
|
|
14
|
+
* both alert. Called by watchers (`sched status --alert`, cron-able).
|
|
15
|
+
* - `reportCrashRestart`: a new `sched start` reclaimed a dead holder's lease
|
|
16
|
+
* → `engine-restarted-after-crash` (or `engine-restarted-after-stop-timeout`
|
|
17
|
+
* when the previous engine had begun a graceful stop first).
|
|
18
|
+
*
|
|
19
|
+
* A watcher runs unattended: an unwritable store or journal downgrades to a
|
|
20
|
+
* stderr warning (and possibly a repeat alert) — it never throws.
|
|
21
|
+
*/
|
|
22
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
25
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
26
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
27
|
+
}
|
|
28
|
+
Object.defineProperty(o, k2, desc);
|
|
29
|
+
}) : (function(o, m, k, k2) {
|
|
30
|
+
if (k2 === undefined) k2 = k;
|
|
31
|
+
o[k2] = m[k];
|
|
32
|
+
}));
|
|
33
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
34
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
35
|
+
}) : function(o, v) {
|
|
36
|
+
o["default"] = v;
|
|
37
|
+
});
|
|
38
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
39
|
+
var ownKeys = function(o) {
|
|
40
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
41
|
+
var ar = [];
|
|
42
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
43
|
+
return ar;
|
|
44
|
+
};
|
|
45
|
+
return ownKeys(o);
|
|
46
|
+
};
|
|
47
|
+
return function (mod) {
|
|
48
|
+
if (mod && mod.__esModule) return mod;
|
|
49
|
+
var result = {};
|
|
50
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
51
|
+
__setModuleDefault(result, mod);
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
})();
|
|
55
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.checkStaleLeaseAlert = checkStaleLeaseAlert;
|
|
57
|
+
exports.reportCrashRestart = reportCrashRestart;
|
|
58
|
+
const fs = __importStar(require("node:fs"));
|
|
59
|
+
const path = __importStar(require("node:path"));
|
|
60
|
+
const status_1 = require("./status");
|
|
61
|
+
const ALERT_MARKER_PREFIX = '.alerted-';
|
|
62
|
+
const LEGACY_MARKER_FILE = '.stale-lease-alerted';
|
|
63
|
+
/** Episode ids come from lease ids (`pid-ms-random`); keep the marker name filesystem-safe. */
|
|
64
|
+
function markerName(episode) {
|
|
65
|
+
return `${ALERT_MARKER_PREFIX}${episode.replace(/[^A-Za-z0-9._-]/g, '_')}`;
|
|
66
|
+
}
|
|
67
|
+
function clearMarkers(dir) {
|
|
68
|
+
try {
|
|
69
|
+
for (const f of fs.readdirSync(dir)) {
|
|
70
|
+
if (f.startsWith(ALERT_MARKER_PREFIX) || f === LEGACY_MARKER_FILE) {
|
|
71
|
+
fs.rmSync(path.join(dir, f), { force: true });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// no store dir yet / unreadable: nothing to clear
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** True when this caller won the episode (marker created); false when it already existed. Throws only on an unwritable store. */
|
|
80
|
+
function claimEpisode(dir, episode) {
|
|
81
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
82
|
+
try {
|
|
83
|
+
fs.closeSync(fs.openSync(path.join(dir, markerName(episode)), 'wx', 0o600));
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
if (err.code === 'EEXIST')
|
|
88
|
+
return false;
|
|
89
|
+
throw err;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function checkStaleLeaseAlert(store, journal, notify, now = new Date(), opts = {}) {
|
|
93
|
+
const lease = store.engineLeaseStatus();
|
|
94
|
+
if (lease === null) {
|
|
95
|
+
clearMarkers(store.dir);
|
|
96
|
+
return 'no-lease';
|
|
97
|
+
}
|
|
98
|
+
const hungAfterMs = opts.hungAfterMs ?? (0, status_1.resolveHungAfterMs)(0);
|
|
99
|
+
const hungFor = (0, status_1.engineHungForMs)(lease, now, hungAfterMs);
|
|
100
|
+
if (lease.alive && hungFor === null) {
|
|
101
|
+
clearMarkers(store.dir);
|
|
102
|
+
return 'alive';
|
|
103
|
+
}
|
|
104
|
+
const { unfinished, liveSlots } = (0, status_1.countUnfinishedWork)(store.load());
|
|
105
|
+
if (unfinished === 0 && liveSlots === 0)
|
|
106
|
+
return 'no-work';
|
|
107
|
+
const hung = lease.alive;
|
|
108
|
+
const baseEpisode = lease.id ?? `pid-${lease.pid}`;
|
|
109
|
+
const episode = hung ? `${baseEpisode}-hung` : baseEpisode;
|
|
110
|
+
try {
|
|
111
|
+
if (!claimEpisode(store.dir, episode))
|
|
112
|
+
return 'already-alerted';
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
// Cannot remember the episode: still alert (a repeat beats silence).
|
|
116
|
+
process.stderr.write(`⚠ sched alert: could not record alert marker: ${err.message}\n`);
|
|
117
|
+
}
|
|
118
|
+
const queue = `${unfinished} queue entr${unfinished === 1 ? 'y is' : 'ies are'} unfinished and ${liveSlots} slot(s) live`;
|
|
119
|
+
const message = hung
|
|
120
|
+
? `sched engine looks HUNG: pid ${lease.pid} is alive but its last heartbeat was ${lease.updated_at} (${Math.round((hungFor ?? 0) / 60_000)} min ago) while ${queue} — a tick is probably wedged. Stop it (SIGTERM; SIGKILL if it ignores that) and run \`ai-dossier sched start\`.`
|
|
121
|
+
: `sched engine is DOWN: lease holder pid ${lease.pid} is not running${lease.updated_at ? ` (last heartbeat ${lease.updated_at})` : ''} while ${queue} — nothing is ticking. Restart with \`ai-dossier sched start\`.`;
|
|
122
|
+
try {
|
|
123
|
+
journal.append({
|
|
124
|
+
event: 'stale-engine-lease-alert',
|
|
125
|
+
pid: lease.pid,
|
|
126
|
+
reason: hung ? 'engine-hung' : 'stale-lease',
|
|
127
|
+
detail: message,
|
|
128
|
+
}, now);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
process.stderr.write(`⚠ sched alert: could not journal alert: ${err.message}\n`);
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
notify({ kind: hung ? 'engine-hung' : 'stale-engine-lease', message });
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
process.stderr.write(`⚠ sched alert notifier failed: ${err.message}\n`);
|
|
138
|
+
}
|
|
139
|
+
return 'alerted';
|
|
140
|
+
}
|
|
141
|
+
function reportCrashRestart(journal, reclaimed, notify, now = new Date(), stopping) {
|
|
142
|
+
const since = reclaimed.updated_at ? `; its last heartbeat was ${reclaimed.updated_at}` : '';
|
|
143
|
+
const stopGoneWrong = stopping !== undefined && stopping !== null;
|
|
144
|
+
const message = stopGoneWrong
|
|
145
|
+
? `sched engine restarted after a stop that never finished: previous engine pid ${reclaimed.pid} began a graceful stop on ${stopping.signal} at ${stopping.at} and was then killed${since}. This is a wedged stop, not a crash.`
|
|
146
|
+
: `sched engine restarted after an unexpected exit: previous engine pid ${reclaimed.pid} died without releasing its lease${since}. Check the journal for its final \`engine-exit\` line (none means SIGKILL/OOM).`;
|
|
147
|
+
journal.append({
|
|
148
|
+
event: 'engine-restarted-after-crash',
|
|
149
|
+
pid: reclaimed.pid,
|
|
150
|
+
reason: stopGoneWrong ? 'stop-interrupted' : 'lease-reclaimed',
|
|
151
|
+
detail: message,
|
|
152
|
+
}, now);
|
|
153
|
+
try {
|
|
154
|
+
notify({
|
|
155
|
+
kind: stopGoneWrong ? 'engine-restarted-after-stop-timeout' : 'engine-restarted-after-crash',
|
|
156
|
+
message,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
process.stderr.write(`⚠ sched alert notifier failed: ${err.message}\n`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=engine-alert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine-alert.js","sourceRoot":"","sources":["../src/engine-alert.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine termination logging (#945, #940 ask 3): the engine must never die
|
|
3
|
+
* silently. Every exit the process can observe writes one final `engine-exit`
|
|
4
|
+
* journal line with the reason (and stack for a crash):
|
|
5
|
+
*
|
|
6
|
+
* signal:<SIG> SIGTERM / SIGINT — graceful stop is requested (the loop
|
|
7
|
+
* finishes its tick, agents keep running). A `stopping`
|
|
8
|
+
* marker is written first, a second signal exits at once,
|
|
9
|
+
* and a hard deadline (`stopTimeoutMs`) bounds a wedged
|
|
10
|
+
* stop (`stop-timeout`)
|
|
11
|
+
* signal-ignored:SIGHUP SIGHUP never stops the engine (nohup / a closing terminal
|
|
12
|
+
* must not kill an unattended engine); journaled, ignored
|
|
13
|
+
* stop-timeout a stop was requested but the process was still alive at
|
|
14
|
+
* the deadline; forced exit, lease released (NOT a crash)
|
|
15
|
+
* uncaught-exception journaled with stack, exit 70, lease deliberately NOT
|
|
16
|
+
* unhandled-rejection released so the next start / watcher sees a crash (also
|
|
17
|
+
* journaled when it follows a signal — every cause is kept)
|
|
18
|
+
* normal the loop returned after a stop request
|
|
19
|
+
* process-exit a bare `process.exit()` nobody else logged
|
|
20
|
+
*
|
|
21
|
+
* #920: the engine's log ended in "nothing to do" (an idle tick's line) with no
|
|
22
|
+
* error. Only SIGINT had a handler, so a SIGTERM/SIGHUP (another session's
|
|
23
|
+
* restart, a closing terminal) killed it with no log line and no lease release.
|
|
24
|
+
* SIGKILL/OOM remain unobservable by design — the stale heartbeat lease is what
|
|
25
|
+
* catches those (`engine-restarted-after-crash`, `stale-engine-lease-alert`).
|
|
26
|
+
*/
|
|
27
|
+
import type { Journal } from './journal';
|
|
28
|
+
/** Written on the FIRST stop signal: a later SIGKILL/timeout is a stop gone wrong, not a crash. */
|
|
29
|
+
export interface StoppingMarker {
|
|
30
|
+
pid: number;
|
|
31
|
+
signal: string;
|
|
32
|
+
at: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function writeStoppingMarker(dir: string, marker: StoppingMarker): void;
|
|
35
|
+
export declare function readStoppingMarker(dir: string): StoppingMarker | null;
|
|
36
|
+
export declare function clearStoppingMarker(dir: string): void;
|
|
37
|
+
/** Deadline for a requested stop to finish before the engine forces its own exit. */
|
|
38
|
+
export declare const DEFAULT_STOP_TIMEOUT_MS = 120000;
|
|
39
|
+
/** The slice of `process` the installer needs — injectable so tests never signal the test runner. */
|
|
40
|
+
export interface ExitProcess {
|
|
41
|
+
pid: number;
|
|
42
|
+
on(event: string, listener: (...args: never[]) => void): unknown;
|
|
43
|
+
removeListener(event: string, listener: (...args: never[]) => void): unknown;
|
|
44
|
+
exit(code?: number): never;
|
|
45
|
+
}
|
|
46
|
+
export interface EngineExitLogger {
|
|
47
|
+
/** Journal a graceful, loop-returned exit (`normal`). Idempotent with the rest. */
|
|
48
|
+
logNormalExit(detail?: string): void;
|
|
49
|
+
/** False after a crash: the lease must stay behind as the crash marker. */
|
|
50
|
+
shouldReleaseLease(): boolean;
|
|
51
|
+
/** The recorded reason, or null before any exit path fired. */
|
|
52
|
+
reason(): string | null;
|
|
53
|
+
dispose(): void;
|
|
54
|
+
}
|
|
55
|
+
export declare function installEngineExitLogging(opts: {
|
|
56
|
+
journal: Journal;
|
|
57
|
+
/** Ask the loop to stop after the current tick (graceful). */
|
|
58
|
+
requestStop: () => void;
|
|
59
|
+
/** Called once on the first stop signal, before `requestStop` (writes the `stopping` marker). */
|
|
60
|
+
markStopping?: (signal: string) => void;
|
|
61
|
+
/** Hard deadline for a requested stop (default {@link DEFAULT_STOP_TIMEOUT_MS}); 0 disables. */
|
|
62
|
+
stopTimeoutMs?: number;
|
|
63
|
+
proc?: ExitProcess;
|
|
64
|
+
now?: () => Date;
|
|
65
|
+
/** Timer factory — injectable so tests never wait out a real deadline. */
|
|
66
|
+
setTimer?: (fn: () => void, ms: number) => {
|
|
67
|
+
unref(): unknown;
|
|
68
|
+
};
|
|
69
|
+
}): EngineExitLogger;
|
|
70
|
+
//# sourceMappingURL=engine-exit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine-exit.d.ts","sourceRoot":"","sources":["../src/engine-exit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC,mGAAmG;AACnG,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAS7E;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAWrE;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAErD;AAED,qFAAqF;AACrF,eAAO,MAAM,uBAAuB,SAAU,CAAC;AAE/C,qGAAqG;AACrG,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC;IACjE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC;IAC7E,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,2EAA2E;IAC3E,kBAAkB,IAAI,OAAO,CAAC;IAC9B,+DAA+D;IAC/D,MAAM,IAAI,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,IAAI,IAAI,CAAC;CACjB;AAkBD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,OAAO,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,iGAAiG;IACjG,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,gGAAgG;IAChG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK;QAAE,KAAK,IAAI,OAAO,CAAA;KAAE,CAAC;CACjE,GAAG,gBAAgB,CAiGnB"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Engine termination logging (#945, #940 ask 3): the engine must never die
|
|
4
|
+
* silently. Every exit the process can observe writes one final `engine-exit`
|
|
5
|
+
* journal line with the reason (and stack for a crash):
|
|
6
|
+
*
|
|
7
|
+
* signal:<SIG> SIGTERM / SIGINT — graceful stop is requested (the loop
|
|
8
|
+
* finishes its tick, agents keep running). A `stopping`
|
|
9
|
+
* marker is written first, a second signal exits at once,
|
|
10
|
+
* and a hard deadline (`stopTimeoutMs`) bounds a wedged
|
|
11
|
+
* stop (`stop-timeout`)
|
|
12
|
+
* signal-ignored:SIGHUP SIGHUP never stops the engine (nohup / a closing terminal
|
|
13
|
+
* must not kill an unattended engine); journaled, ignored
|
|
14
|
+
* stop-timeout a stop was requested but the process was still alive at
|
|
15
|
+
* the deadline; forced exit, lease released (NOT a crash)
|
|
16
|
+
* uncaught-exception journaled with stack, exit 70, lease deliberately NOT
|
|
17
|
+
* unhandled-rejection released so the next start / watcher sees a crash (also
|
|
18
|
+
* journaled when it follows a signal — every cause is kept)
|
|
19
|
+
* normal the loop returned after a stop request
|
|
20
|
+
* process-exit a bare `process.exit()` nobody else logged
|
|
21
|
+
*
|
|
22
|
+
* #920: the engine's log ended in "nothing to do" (an idle tick's line) with no
|
|
23
|
+
* error. Only SIGINT had a handler, so a SIGTERM/SIGHUP (another session's
|
|
24
|
+
* restart, a closing terminal) killed it with no log line and no lease release.
|
|
25
|
+
* SIGKILL/OOM remain unobservable by design — the stale heartbeat lease is what
|
|
26
|
+
* catches those (`engine-restarted-after-crash`, `stale-engine-lease-alert`).
|
|
27
|
+
*/
|
|
28
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
29
|
+
if (k2 === undefined) k2 = k;
|
|
30
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
31
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
32
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
33
|
+
}
|
|
34
|
+
Object.defineProperty(o, k2, desc);
|
|
35
|
+
}) : (function(o, m, k, k2) {
|
|
36
|
+
if (k2 === undefined) k2 = k;
|
|
37
|
+
o[k2] = m[k];
|
|
38
|
+
}));
|
|
39
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
40
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
41
|
+
}) : function(o, v) {
|
|
42
|
+
o["default"] = v;
|
|
43
|
+
});
|
|
44
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
45
|
+
var ownKeys = function(o) {
|
|
46
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
47
|
+
var ar = [];
|
|
48
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
49
|
+
return ar;
|
|
50
|
+
};
|
|
51
|
+
return ownKeys(o);
|
|
52
|
+
};
|
|
53
|
+
return function (mod) {
|
|
54
|
+
if (mod && mod.__esModule) return mod;
|
|
55
|
+
var result = {};
|
|
56
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
57
|
+
__setModuleDefault(result, mod);
|
|
58
|
+
return result;
|
|
59
|
+
};
|
|
60
|
+
})();
|
|
61
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
62
|
+
exports.DEFAULT_STOP_TIMEOUT_MS = void 0;
|
|
63
|
+
exports.writeStoppingMarker = writeStoppingMarker;
|
|
64
|
+
exports.readStoppingMarker = readStoppingMarker;
|
|
65
|
+
exports.clearStoppingMarker = clearStoppingMarker;
|
|
66
|
+
exports.installEngineExitLogging = installEngineExitLogging;
|
|
67
|
+
const fs = __importStar(require("node:fs"));
|
|
68
|
+
const path = __importStar(require("node:path"));
|
|
69
|
+
const STOPPING_MARKER_FILE = 'engine-stopping.json';
|
|
70
|
+
function writeStoppingMarker(dir, marker) {
|
|
71
|
+
try {
|
|
72
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
73
|
+
fs.writeFileSync(path.join(dir, STOPPING_MARKER_FILE), `${JSON.stringify(marker)}\n`, {
|
|
74
|
+
mode: 0o600,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Advisory: a missing marker only downgrades "stop gone wrong" to "crash".
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function readStoppingMarker(dir) {
|
|
82
|
+
try {
|
|
83
|
+
const raw = JSON.parse(fs.readFileSync(path.join(dir, STOPPING_MARKER_FILE), 'utf8'));
|
|
84
|
+
if (raw === null || typeof raw !== 'object')
|
|
85
|
+
return null;
|
|
86
|
+
const m = raw;
|
|
87
|
+
return typeof m.pid === 'number' && typeof m.signal === 'string' && typeof m.at === 'string'
|
|
88
|
+
? { pid: m.pid, signal: m.signal, at: m.at }
|
|
89
|
+
: null;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function clearStoppingMarker(dir) {
|
|
96
|
+
fs.rmSync(path.join(dir, STOPPING_MARKER_FILE), { force: true });
|
|
97
|
+
}
|
|
98
|
+
/** Deadline for a requested stop to finish before the engine forces its own exit. */
|
|
99
|
+
exports.DEFAULT_STOP_TIMEOUT_MS = 120_000;
|
|
100
|
+
const CRASH_EXIT_CODE = 70;
|
|
101
|
+
const STOP_SIGNALS = ['SIGTERM', 'SIGINT'];
|
|
102
|
+
const SIGNAL_NUMBERS = {
|
|
103
|
+
SIGINT: 2,
|
|
104
|
+
SIGTERM: 15,
|
|
105
|
+
};
|
|
106
|
+
function describeError(err) {
|
|
107
|
+
if (err instanceof Error)
|
|
108
|
+
return err.stack ?? `${err.name}: ${err.message}`;
|
|
109
|
+
try {
|
|
110
|
+
return typeof err === 'string' ? err : JSON.stringify(err);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return String(err);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function installEngineExitLogging(opts) {
|
|
117
|
+
const proc = opts.proc ?? process;
|
|
118
|
+
const now = opts.now ?? (() => new Date());
|
|
119
|
+
let recorded = null;
|
|
120
|
+
let crashed = false;
|
|
121
|
+
let signalled = false;
|
|
122
|
+
const append = (reason, detail) => {
|
|
123
|
+
try {
|
|
124
|
+
opts.journal.append({ event: 'engine-exit', pid: proc.pid, reason, ...(detail ? { detail } : {}) }, now());
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// The exit path must still exit: an unwritable journal cannot block it.
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const record = (reason, detail) => {
|
|
131
|
+
if (recorded !== null)
|
|
132
|
+
return;
|
|
133
|
+
recorded = reason;
|
|
134
|
+
append(reason, detail);
|
|
135
|
+
};
|
|
136
|
+
/** A crash is always journaled (with its stack), even after an earlier signal line. */
|
|
137
|
+
const recordCrash = (reason, detail) => {
|
|
138
|
+
crashed = true;
|
|
139
|
+
if (recorded === null)
|
|
140
|
+
recorded = reason;
|
|
141
|
+
append(reason, detail);
|
|
142
|
+
};
|
|
143
|
+
const setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
144
|
+
const stopTimeoutMs = opts.stopTimeoutMs ?? exports.DEFAULT_STOP_TIMEOUT_MS;
|
|
145
|
+
const onSignal = (signal) => () => {
|
|
146
|
+
if (signalled) {
|
|
147
|
+
record('signal-repeat', `second ${signal}: exiting immediately`);
|
|
148
|
+
proc.exit(128 + SIGNAL_NUMBERS[signal]);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
signalled = true;
|
|
152
|
+
try {
|
|
153
|
+
opts.markStopping?.(signal);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
// advisory
|
|
157
|
+
}
|
|
158
|
+
record(`signal:${signal}`, 'graceful stop requested; spawned agents keep running');
|
|
159
|
+
if (stopTimeoutMs > 0) {
|
|
160
|
+
setTimer(() => {
|
|
161
|
+
append('stop-timeout', `stop requested by ${signal} ${stopTimeoutMs} ms ago but the engine is still running (wedged tick?); forcing exit`);
|
|
162
|
+
proc.exit(128 + SIGNAL_NUMBERS[signal]);
|
|
163
|
+
}, stopTimeoutMs).unref();
|
|
164
|
+
}
|
|
165
|
+
opts.requestStop();
|
|
166
|
+
};
|
|
167
|
+
const handlers = STOP_SIGNALS.map((sig) => [sig, onSignal(sig)]);
|
|
168
|
+
// A SIGHUP handler must exist: Node's default (terminate) would kill a nohup'd
|
|
169
|
+
// engine, and installing any handler overrides an inherited SIG_IGN anyway.
|
|
170
|
+
const onHup = () => {
|
|
171
|
+
append('signal-ignored:SIGHUP', 'SIGHUP received; ignored — the engine keeps running');
|
|
172
|
+
};
|
|
173
|
+
const onUncaught = (err) => {
|
|
174
|
+
recordCrash('uncaught-exception', describeError(err));
|
|
175
|
+
process.stderr.write(`⚠ sched engine crashed: ${describeError(err)}\n`);
|
|
176
|
+
proc.exit(CRASH_EXIT_CODE);
|
|
177
|
+
};
|
|
178
|
+
const onRejection = (err) => {
|
|
179
|
+
recordCrash('unhandled-rejection', describeError(err));
|
|
180
|
+
process.stderr.write(`⚠ sched engine crashed (unhandled rejection): ${describeError(err)}\n`);
|
|
181
|
+
proc.exit(CRASH_EXIT_CODE);
|
|
182
|
+
};
|
|
183
|
+
const onExit = (code) => {
|
|
184
|
+
record('process-exit', `exit code ${code}`);
|
|
185
|
+
};
|
|
186
|
+
for (const [sig, h] of handlers)
|
|
187
|
+
proc.on(sig, h);
|
|
188
|
+
proc.on('SIGHUP', onHup);
|
|
189
|
+
proc.on('uncaughtException', onUncaught);
|
|
190
|
+
proc.on('unhandledRejection', onRejection);
|
|
191
|
+
proc.on('exit', onExit);
|
|
192
|
+
return {
|
|
193
|
+
logNormalExit: (detail) => record('normal', detail),
|
|
194
|
+
shouldReleaseLease: () => !crashed,
|
|
195
|
+
reason: () => recorded,
|
|
196
|
+
dispose: () => {
|
|
197
|
+
for (const [sig, h] of handlers)
|
|
198
|
+
proc.removeListener(sig, h);
|
|
199
|
+
proc.removeListener('SIGHUP', onHup);
|
|
200
|
+
proc.removeListener('uncaughtException', onUncaught);
|
|
201
|
+
proc.removeListener('unhandledRejection', onRejection);
|
|
202
|
+
proc.removeListener('exit', onExit);
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=engine-exit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine-exit.js","sourceRoot":"","sources":["../src/engine-exit.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.js
CHANGED
|
@@ -2756,6 +2756,9 @@ async function runLoop(deps, configSource, shouldStop, onTick) {
|
|
|
2756
2756
|
const resolveConfig = typeof configSource === 'function' ? configSource : () => configSource;
|
|
2757
2757
|
const interval = (0, dispatch_1.resolveDispatch)(resolveConfig()).reconcileIntervalMs;
|
|
2758
2758
|
while (!shouldStop()) {
|
|
2759
|
+
// #945: heartbeat every tick, before the work — a wedged tick then shows as
|
|
2760
|
+
// a stale `updated_at` on a live pid, and an idle one as a fresh one.
|
|
2761
|
+
deps.store.touchEngineLease?.(deps.now());
|
|
2759
2762
|
try {
|
|
2760
2763
|
const result = tick(deps, resolveConfig());
|
|
2761
2764
|
onTick?.(result);
|