@bridge4dev/runner 0.55.0 → 0.55.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/dist/index.js +5 -5
- package/dist/session-cage.d.ts +6 -0
- package/dist/session-cage.js +28 -13
- package/dist/supervisor.js +16 -18
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1443,10 +1443,10 @@ async function cmdDoctor(args) {
|
|
|
1443
1443
|
print(` ${devbridgeSliceOverridePath()}`);
|
|
1444
1444
|
if (cage.mode === 'scope') {
|
|
1445
1445
|
const mb = (bytes) => bytes === null ? 'infinity' : `${Math.round(bytes / 1024 / 1024)} MB`;
|
|
1446
|
-
print(` per session
|
|
1447
|
-
print(` at the
|
|
1448
|
-
? 'OOMPolicy=continue — the kernel
|
|
1449
|
-
: 'this systemd does not take OOMPolicy on a scope
|
|
1446
|
+
print(` per session share ${mb(cage.memoryHighBytes)} (MemoryHigh — allocations are throttled above it), ceiling ${mb(cage.memoryMaxBytes)} (MemoryMax), swap ${mb(cage.swapMaxBytes)} (MemorySwapMax), TasksMax ${SESSION_TASKS_MAX}`);
|
|
1447
|
+
print(` at the ceiling ${cage.oomContinue
|
|
1448
|
+
? 'OOMPolicy=continue — the kernel stops the largest process in the session'
|
|
1449
|
+
: 'this systemd does not take OOMPolicy on a scope, so its own default applies — on systemd 253+ that stops the whole session'}`);
|
|
1450
1450
|
// What systemd has IN FORCE on the slice, beside the paths of the files
|
|
1451
1451
|
// that were supposed to put it there. «В файле 7680M, действует 6.0G» is the
|
|
1452
1452
|
// lesson of §5.5, and the drop-in of a slice can fail to land in exactly the
|
|
@@ -1457,7 +1457,7 @@ async function cmdDoctor(args) {
|
|
|
1457
1457
|
print(' NOT CAPPED — the drop-in above has not been applied.');
|
|
1458
1458
|
print(` Apply with: ${systemctlHint('daemon-reload')}`);
|
|
1459
1459
|
}
|
|
1460
|
-
print(` service ceiling ${mb(cage.serviceMemoryMaxBytes)} (
|
|
1460
|
+
print(` service ceiling ${mb(cage.serviceMemoryMaxBytes)} (a session's share is a third of the containing ceiling, its own ceiling is that minus one share)`);
|
|
1461
1461
|
print(` expand-env flag ${cage.expandEnvironmentFlag ? 'passed (systemd ≥ 254)' : 'not passed — this systemd does not know it, and --scope does not expand anyway'}`);
|
|
1462
1462
|
}
|
|
1463
1463
|
else {
|
package/dist/session-cage.d.ts
CHANGED
|
@@ -433,6 +433,12 @@ export declare function parseScopeMemoryStatus(files: {
|
|
|
433
433
|
*/
|
|
434
434
|
export declare function readScopeMemoryStatus(unit: string, readFile?: (p: string) => string): ScopeMemoryStatus | null;
|
|
435
435
|
export declare function markScopeOomKillsSeen(id: string, oomKills: number): void;
|
|
436
|
+
/**
|
|
437
|
+
* «a process in it was stopped» / «3 processes in it were stopped», so the verb
|
|
438
|
+
* agrees. Shared with the supervisor's feed notices, which count the same
|
|
439
|
+
* thing and must not word it differently.
|
|
440
|
+
*/
|
|
441
|
+
export declare function stoppedProcesses(count: number, where?: 'it' | 'this one'): string;
|
|
436
442
|
/**
|
|
437
443
|
* Snapshot the cgroup's verdict before systemd can take it away.
|
|
438
444
|
*
|
package/dist/session-cage.js
CHANGED
|
@@ -891,6 +891,16 @@ const oomKillsSeen = new Map();
|
|
|
891
891
|
export function markScopeOomKillsSeen(id, oomKills) {
|
|
892
892
|
oomKillsSeen.set(id, oomKills);
|
|
893
893
|
}
|
|
894
|
+
/**
|
|
895
|
+
* «a process in it was stopped» / «3 processes in it were stopped», so the verb
|
|
896
|
+
* agrees. Shared with the supervisor's feed notices, which count the same
|
|
897
|
+
* thing and must not word it differently.
|
|
898
|
+
*/
|
|
899
|
+
export function stoppedProcesses(count, where = 'it') {
|
|
900
|
+
return count === 1
|
|
901
|
+
? `a process in ${where} was stopped`
|
|
902
|
+
: `${count} processes in ${where} were stopped`;
|
|
903
|
+
}
|
|
894
904
|
const deaths = new Map();
|
|
895
905
|
/**
|
|
896
906
|
* Snapshot the cgroup's verdict before systemd can take it away.
|
|
@@ -909,7 +919,7 @@ export function rememberDeath(id, unit, readStatus = readScopeMemoryStatus) {
|
|
|
909
919
|
maxBytes: status.maxBytes,
|
|
910
920
|
// `oom` moves only where the limit that was hit belongs. A kill with our
|
|
911
921
|
// own counter still at zero came from an ancestor — the pool.
|
|
912
|
-
|
|
922
|
+
reached: status.ownLimitOom > 0 ? 'own-ceiling' : 'shared-pool',
|
|
913
923
|
});
|
|
914
924
|
}
|
|
915
925
|
/**
|
|
@@ -928,9 +938,9 @@ function rememberDeathFromResult(id) {
|
|
|
928
938
|
deaths.set(id, {
|
|
929
939
|
oomKills: 1,
|
|
930
940
|
maxBytes: sessionCage().memoryMaxBytes,
|
|
931
|
-
// systemd stopped the unit for an OOM inside it; which limit was
|
|
932
|
-
// in `Result`, and
|
|
933
|
-
|
|
941
|
+
// systemd stopped the unit for an OOM inside it; which limit was reached is
|
|
942
|
+
// not in `Result`, and neither claim may be guessed.
|
|
943
|
+
reached: 'unknown',
|
|
934
944
|
});
|
|
935
945
|
}
|
|
936
946
|
/**
|
|
@@ -947,16 +957,21 @@ export function explainMemoryDeath(id) {
|
|
|
947
957
|
if (!death)
|
|
948
958
|
return null;
|
|
949
959
|
deaths.delete(id);
|
|
950
|
-
const
|
|
951
|
-
const
|
|
952
|
-
if (death.
|
|
953
|
-
return (`
|
|
954
|
-
|
|
955
|
-
|
|
960
|
+
const stopped = stoppedProcesses(death.oomKills);
|
|
961
|
+
const ceiling = death.maxBytes === null ? '' : ` of ${Math.round(death.maxBytes / MIB)} MB`;
|
|
962
|
+
if (death.reached === 'own-ceiling') {
|
|
963
|
+
return (`This session reached its memory ceiling${ceiling} and ${stopped} — ` +
|
|
964
|
+
'send a message to carry on; if it keeps happening, the machine needs more memory.');
|
|
965
|
+
}
|
|
966
|
+
if (death.reached === 'shared-pool') {
|
|
967
|
+
return (`The machine ran out of memory for agent sessions and ${stoppedProcesses(death.oomKills, 'this one')} — ` +
|
|
968
|
+
'send a message to carry on; if it keeps happening, run fewer sessions at once or give the machine more memory.');
|
|
956
969
|
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
970
|
+
// Which ceiling was reached is unknowable here, so the sentence names no
|
|
971
|
+
// culprit: it has to be true whether this session was the greedy one or a
|
|
972
|
+
// bystander.
|
|
973
|
+
return (`There was not enough memory for this session and ${stopped} — ` +
|
|
974
|
+
'send a message to carry on; if it keeps happening, run fewer sessions at once or give the machine more memory.');
|
|
960
975
|
}
|
|
961
976
|
const realSystemctl = async (args) => {
|
|
962
977
|
const { stdout, stderr } = await execFileAsync('systemctl', ['--user', ...args], {
|
package/dist/supervisor.js
CHANGED
|
@@ -26,7 +26,7 @@ import { agentByDbValue } from './agent-registry.js';
|
|
|
26
26
|
import { invalidateAgentVersions, measureAgentVersions, } from './agent-versions.js';
|
|
27
27
|
import { rememberWorkspacePath } from './environment.js';
|
|
28
28
|
import { hostLoadChangedEnough, hostLoadHeartbeatDue, readHostLoad, HOST_LOAD_HEARTBEAT_MS, HOST_LOAD_SAMPLE_INTERVAL_MS, } from './host-load.js';
|
|
29
|
-
import { markScopeOomKillsSeen, readScopeMemoryStatus, sessionScopeUnitOf, } from './session-cage.js';
|
|
29
|
+
import { markScopeOomKillsSeen, readScopeMemoryStatus, sessionScopeUnitOf, stoppedProcesses, } from './session-cage.js';
|
|
30
30
|
import { composeMessageWithAttachments, saveAttachments, } from './attachments.js';
|
|
31
31
|
import { applyRewind, createCheckpoint, dropCheckpoints, listCheckpoints, MAX_BUSY_SESSIONS, previewRewind, pruneCheckpoints, } from './checkpoints.js';
|
|
32
32
|
import { DeliverMessageArgsSchema, QuestionAnswerArgsSchema } from './protocol.js';
|
|
@@ -715,18 +715,15 @@ export class Supervisor {
|
|
|
715
715
|
// where it was came from the slice over ALL sessions, and telling this
|
|
716
716
|
// session it went over its ceiling would be false (measured on this
|
|
717
717
|
// host: the killed neighbour read `max 0, oom 0, oom_kill 1`).
|
|
718
|
-
const
|
|
719
|
-
const
|
|
720
|
-
? 'a process'
|
|
721
|
-
: `${status.oomKills - base.oomKills} processes`;
|
|
718
|
+
const ownCeiling = status.ownLimitOom > base.ownOom;
|
|
719
|
+
const stopped = stoppedProcesses(status.oomKills - base.oomKills);
|
|
722
720
|
this.sendEvent(running, 'notice', {
|
|
723
721
|
level: 'warn',
|
|
724
|
-
text:
|
|
725
|
-
? `This session
|
|
726
|
-
'
|
|
727
|
-
: `The machine ran out of memory for agent sessions
|
|
728
|
-
|
|
729
|
-
'It is still running; the command that was in flight most likely died with it.',
|
|
722
|
+
text: ownCeiling
|
|
723
|
+
? `This session reached its${status.maxBytes === null ? '' : ` ${mb(status.maxBytes)}`} memory ceiling and ${stopped} — ` +
|
|
724
|
+
'check the output of the command that was running.'
|
|
725
|
+
: `The machine ran out of memory for agent sessions and ${stoppedProcesses(status.oomKills - base.oomKills, 'this one')} — ` +
|
|
726
|
+
'run fewer sessions at once, or give the machine more memory.',
|
|
730
727
|
});
|
|
731
728
|
// So a death minutes later is not blamed on a kill the feed already carries.
|
|
732
729
|
markScopeOomKillsSeen(running.descriptor.id, status.oomKills);
|
|
@@ -744,6 +741,10 @@ export class Supervisor {
|
|
|
744
741
|
*/
|
|
745
742
|
const throttled = status.highEvents > base.highEvents ||
|
|
746
743
|
(status.highBytes !== null && status.currentBytes > status.highBytes);
|
|
744
|
+
// `memory.high` reads `max` on a machine where the share never applied —
|
|
745
|
+
// then there is no number to name, and «its 0 MB share» is worse than no
|
|
746
|
+
// number at all.
|
|
747
|
+
const share = status.highBytes === null ? '' : ` ${mb(status.highBytes)}`;
|
|
747
748
|
let { brakedSince, warnedLong, calmTicks } = base;
|
|
748
749
|
if (throttled) {
|
|
749
750
|
calmTicks = 0;
|
|
@@ -752,18 +753,15 @@ export class Supervisor {
|
|
|
752
753
|
warnedLong = false;
|
|
753
754
|
this.sendEvent(running, 'notice', {
|
|
754
755
|
level: 'warn',
|
|
755
|
-
text: `This session
|
|
756
|
-
'Heavy commands will finish slower until memory is freed.',
|
|
756
|
+
text: `This session has hit its${share} memory share — commands run slower until it uses less memory.`,
|
|
757
757
|
});
|
|
758
758
|
}
|
|
759
759
|
else if (!warnedLong && now - brakedSince >= longMs) {
|
|
760
760
|
warnedLong = true;
|
|
761
761
|
this.sendEvent(running, 'notice', {
|
|
762
762
|
level: 'warn',
|
|
763
|
-
text: `
|
|
764
|
-
|
|
765
|
-
? ''
|
|
766
|
-
: ` The hard ceiling, where a process would be killed, is ${mb(status.maxBytes)}.`}`,
|
|
763
|
+
text: `This session has been over its${share} memory share for ${Math.round(longMs / 60_000)} min and is now using ${mb(status.currentBytes)} — ` +
|
|
764
|
+
'if a command is stuck rather than slow, stop the turn or pause the session.',
|
|
767
765
|
});
|
|
768
766
|
}
|
|
769
767
|
}
|
|
@@ -776,7 +774,7 @@ export class Supervisor {
|
|
|
776
774
|
warnedLong = false;
|
|
777
775
|
this.sendEvent(running, 'notice', {
|
|
778
776
|
level: 'info',
|
|
779
|
-
text: `
|
|
777
|
+
text: `This session is back under its${share} memory share — it runs at full speed again.`,
|
|
780
778
|
});
|
|
781
779
|
}
|
|
782
780
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const RUNNER_VERSION = "0.55.
|
|
1
|
+
export declare const RUNNER_VERSION = "0.55.1";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
package/package.json
CHANGED