@intentic/constants 1.334.0 → 1.336.0

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.d.ts CHANGED
@@ -10,7 +10,7 @@ export declare const PUBLIC_DIR = "public";
10
10
  export declare const MEMORY_FILE = "AGENTS.md";
11
11
  export declare const FIELD_NOTES_FILE = ".intentic/config/field-notes.toon";
12
12
  export declare const HOST_STATE_ROOT = "/opt/intentic";
13
- export declare const LEGAL_VERSION = "2026-10-04";
13
+ export declare const LEGAL_VERSION = "2026-10-13";
14
14
  export declare const LEGAL_CONTACT_EMAIL = "contact@intentic.dev";
15
15
  export declare const LEGAL_ENTITY_NAME = "Artur Kurowski, trading as radarsu";
16
16
  export declare const LEGAL_ENTITY_COUNTRY = "Poland";
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ export const PUBLIC_DIR = "public";
10
10
  export const MEMORY_FILE = "AGENTS.md";
11
11
  export const FIELD_NOTES_FILE = ".intentic/config/field-notes.toon";
12
12
  export const HOST_STATE_ROOT = "/opt/intentic";
13
- export const LEGAL_VERSION = "2026-10-04";
13
+ export const LEGAL_VERSION = "2026-10-13";
14
14
  export const LEGAL_CONTACT_EMAIL = "contact@intentic.dev";
15
15
  export const LEGAL_ENTITY_NAME = "Artur Kurowski, trading as radarsu";
16
16
  export const LEGAL_ENTITY_COUNTRY = "Poland";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentic/constants",
3
- "version": "1.334.0",
3
+ "version": "1.336.0",
4
4
  "description": "Shared constants for the intentic packages, ports, paths, and image references the daemon, CLIs and desktop app all agree on",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,6 +9,8 @@ export type RoomSize = "targeted";
9
9
  export const TEST_PROCESS_BYTES: Readonly<{ fanOutWorker: number; standaloneWorker: number; typecheck: number; ceiling: number }>;
10
10
  export const STALL_PERCENT: number;
11
11
  export const STALL_SUSTAINED_PERCENT: number;
12
+ export const IO_STALL_PERCENT: number;
13
+ export const IO_STALL_SUSTAINED_PERCENT: number;
12
14
  export const SWAP_FULL_SHARE: number;
13
15
  export const RESERVATION_MS: number;
14
16
  export const ROOM_SOCKET: string;
@@ -30,6 +32,9 @@ export interface MemoryReading {
30
32
  readonly stallPercent: number;
31
33
  // Memory PSI `full avg60`, in percent, from the same file; 0 when healthy or unreported.
32
34
  readonly stallSustainedPercent?: number | undefined;
35
+ // IO PSI `full avg10` and `avg60`, in percent: the cgroup's, else the machine's; 0 when healthy or unreported.
36
+ readonly ioStallPercent?: number | undefined;
37
+ readonly ioStallSustainedPercent?: number | undefined;
33
38
  // memory.events `oom_kill`, cumulative; undefined without a cgroup.
34
39
  readonly oomKills: number | undefined;
35
40
  }
@@ -23,6 +23,12 @@
23
23
  // stall memory PSI `full`: avg10, the share of the last ten seconds in which everything waited on memory, and avg60,
24
24
  // the last minute's. A sandbox paging hard flickers around any avg10 threshold (19, 11, 15, 4, 18% in five
25
25
  // minutes of the reading above, never 20), so a lower bar on the minute catches what the spikes miss.
26
+ // disk io PSI `full` the same way, for work nobody is waiting on only: swap paged in from disk and a build's writes
27
+ // stall everything on the disk, the daemon's own event loop included, before memory PSI says much. Measured
28
+ // 2026-10-10..13: of 29 daemon freezes of 5 s or more, 20 had disk `full` avg10 at 30% or past it, 18 had
29
+ // memory's at 20%, and the one that failed a turn's setup on 2026-10-13 had disk 42% and memory 7%. Disk
30
+ // reached 30% in 1.5% of the minute samples of those days, so holding background work then costs little. A
31
+ // person's turn is never refused for it: a busy disk slows a turn, it does not kill one.
26
32
  import { spawnSync } from "node:child_process";
27
33
  import { existsSync, readFileSync, realpathSync } from "node:fs";
28
34
  import { request } from "node:http";
@@ -74,6 +80,9 @@ export const PERSON_RESERVE_BYTES = COST_BYTES.agentRuntime;
74
80
  export const STALL_PERCENT = 20;
75
81
  // Percent of full avg60 at which it does: lower than STALL_PERCENT, since a minute's average has smoothed the spikes.
76
82
  export const STALL_SUSTAINED_PERCENT = 10;
83
+ // Percent of io `full` avg10, and of avg60, at which work nobody is waiting on waits for the disk (the `disk` clause).
84
+ export const IO_STALL_PERCENT = 30;
85
+ export const IO_STALL_SUSTAINED_PERCENT = 20;
77
86
  // The share of its swap limit past which swap counts as full, and the swapped pages count as used.
