@runuai/host 0.9.151 → 0.9.152

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.
@@ -1685,7 +1685,11 @@ export async function stopAnchoredEngines(
1685
1685
  request.taskId,
1686
1686
  String(request.runEpoch),
1687
1687
  named === undefined ? "all" : "named",
1688
- (named ?? []).map(idPathSegment).join("\n"),
1688
+ // Every provider refuses an empty argv entry, and "all" names no
1689
+ // account: a lone newline stands for the empty list (the helper drops
1690
+ // blank names). Live 2026-10-11: the empty string failed every
1691
+ // stop-all, so no cleanup, recovery sweep or relaunch could finish.
1692
+ (named ?? []).map(idPathSegment).join("\n") || "\n",
1689
1693
  String(anchorStopSeconds),
1690
1694
  carrierRoots(),
1691
1695
  ],
@@ -3087,7 +3087,14 @@ export class Orchestrator {
3087
3087
  );
3088
3088
  try {
3089
3089
  await Promise.all(earlier.map((retired) => this.retireInRuntime(retired)));
3090
- } catch {
3090
+ } catch (error) {
3091
+ // Live 2026-10-11: this refusal was silent, so agents that never started
3092
+ // left nothing in the log to say why.
3093
+ console.warn(
3094
+ `[orchestrator] ${slot.identity.taskId}/${agentId}: not starting the agent, an earlier start could not be retired in its runtime (${
3095
+ error instanceof Error ? error.message : String(error)
3096
+ })`,
3097
+ );
3091
3098
  throw new EngineLaunchRefused("an earlier start of this agent could not be retired in its runtime yet");
3092
3099
  }
3093
3100
  }
@@ -100,9 +100,19 @@ export interface RuntimeNamespace {
100
100
  /** macOS (11 and later): fail if any component of the path is a link. */
101
101
  const O_NOFOLLOW_ANY = 0x20000000;
102
102
  const DIRECTORY = constants.O_RDONLY | constants.O_DIRECTORY;
103
- /** macOS: a directory, a link nowhere in its path (the last component
104
- * refused by O_NOFOLLOW as well). */
105
- const DIRECTORY_NOFOLLOW_ANY = DIRECTORY | O_NOFOLLOW_ANY | constants.O_NOFOLLOW;
103
+ /** macOS: a directory, a link nowhere in its path, the last component
104
+ * included. Never with O_NOFOLLOW: O_NOFOLLOW_ANY already refuses a final
105
+ * link, and macOS 27 rejects the two together with EINVAL (live
106
+ * 2026-10-11: every workspace read and write failed on the Studio). */
107
+ const DARWIN = process.platform === "darwin";
108
+ /** Elsewhere (tests forcing this mode on Linux, where O_NOFOLLOW_ANY means
109
+ * nothing) O_NOFOLLOW stays, so a final link is still refused there. */
110
+ const DIRECTORY_NOFOLLOW_ANY = DIRECTORY | O_NOFOLLOW_ANY | (DARWIN ? 0 : constants.O_NOFOLLOW);
111
+ /** The flags one file is opened with in this mode: on macOS `flags` without
112
+ * O_NOFOLLOW, which O_NOFOLLOW_ANY covers and cannot be combined with. */
113
+ function nofollowAnyFlags(flags: number): number {
114
+ return (DARWIN ? flags & ~constants.O_NOFOLLOW : flags) | O_NOFOLLOW_ANY;
115
+ }
106
116
 
107
117
  type Containment = "proc-fd" | "nofollow-any" | "verify";
108
118
  let containment: Containment | null = null;
@@ -128,7 +138,9 @@ function probeContainment(): Containment {
128
138
  }
129
139
  }
130
140
  if (process.platform === "darwin") {
131
- const scratch = mkdtempSync(join(tmpdir(), "uai-nofollow-any-"));
141
+ // Resolved: macOS's temporary directory is below /var, itself a link,
142
+ // which O_NOFOLLOW_ANY refuses on every path through it.
143
+ const scratch = realpathSync(mkdtempSync(join(tmpdir(), "uai-nofollow-any-")));
132
144
  try {
133
145
  mkdirSync(join(scratch, "real"));
134
146
  writeFileSync(join(scratch, "real", "file"), "");
@@ -138,8 +150,23 @@ function probeContainment(): Containment {
138
150
  // Opened through the link: the flag is not honored here.
139
151
  return "verify";
140
152
  } catch (error) {
141
- return (error as NodeJS.ErrnoException).code === "ELOOP" ? "nofollow-any" : "verify";
153
+ if ((error as NodeJS.ErrnoException).code !== "ELOOP") return "verify";
154
+ }
155
+ // The exact flags the real opens use must work on a plain file and
156
+ // directory too: a flag the kernel refuses in combination would
157
+ // otherwise fail every workspace operation, not just links.
158
+ try {
159
+ closeSync(
160
+ openSync(
161
+ join(scratch, "real", "file"),
162
+ nofollowAnyFlags(constants.O_WRONLY | constants.O_APPEND | constants.O_NONBLOCK | constants.O_NOFOLLOW),
163
+ ),
164
+ );
165
+ closeSync(openSync(join(scratch, "real"), DIRECTORY_NOFOLLOW_ANY));
166
+ } catch {
167
+ return "verify";
142
168
  }
169
+ return "nofollow-any";
143
170
  } finally {
144
171
  rmSync(scratch, { recursive: true, force: true });
145
172
  }
@@ -255,7 +282,7 @@ function openWithin(root: string, parts: readonly string[], flags: number): numb
255
282
  }
256
283
  }
257
284
  if (mode === "nofollow-any") {
258
- return openSync(join(base, ...parts), finalFlags | O_NOFOLLOW_ANY, 0o644);
285
+ return openSync(join(base, ...parts), nofollowAnyFlags(finalFlags), 0o644);
259
286
  }
260
287
  // Neither: a read verifies what it opened; nothing is written.
261
288
  if ((flags & (constants.O_WRONLY | constants.O_RDWR | constants.O_CREAT)) !== 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runuai/host",
3
- "version": "0.9.151",
3
+ "version": "0.9.152",
4
4
  "description": "Uai host — runs ephemeral AI tasks in containers on a machine you control.",
5
5
  "license": "MIT",
6
6
  "author": "Uai Tech <team@runuai.com>",