@runuai/host 0.9.116 → 0.9.118
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/lib/browser-testing.ts +7 -1
- package/lib/orchestrator.ts +28 -1
- package/lib/task-environment/index.ts +1 -1
- package/package.json +1 -1
- package/src/index.ts +8 -0
- package/src/protocol.ts +10 -0
package/lib/browser-testing.ts
CHANGED
|
@@ -1048,8 +1048,14 @@ const DEFAULT_NODE_BIN =
|
|
|
1048
1048
|
`/opt/asdf-data/installs/nodejs/${STANDARD_DEFAULT_NODE_VERSION}/bin`;
|
|
1049
1049
|
const ROOT_RUNTIME_PATH =
|
|
1050
1050
|
`${DEFAULT_NODE_BIN}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`;
|
|
1051
|
+
// `npx` through ROOT_RUNTIME_PATH, not an absolute asdf path: the machine
|
|
1052
|
+
// image (ADR-121) carries no asdf — node is /usr/bin/node there — and the
|
|
1053
|
+
// absolute path made this worker exit 127 on every task machine, so its
|
|
1054
|
+
// done-marker was never written and each ensure re-ran it (live 2026-09-09).
|
|
1055
|
+
// The path lists the asdf bin first and never the shims, so a container
|
|
1056
|
+
// resolves exactly what it did before.
|
|
1051
1057
|
const APT_DEPS_CMD =
|
|
1052
|
-
`PATH=${ROOT_RUNTIME_PATH}
|
|
1058
|
+
`PATH=${ROOT_RUNTIME_PATH} npx ` +
|
|
1053
1059
|
"-y playwright@latest install-deps chromium";
|
|
1054
1060
|
const APT_VIEWER_CMD =
|
|
1055
1061
|
"/usr/bin/apt-get install -y --no-install-recommends xvfb x11vnc novnc websockify";
|
package/lib/orchestrator.ts
CHANGED
|
@@ -135,6 +135,7 @@ import type {
|
|
|
135
135
|
HostEvent,
|
|
136
136
|
} from "../src/protocol";
|
|
137
137
|
import {
|
|
138
|
+
type TaskStartStage,
|
|
138
139
|
HOST_ACCOUNT_SCOPE,
|
|
139
140
|
MAX_AGENT_ID_CHARS,
|
|
140
141
|
MAX_SECRETARY_DISPATCH_ID_CHARS,
|
|
@@ -455,6 +456,11 @@ export class Orchestrator {
|
|
|
455
456
|
this.emitHost({ kind: "system.note", taskId, text });
|
|
456
457
|
}
|
|
457
458
|
|
|
459
|
+
/** A task-start stage boundary (the launch screen on the task page). */
|
|
460
|
+
emitTaskStage(taskId: string, stage: TaskStartStage): void {
|
|
461
|
+
this.emitHost({ kind: "task.stage", taskId, stage, at: Date.now() });
|
|
462
|
+
}
|
|
463
|
+
|
|
458
464
|
private emitHost(event: HostEvent): void {
|
|
459
465
|
for (const fn of this.hostSubscribers) {
|
|
460
466
|
try {
|
|
@@ -1589,6 +1595,15 @@ export class Orchestrator {
|
|
|
1589
1595
|
session: AgentSession,
|
|
1590
1596
|
): void {
|
|
1591
1597
|
channel.sessions.set(agentId, session);
|
|
1598
|
+
// The launch screen's `ready` rides the bind, not the tail of the start
|
|
1599
|
+
// loop: whichever path brings the last roster agent up — the initial
|
|
1600
|
+
// start, a reconcile that won the race with it, a respawn after Resume —
|
|
1601
|
+
// is the moment the task can take a message. Live 2026-09-09: `agents`
|
|
1602
|
+
// was stored, Jonah was up and idle, and no `ready` ever followed, so
|
|
1603
|
+
// the page stayed covered for minutes. Idempotent on the cloud side.
|
|
1604
|
+
if (channel.roster.every((agent) => channel.sessions.has(agent.id))) {
|
|
1605
|
+
this.emitTaskStage(channel.taskId, "ready");
|
|
1606
|
+
}
|
|
1592
1607
|
// Adapters may synchronously emit `error` followed by `turn_complete` (or
|
|
1593
1608
|
// `exit`). Terminal classification now awaits a daemon proof, so preserve
|
|
1594
1609
|
// transport emission order across that await; otherwise the later boundary
|
|
@@ -1889,6 +1904,7 @@ export class Orchestrator {
|
|
|
1889
1904
|
// which the client caps at 10s (Codex) / 30s (Claude Code). Idempotent
|
|
1890
1905
|
// and ~0.5s once the build is present; a failure arms its own 60s retry
|
|
1891
1906
|
// rather than depending on another delivery/ensure to arrive.
|
|
1907
|
+
this.emitTaskStage(channel.taskId, "tools");
|
|
1892
1908
|
await this.ensureBrowserSetup(
|
|
1893
1909
|
channel,
|
|
1894
1910
|
initialRoster.some((a) => a.kind === "codex"),
|
|
@@ -1923,6 +1939,7 @@ export class Orchestrator {
|
|
|
1923
1939
|
return false;
|
|
1924
1940
|
}
|
|
1925
1941
|
|
|
1942
|
+
this.emitTaskStage(channel.taskId, "agents");
|
|
1926
1943
|
for (const agent of initialRoster) {
|
|
1927
1944
|
if (!this.isActiveChannel(channel)) return false;
|
|
1928
1945
|
// A prior partial start or an independently guarded recovery may already
|
|
@@ -1958,7 +1975,15 @@ export class Orchestrator {
|
|
|
1958
1975
|
),
|
|
1959
1976
|
},
|
|
1960
1977
|
);
|
|
1961
|
-
if (!session)
|
|
1978
|
+
if (!session) {
|
|
1979
|
+
// The agents that did come up are usable; the reconcile pass keeps
|
|
1980
|
+
// retrying this one. Do not leave the page covered for a session
|
|
1981
|
+
// that may never bind — `ready` otherwise rides bindSession.
|
|
1982
|
+
if (channel.sessions.size > 0) {
|
|
1983
|
+
this.emitTaskStage(channel.taskId, "ready");
|
|
1984
|
+
}
|
|
1985
|
+
return false;
|
|
1986
|
+
}
|
|
1962
1987
|
this.startNextPendingPrompt(channel, agent.id);
|
|
1963
1988
|
} finally {
|
|
1964
1989
|
channel.spawning.delete(agent.id);
|
|
@@ -1972,6 +1997,8 @@ export class Orchestrator {
|
|
|
1972
1997
|
// so it both duplicated the opening turn AND re-injected it on a host
|
|
1973
1998
|
// restart, making agents redo the first turn mid-task. The persona/mission
|
|
1974
1999
|
// an agent needs live in the always-on system preamble, so nothing is lost.
|
|
2000
|
+
// `ready` itself was emitted by bindSession as the last roster agent
|
|
2001
|
+
// bound (or above, when one could not).
|
|
1975
2002
|
return true;
|
|
1976
2003
|
}
|
|
1977
2004
|
|
|
@@ -128,7 +128,7 @@ registry.register(appleProvider);
|
|
|
128
128
|
// host) until machine-backing becomes a per-task property.
|
|
129
129
|
// ---------------------------------------------------------------------------
|
|
130
130
|
|
|
131
|
-
function machineTasksEnabled(): boolean {
|
|
131
|
+
export function machineTasksEnabled(): boolean {
|
|
132
132
|
return process.env.UAI_MACHINE_TASKS === "1";
|
|
133
133
|
}
|
|
134
134
|
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -77,6 +77,7 @@ import {
|
|
|
77
77
|
} from "../lib/container-runtime";
|
|
78
78
|
import { vnodeAdmissionProblem } from "../lib/vnode-pressure";
|
|
79
79
|
import {
|
|
80
|
+
machineTasksEnabled,
|
|
80
81
|
provisionTaskEnvironment,
|
|
81
82
|
reconstructPersistedTaskEnvironment,
|
|
82
83
|
} from "../lib/task-environment";
|
|
@@ -818,6 +819,13 @@ export const hostCommands: HostCommands = {
|
|
|
818
819
|
"GitHub is connected on this host, but Uai could not prepare its credential for Git. Retry the task; if it persists, reconnect GitHub on this host.",
|
|
819
820
|
);
|
|
820
821
|
}
|
|
822
|
+
// Launch screen: the first stage boundary. A machine-backed task
|
|
823
|
+
// spends the next minute launching an EC2 instance; a container
|
|
824
|
+
// task goes straight to preparing its workspace.
|
|
825
|
+
orchestrator.emitTaskStage(
|
|
826
|
+
input.task.id,
|
|
827
|
+
machineTasksEnabled() ? "machine" : "workspace",
|
|
828
|
+
);
|
|
821
829
|
try {
|
|
822
830
|
const provisioned = gitCredential
|
|
823
831
|
? await gitCredential.run(() =>
|
package/src/protocol.ts
CHANGED
|
@@ -1831,6 +1831,15 @@ export type HostToCloud =
|
|
|
1831
1831
|
| EngineConnectEventFrame
|
|
1832
1832
|
| HostConfigAckFrame;
|
|
1833
1833
|
|
|
1834
|
+
/** Task start stages, in order (docs/agent-notes.md "task start screen"). The
|
|
1835
|
+
* host emits them as `task.stage` events while a task comes up; the page
|
|
1836
|
+
* shows a launch screen instead of the chat until `ready`. */
|
|
1837
|
+
export const TASK_START_STAGES = ["machine", "workspace", "tools", "agents", "ready"] as const;
|
|
1838
|
+
export type TaskStartStage = (typeof TASK_START_STAGES)[number];
|
|
1839
|
+
export function isTaskStartStage(value: unknown): value is TaskStartStage {
|
|
1840
|
+
return (TASK_START_STAGES as readonly unknown[]).includes(value);
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1834
1843
|
export type HostEvent =
|
|
1835
1844
|
| {
|
|
1836
1845
|
kind: "task.status_changed";
|
|
@@ -1838,6 +1847,7 @@ export type HostEvent =
|
|
|
1838
1847
|
from: string;
|
|
1839
1848
|
to: string;
|
|
1840
1849
|
}
|
|
1850
|
+
| { kind: "task.stage"; taskId: string; stage: TaskStartStage; at: number }
|
|
1841
1851
|
| {
|
|
1842
1852
|
kind: "task.log";
|
|
1843
1853
|
taskId: string;
|