@miraland-labs/conduit-bridge 0.16.154 → 0.16.156
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/driver.js +57 -2
- package/dist/ensure-test-evidence.js +4 -2
- package/package.json +1 -1
package/dist/driver.js
CHANGED
|
@@ -1885,6 +1885,41 @@ const MAX_AGENT_OUTPUT = 8 * 1024 * 1024;
|
|
|
1885
1885
|
export const STDIO_DRAIN_MS = 2_000;
|
|
1886
1886
|
/** How long a stopped child may take to die before the run reports what it already has. */
|
|
1887
1887
|
const CHILD_STOP_GRACE_MS = 20_000;
|
|
1888
|
+
/**
|
|
1889
|
+
* An agent runs as the leader of its own process group, so what it starts in the background (a dev
|
|
1890
|
+
* server, a test watcher, a database) is stopped with it. Before, only the agent itself was
|
|
1891
|
+
* stopped: its background processes outlived the run and held files and ports on the computer
|
|
1892
|
+
* (acme-sim SV1b, 2026-10-10). Windows has no process groups; there only the agent is stopped.
|
|
1893
|
+
*/
|
|
1894
|
+
const OWN_PROCESS_GROUP = process.platform !== "win32";
|
|
1895
|
+
/** Process groups of the agents running now, stopped when the runner itself is stopped. */
|
|
1896
|
+
const runningGroups = new Set();
|
|
1897
|
+
let shutdownHookInstalled = false;
|
|
1898
|
+
function signalGroup(pid, signal) {
|
|
1899
|
+
try {
|
|
1900
|
+
process.kill(-pid, signal);
|
|
1901
|
+
}
|
|
1902
|
+
catch {
|
|
1903
|
+
// ESRCH: every process of the group is already gone.
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
/**
|
|
1907
|
+
* systemd stops a whole cgroup, but launchd stops only the runner's own process group, which an
|
|
1908
|
+
* agent no longer shares. Stop the agents' groups first, then let the signal end the runner as it
|
|
1909
|
+
* did before. A runner killed with SIGKILL cannot do this.
|
|
1910
|
+
*/
|
|
1911
|
+
function installShutdownHook() {
|
|
1912
|
+
if (shutdownHookInstalled)
|
|
1913
|
+
return;
|
|
1914
|
+
shutdownHookInstalled = true;
|
|
1915
|
+
for (const signal of ["SIGTERM", "SIGINT"]) {
|
|
1916
|
+
process.once(signal, () => {
|
|
1917
|
+
for (const pid of runningGroups)
|
|
1918
|
+
signalGroup(pid, "SIGKILL");
|
|
1919
|
+
process.kill(process.pid, signal);
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1888
1923
|
export function execute(executable, args, cwd, timeoutMs, fuel, fuelSource = "conduit", stdinText, signal, extraEnv,
|
|
1889
1924
|
/** The whole environment, for a run that must see less than an agent does (a probe instrument). */
|
|
1890
1925
|
env) {
|
|
@@ -1893,7 +1928,19 @@ env) {
|
|
|
1893
1928
|
cwd,
|
|
1894
1929
|
stdio: [stdinText !== undefined ? "pipe" : "ignore", "pipe", "pipe"],
|
|
1895
1930
|
env: env ?? boundedEnvironment(fuel, fuelSource, extraEnv),
|
|
1931
|
+
detached: OWN_PROCESS_GROUP,
|
|
1896
1932
|
});
|
|
1933
|
+
const group = OWN_PROCESS_GROUP ? child.pid : undefined;
|
|
1934
|
+
if (group !== undefined) {
|
|
1935
|
+
installShutdownHook();
|
|
1936
|
+
runningGroups.add(group);
|
|
1937
|
+
}
|
|
1938
|
+
const stop = (signal) => {
|
|
1939
|
+
if (group !== undefined)
|
|
1940
|
+
signalGroup(group, signal);
|
|
1941
|
+
else
|
|
1942
|
+
child.kill(signal);
|
|
1943
|
+
};
|
|
1897
1944
|
let stdout = "";
|
|
1898
1945
|
let stderr = "";
|
|
1899
1946
|
let timedOut = false;
|
|
@@ -1917,6 +1964,12 @@ env) {
|
|
|
1917
1964
|
// behind can hold the other end, and a kept handle is one the runner never closes.
|
|
1918
1965
|
child.stdout?.destroy();
|
|
1919
1966
|
child.stderr?.destroy();
|
|
1967
|
+
// The agent has answered; whatever it left running in its group stops now.
|
|
1968
|
+
if (group !== undefined) {
|
|
1969
|
+
runningGroups.delete(group);
|
|
1970
|
+
signalGroup(group, "SIGTERM");
|
|
1971
|
+
setTimeout(() => signalGroup(group, "SIGKILL"), 5_000).unref?.();
|
|
1972
|
+
}
|
|
1920
1973
|
resolve({
|
|
1921
1974
|
code,
|
|
1922
1975
|
stdout,
|
|
@@ -1928,8 +1981,8 @@ env) {
|
|
|
1928
1981
|
// SIGTERM first, then SIGKILL after 10 s: a child that ignores the polite stop must not keep
|
|
1929
1982
|
// the slot busy. The turn timer and the runner's abort use the same two steps.
|
|
1930
1983
|
const stopChild = () => {
|
|
1931
|
-
|
|
1932
|
-
setTimeout(() =>
|
|
1984
|
+
stop("SIGTERM");
|
|
1985
|
+
setTimeout(() => stop("SIGKILL"), 10_000).unref?.();
|
|
1933
1986
|
// A child that is already gone cannot close a pipe another process holds open.
|
|
1934
1987
|
setTimeout(() => settle(null), CHILD_STOP_GRACE_MS).unref?.();
|
|
1935
1988
|
};
|
|
@@ -1958,6 +2011,8 @@ env) {
|
|
|
1958
2011
|
if (settled)
|
|
1959
2012
|
return;
|
|
1960
2013
|
settled = true;
|
|
2014
|
+
if (group !== undefined)
|
|
2015
|
+
runningGroups.delete(group);
|
|
1961
2016
|
clearTimeout(timer);
|
|
1962
2017
|
signal?.removeEventListener("abort", stopChild);
|
|
1963
2018
|
reject(error);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { execFile } from "node:child_process";
|
|
7
7
|
import { promisify } from "node:util";
|
|
8
|
-
import { criterionKey, resolveCriterionReference } from "./driver.js";
|
|
8
|
+
import { boundedEnvironment, criterionKey, resolveCriterionReference } from "./driver.js";
|
|
9
9
|
import { isBoundedVerificationCommand, isRunnableVerificationCommand } from "./execution-class.js";
|
|
10
10
|
const execFileAsync = promisify(execFile);
|
|
11
11
|
const TEST_EVIDENCE_DETAILS_MIN = 32;
|
|
@@ -388,7 +388,9 @@ export async function runBoundedVerificationCommand(command, workspace, timeoutM
|
|
|
388
388
|
cwd: workspace,
|
|
389
389
|
timeout: timeoutMs,
|
|
390
390
|
maxBuffer: VERIFICATION_MAX_BUFFER_BYTES,
|
|
391
|
-
|
|
391
|
+
// The agent's environment, not the runner's: a gate that passes only with a variable the
|
|
392
|
+
// agent never sees ties the project to this computer (acme-sim DATABASE_URL, 2026-10-10).
|
|
393
|
+
env: boundedEnvironment(),
|
|
392
394
|
});
|
|
393
395
|
pending.child.stdout?.on("data", collect("stdout"));
|
|
394
396
|
pending.child.stderr?.on("data", collect("stderr"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miraland-labs/conduit-bridge",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.156",
|
|
4
4
|
"description": "Conduit Bridge CLI \u2014 join, connect, disconnect, multi-driver lanes, and run Claude Code / Codex / Cursor / OpenCode / Pi / Kiro / Antigravity / Grok Build agents for a Conduit organization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|