78
87
  export const SWAP_FULL_SHARE = 0.9;
79
88
  export const RESERVATION_MS = 90_000;
@@ -91,8 +100,10 @@ export const READING_FILES = Object.freeze([
91
100
  `${CGROUP}/memory.swap.max`,
92
101
  `${CGROUP}/memory.pressure`,
93
102
  `${CGROUP}/memory.events`,
103
+ `${CGROUP}/io.pressure`,
94
104
  "/proc/meminfo",
95
105
  "/proc/pressure/memory",
106
+ "/proc/pressure/io",
96
107
  ]);
97
108
 
98
109
  const numeric = (text) => {
@@ -119,6 +130,13 @@ const fullAvg = (text, window) => {
119
130
 
120
131
  const kib = (fields, key) => (fields[key] === undefined ? undefined : fields[key] * 1024);
121
132
 
133
+ // One resource's PSI `full` averages: the cgroup's where it keeps one, else the machine's, avg60 from the same file avg10
134
+ // came from; 0 where nothing reports it.
135
+ const stallOf = (read, resource) => {
136
+ const text = [read(`${CGROUP}/${resource}.pressure`), read(`/proc/pressure/${resource}`)].find((file) => fullAvg(file, "avg10") !== undefined);
137
+ return { recent: fullAvg(text, "avg10") ?? 0, sustained: fullAvg(text, "avg60") ?? 0 };
138
+ };
139
+
122
140
  /** The reading, from a reader that answers each of READING_FILES with its text or undefined. */
123
141
  export const readingFrom = (read) => {
124
142
  const meminfo = keyed(read("/proc/meminfo"));
@@ -144,15 +162,18 @@ export const readingFrom = (read) => {
144
162
  swapLimit = kib(meminfo, "SwapTotal") ?? Number.POSITIVE_INFINITY;
145
163
  }
146
164
  // The cgroup's pressure where it keeps one, else the machine's; avg60 from the same file avg10 came from.
147
- const pressure = [read(`${CGROUP}/memory.pressure`), read("/proc/pressure/memory")].find((text) => fullAvg(text, "avg10") !== undefined);
165
+ const memory = stallOf(read, "memory");
166
+ const disk = stallOf(read, "io");
148
167
  return {
149
168
  limitBytes: Number.isFinite(bound) ? bound : undefined,
150
169
  usedBytes,
151
170
  swapBytes,
152
171
  swapLimitBytes: Number.isFinite(swapLimit) ? swapLimit : undefined,
153
172
  availableBytes: kib(meminfo, "MemAvailable"),
154
- stallPercent: fullAvg(pressure, "avg10") ?? 0,
155
- stallSustainedPercent: fullAvg(pressure, "avg60") ?? 0,
173
+ stallPercent: memory.recent,
174
+ stallSustainedPercent: memory.sustained,
175
+ ioStallPercent: disk.recent,
176
+ ioStallSustainedPercent: disk.sustained,
156
177
  oomKills: keyed(read(`${CGROUP}/memory.events`)).oom_kill,
157
178
  };
158
179
  };
@@ -215,6 +236,10 @@ export const judge = (reading, { workload, attended, reservedBytes = 0, size })
215
236
  if (stallSustainedPercent >= STALL_SUSTAINED_PERCENT) {
216
237
  return shortOf(`The sandbox is short of memory: for ${Math.round(stallSustainedPercent)}% of the last minute, everything in it was waiting on memory`);
217
238
  }
239
+ const disk = attended ? undefined : diskStalled(reading);
240
+ if (disk !== undefined) {
241
+ return shortOf(disk);
242
+ }
218
243
  if (freeBytes >= need) {
219
244
  return { verdict: "run", needBytes: need, freeBytes, reservedBytes };
220
245
  }
@@ -235,6 +260,18 @@ export const judge = (reading, { workload, attended, reservedBytes = 0, size })
235
260
  return shortOf(`Sandbox memory is low: ${used}${held}`, { limitBytes, residentBytes: usedBytes - swapBytes, swapBytes });
236
261
  };
237
262
 
263
+ // Why work nobody is waiting on should wait for the disk, or undefined while it need not (the `disk` clause).
264
+ const diskStalled = (reading) => {
265
+ const recent = reading.ioStallPercent ?? 0;
266
+ const sustained = reading.ioStallSustainedPercent ?? 0;
267
+ if (recent >= IO_STALL_PERCENT) {
268
+ return `The sandbox's disk is saturated: for ${Math.round(recent)}% of the last ten seconds, everything in it was waiting on the disk`;
269
+ }
270
+ return sustained >= IO_STALL_SUSTAINED_PERCENT
271
+ ? `The sandbox's disk is saturated: for ${Math.round(sustained)}% of the last minute, everything in it was waiting on the disk`
272
+ : undefined;
273
+ };
274
+
238
275
  /**
239
276
  * Free memory as the formula counts it (the limit less what counts against it: the resident part, and swap too once swap
240
277
  * is full), or undefined where nothing bounds or measures it.