@intentic/constants 1.390.0 → 1.392.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/README.md +6 -2
- package/package.json +1 -1
- package/src/heavy/heavy-exec.cjs +30 -3
- package/src/heavy/heavy-rules.cjs +41 -1
- package/src/heavy/heavy-rules.d.cts +3 -0
- package/src/memory-room.d.mts +2 -2
- package/src/memory-room.mjs +4 -0
package/README.md
CHANGED
|
@@ -31,10 +31,14 @@ flowchart LR
|
|
|
31
31
|
on any Node an agent's project pins. A rule may also match the program's working directory (`cwd`), name its memory
|
|
32
32
|
price (`size`), and be judged under a program that already holds a slot (`nests`): the editor's `vue-tsc` takes the
|
|
33
33
|
`vue-typecheck` lane, one at a time, from under a `pnpm -r typecheck`. Only a pool no `nests` rule names may wait on
|
|
34
|
-
one that does, so the waits cannot form a cycle.
|
|
34
|
+
one that does, so the waits cannot form a cycle. `cargoBuildArgs` translates supported `cargo run` options into
|
|
35
|
+
a build; `heavy-exec.cjs` queues that build in a child and starts the original run once its slot is released.
|
|
36
|
+
Unknown arguments retain the original invocation. The prebuild stays local even when ordinary Cargo builds
|
|
37
|
+
are offloaded, so its artifacts warm the original run.
|
|
35
38
|
- `memory-room`'s `SIZE_BYTES` prices a toolchain command by its measured shape instead of its class: `targeted` (a test
|
|
36
39
|
run aimed at named files), `editor-typecheck` (the editor's full `vue-tsc`, 7 GiB) and `scoped-typecheck` (`lsp diag`
|
|
37
|
-
on a Vue package, 4 GiB).
|
|
40
|
+
on a Vue package, 4 GiB), and `cargo-build` (7 GiB per admission, based on Shinobi's 2026-11-02 Rust build peaks).
|
|
41
|
+
Two Cargo reservations fit an 18 GiB sandbox with headroom; the price does not cap compiler RSS.
|
|
38
42
|
- `memory-room` is the one formula for whether the sandbox has room (limit, used, free, stall, and what each workload
|
|
39
43
|
class costs). The daemon's resource budget judges by it, and it is also the `memory-room` command `queue-run` asks:
|
|
40
44
|
it asks the daemon's room socket, and applies the formula itself where no daemon answers. (2026-11-02) A reading may
|
package/package.json
CHANGED
package/src/heavy/heavy-exec.cjs
CHANGED
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
const { accessSync, constants } = require("node:fs");
|
|
9
9
|
const { delimiter, join, resolve } = require("node:path");
|
|
10
|
-
const {
|
|
10
|
+
const { spawnSync } = require("node:child_process");
|
|
11
|
+
const { constants: osConstants } = require("node:os");
|
|
12
|
+
const { takeTurn, pendingSpec } = require("./heavy-turn.cjs");
|
|
13
|
+
const { cargoBuildArgs, matchInvocation } = require("./heavy-rules.cjs");
|
|
11
14
|
|
|
12
15
|
const [shims = "", program = "", ...args] = process.argv.slice(2);
|
|
13
16
|
|
|
@@ -30,8 +33,33 @@ if (real === undefined) {
|
|
|
30
33
|
process.stderr.write(`${program}: command not found\n`);
|
|
31
34
|
process.exit(127);
|
|
32
35
|
}
|
|
36
|
+
// The child holds the slot only while compiling. The original run then starts with its untouched environment,
|
|
37
|
+
// so a server inherits neither the build's slot nor its toolchain priority. This build stays local to warm its artifacts.
|
|
38
|
+
let build;
|
|
39
|
+
let buildEnv = process.env;
|
|
40
|
+
try {
|
|
41
|
+
const spec = pendingSpec();
|
|
42
|
+
const translated = program === "cargo" ? cargoBuildArgs(args) : undefined;
|
|
43
|
+
if (spec !== undefined && translated !== undefined &&
|
|
44
|
+
matchInvocation([program, ...translated].join(" "), spec.rules, undefined, { cwd: process.cwd() })?.id === "cargo") {
|
|
45
|
+
build = translated;
|
|
46
|
+
const offload = { ...spec.offload };
|
|
47
|
+
delete offload.cargo;
|
|
48
|
+
buildEnv = { ...process.env, INTENTIC_HEAVY: JSON.stringify({ ...spec, offload }) };
|
|
49
|
+
}
|
|
50
|
+
} catch {
|
|
51
|
+
// allow(silent-catch): an unreadable table preserves the original invocation
|
|
52
|
+
}
|
|
53
|
+
if (build !== undefined) {
|
|
54
|
+
const compiled = spawnSync(process.execPath, [__filename, shims, program, ...build], { stdio: "inherit", env: buildEnv });
|
|
55
|
+
if (compiled.status !== 0) {
|
|
56
|
+
process.exit(compiled.status ?? (compiled.signal === null ? 1 : 128 + (osConstants.signals[compiled.signal] ?? 0)));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
33
59
|
try {
|
|
34
|
-
|
|
60
|
+
if (build === undefined) {
|
|
61
|
+
takeTurn({ program, args, command: [real, ...args] });
|
|
62
|
+
}
|
|
35
63
|
} catch {
|
|
36
64
|
// allow(silent-catch): a table that does not parse runs the program as if unjudged
|
|
37
65
|
}
|
|
@@ -39,6 +67,5 @@ try {
|
|
|
39
67
|
if (typeof process.execve === "function") {
|
|
40
68
|
process.execve(real, [program, ...args], process.env);
|
|
41
69
|
}
|
|
42
|
-
const { spawnSync } = require("node:child_process");
|
|
43
70
|
const run = spawnSync(real, args, { stdio: "inherit", env: process.env, argv0: program });
|
|
44
71
|
process.exit(run.status ?? 1);
|
|
@@ -113,13 +113,14 @@ const SHIPPED_HEAVY_COMMANDS = Object.freeze({
|
|
|
113
113
|
{ id: "typechecker", pattern: "(?<![-.])\\b(tsc|tsgo|vue-tsc)\\b(?![-.])" },
|
|
114
114
|
{ id: "turbo-fanout", pattern: "\\bturbo\\b.*\\brun\\b.*\\b(build|test|typecheck|check)\\b" },
|
|
115
115
|
{ id: "package-script", pattern: "\\b(pnpm|npm|yarn|bun)\\b.*\\b(test|typecheck|verify|check|build)\\b" },
|
|
116
|
-
{ id: "cargo", pattern: "^cargo\\s+(\\+\\S+\\s+)?(build|test|check|clippy|bench|doc)\\b" },
|
|
116
|
+
{ id: "cargo", pattern: "^cargo\\s+(\\+\\S+\\s+)?(build|test|check|clippy|bench|doc)\\b", size: "cargo-build" },
|
|
117
117
|
]),
|
|
118
118
|
});
|
|
119
119
|
|
|
120
120
|
// Every rule an earlier release shipped, as it shipped it (matched against the agent's shell line then). A stored rule
|
|
121
121
|
// equal to one of these was seeded, not chosen, so converting a stored file drops it and the current rule takes over.
|
|
122
122
|
const EARLIER_SHIPPED_RULES = Object.freeze([
|
|
123
|
+
{ id: "cargo", pattern: "^cargo\\s+(\\+\\S+\\s+)?(build|test|check|clippy|bench|doc)\\b" },
|
|
123
124
|
{
|
|
124
125
|
id: "read-only",
|
|
125
126
|
pattern: "^\\s*(grep|rg|iq|ag|cat|bat|head|tail|less|ls|find|fd|wc|which|echo|git\\s+(log|status|diff|show|blame))\\b",
|
|
@@ -342,6 +343,44 @@ const sizeArgs = (match, invocation) => {
|
|
|
342
343
|
return ["vitest", "package-script"].includes(match.id) && targetedRun(invocation) ? ["--size", "targeted"] : [];
|
|
343
344
|
};
|
|
344
345
|
|
|
346
|
+
// Translate only known Cargo options. Unknown flags, aliases and help requests keep the original invocation.
|
|
347
|
+
// Runtime arguments after `--` belong only to `run`; a toolchain selector belongs before either verb.
|
|
348
|
+
const cargoBuildArgs = (args) => {
|
|
349
|
+
const prefix = args[0]?.startsWith("+") ? [args[0]] : [];
|
|
350
|
+
if (args[prefix.length] !== "run") {
|
|
351
|
+
return undefined;
|
|
352
|
+
}
|
|
353
|
+
const build = [...prefix, "build"];
|
|
354
|
+
const values = new Set([
|
|
355
|
+
"--package", "--bin", "--example", "--features", "--profile", "--target", "--target-dir", "--manifest-path",
|
|
356
|
+
"--jobs", "--color", "--message-format", "--config", "--lockfile-path", "-p", "-F", "-j", "-Z",
|
|
357
|
+
]);
|
|
358
|
+
const switches = new Set([
|
|
359
|
+
"--release", "-r", "--all-features", "--no-default-features", "--locked", "--offline", "--frozen",
|
|
360
|
+
"--verbose", "--quiet", "-q", "--timings", "--future-incompat-report", "--ignore-rust-version", "--keep-going",
|
|
361
|
+
]);
|
|
362
|
+
for (let i = prefix.length + 1; i < args.length; i++) {
|
|
363
|
+
const arg = args[i];
|
|
364
|
+
if (arg === "--") {
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
if (switches.has(arg) || /^-v+$/u.test(arg) || /^-(p|F|j|Z).+/u.test(arg)) {
|
|
368
|
+
build.push(arg);
|
|
369
|
+
} else if (values.has(arg)) {
|
|
370
|
+
const value = args[++i];
|
|
371
|
+
if (value === undefined || value === "--" || (value.startsWith("-") && !(arg === "-j" || arg === "--jobs"))) {
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
374
|
+
build.push(arg, value);
|
|
375
|
+
} else if (arg.includes("=") && (values.has(arg.split("=")[0]) || arg.startsWith("--timings="))) {
|
|
376
|
+
build.push(arg);
|
|
377
|
+
} else {
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return build;
|
|
382
|
+
};
|
|
383
|
+
|
|
345
384
|
// When a slot held this long is worth a line in the log: half of what its own rule lets it hold, never for a rule that
|
|
346
385
|
// lets it hold forever.
|
|
347
386
|
const holdWarnSeconds = (maxHold) => (maxHold > 0 ? maxHold / 2 : undefined);
|
|
@@ -363,4 +402,5 @@ module.exports = {
|
|
|
363
402
|
holdWarnSeconds,
|
|
364
403
|
ruleById,
|
|
365
404
|
heldPoolsOf,
|
|
405
|
+
cargoBuildArgs,
|
|
366
406
|
};
|
|
@@ -95,3 +95,6 @@ export function holdWarnSeconds(maxHold: number): number | undefined;
|
|
|
95
95
|
export function ruleById(config: HeavyCommands, id: string): HeavyCommandRule | undefined;
|
|
96
96
|
// The pools the programs above one already hold slots of, from INTENTIC_HEAVY_HELD and INTENTIC_QUEUE_SLOT.
|
|
97
97
|
export function heldPoolsOf(env: Readonly<Record<string, string | undefined>>, config: HeavyCommands): string[];
|
|
98
|
+
|
|
99
|
+
// The build before a cargo run, or undefined when its arguments cannot be translated safely.
|
|
100
|
+
export function cargoBuildArgs(args: readonly string[]): string[] | undefined;
|
package/src/memory-room.d.mts
CHANGED
|
@@ -5,8 +5,8 @@ export const COST_BYTES: Readonly<Record<RoomWorkload, number>>;
|
|
|
5
5
|
export const PERSON_RESERVE_BYTES: number;
|
|
6
6
|
export const TARGETED_RUN_BYTES: number;
|
|
7
7
|
// A toolchain command priced by its named shape instead of its class's cost (SIZE_BYTES): one aimed at named files,
|
|
8
|
-
// the editor's full typecheck, a changed-files Vue typecheck.
|
|
9
|
-
export type RoomSize = "targeted" | "editor-typecheck" | "scoped-typecheck";
|
|
8
|
+
// the editor's full typecheck, a changed-files Vue typecheck, a Cargo build.
|
|
9
|
+
export type RoomSize = "targeted" | "editor-typecheck" | "scoped-typecheck" | "cargo-build";
|
|
10
10
|
export const SIZE_BYTES: Readonly<Record<RoomSize, number>>;
|
|
11
11
|
export function isRoomSize(size: string): size is RoomSize;
|
|
12
12
|
export const TEST_PROCESS_BYTES: Readonly<{ fanOutWorker: number; standaloneWorker: number; typecheck: number; ceiling: number }>;
|
package/src/memory-room.mjs
CHANGED
|
@@ -34,6 +34,10 @@ export const TARGETED_RUN_BYTES = 512 * MIB;
|
|
|
34
34
|
// `size`, which queue-run passes as `--size <name>`). One name per measured shape:
|
|
35
35
|
export const SIZE_BYTES = Object.freeze({
|
|
36
36
|
targeted: TARGETED_RUN_BYTES,
|
|
37
|
+
// Shinobi resource-metrics.jsonl, 2026-11-02: `other` RSS peaked at 12.70 GiB at 19:25 with one cargo slot held,
|
|
38
|
+
// and 13.88 GiB at 23:14 with two. Rust was unclassified then, so this includes other work and unqueued runs.
|
|
39
|
+
// Admission estimate per slot: two fit the 18 GiB sandbox with headroom. Cargo's jobs still bound its fan-out.
|
|
40
|
+
"cargo-build": 7 * GIB,
|
|
37
41
|
// The editor's full typecheck, `vue-tsc` over `_editor/web`, or `_site/demo`'s, which compiles the same source:
|
|
38
42
|
// 6.8 GiB at its peak (measured 2026-10-26, cold and warm alike), under the 8 GiB heap its script gives it.
|
|
39
43
|
"editor-typecheck": 7 * GIB,
|