Merge nucleic/humble-harbor-viper into dev

This commit is contained in:
2026-07-10 23:51:32 -07:00
parent 27cbf167b6
commit dd782e2e56
9 changed files with 187 additions and 9 deletions
+21 -1
View File
@@ -98,6 +98,8 @@ private struct RunnerSection: View {
.defaultMaxContainers
@AppStorage(RunnerSettings.balancerPolicyKey) private var balancerPolicy = RunnerBalancerPolicy
.leastSessions.rawValue
@AppStorage(RunnerSettings.intelligenceModeKey) private var intelligenceMode =
RunnerIntelligenceMode.mesh.rawValue
// Dev/self-host override; empty the production control plane.
@AppStorage(RunnerSettings.controlURLKey) private var controlURL = ""
@@ -136,6 +138,22 @@ private struct RunnerSection: View {
.tag(RunnerBalancerPolicy.roundRobin.rawValue)
}
// ANTIMATTER_RUNNER §5, item 6: how the runner replaces Apple Foundation
// Models. Rides the same debounced settings push; the container picks it up on
// its next boot as NUCLEIC_RUNNER_INTELLIGENCE_MODE.
Picker("Runner intelligence", selection: $intelligenceMode) {
ForEach(RunnerIntelligenceMode.allCases, id: \.rawValue) { mode in
Text(mode.displayName).tag(mode.rawValue)
}
}
Text(
"How the runner names chats and writes summaries. “Your devices” delegates "
+ "to this Mac / your iPhone over the mesh (background work may run on the "
+ "phone; time-sensitive work stays on Macs); “Agent” spends a small model's "
+ "tokens on the runner itself. Applies when the runner container next boots.")
.font(.caption).foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
if isDevBuild {
TextField(
"Control URL (dev)", text: $controlURL,
@@ -214,6 +232,7 @@ private struct RunnerSection: View {
// Knob changes ride to the pool debounced; the DO re-clamps server-side.
.onChange(of: maxContainers) { _, _ in pushKnobs() }
.onChange(of: balancerPolicy) { _, _ in pushKnobs() }
.onChange(of: intelligenceMode) { _, _ in pushKnobs() }
.onChange(of: runnerEnabled) { _, on in if on { refreshSharedCredentials() } }
.onAppear { if runnerEnabled { refreshSharedCredentials() } }
}
@@ -274,7 +293,8 @@ private struct RunnerSection: View {
do {
try await store.runnerPoolClient().pushSettings(
maxContainers: maxContainers,
balancerPolicy: RunnerBalancerPolicy(rawValue: balancerPolicy))
balancerPolicy: RunnerBalancerPolicy(rawValue: balancerPolicy),
intelligenceMode: RunnerIntelligenceMode(rawValue: intelligenceMode))
poolError = nil
} catch {
poolError = error.localizedDescription
+10 -1
View File
@@ -1121,11 +1121,20 @@ public enum RunnerSettings {
/// The runner's `IntelligenceProviding` choice (ANTIMATTER_RUNNER §5): `agent` runs a small
/// SKU of the user's authenticated agent behind strict templates, `mesh` delegates to
/// Apple-Intelligence-capable devices on the mesh, `heuristic` skips models entirely. Modes
/// 12 always terminate in the heuristic fallback when they can't serve.
/// 12 always terminate in the heuristic fallback when they can't serve. Mirrors the
/// `IntelligenceMode` union in `cloud/nucleic-runner/src/pool.ts` (keep in lockstep).
public enum RunnerIntelligenceMode: String, CaseIterable, Sendable {
case agent
case mesh
case heuristic
public var displayName: String {
switch self {
case .agent: return "Agent (small model)"
case .mesh: return "Your devices (Apple Intelligence)"
case .heuristic: return "Off (heuristics only)"
}
}
}
/// A registered git repository plus per-project configuration.
@@ -107,15 +107,17 @@ public actor RunnerPoolClient {
/// Push the Settings knobs. The DO re-clamps server-side (`clampSettings` same 116
/// bounds as `RunnerSettings.maxContainersRange`; keep them in lockstep), so a stale client
/// can't exceed the cap.
/// can't exceed the cap. `intelligenceMode` reaches a container as
/// `NUCLEIC_RUNNER_INTELLIGENCE_MODE` on its next boot (ANTIMATTER_RUNNER §5, item 6).
public func pushSettings(
maxContainers: Int? = nil, balancerPolicy: RunnerBalancerPolicy? = nil,
idleTimeoutSeconds: Int? = nil
idleTimeoutSeconds: Int? = nil, intelligenceMode: RunnerIntelligenceMode? = nil
) async throws {
var patch: [String: Any] = [:]
if let maxContainers { patch["maxContainers"] = maxContainers }
if let balancerPolicy { patch["balancerPolicy"] = balancerPolicy.rawValue }
if let idleTimeoutSeconds { patch["idleTimeoutSeconds"] = idleTimeoutSeconds }
if let intelligenceMode { patch["intelligenceMode"] = intelligenceMode.rawValue }
_ = try await authed("PATCH", "/v1/pool/settings", body: patch)
}
@@ -257,6 +259,9 @@ public struct RunnerPoolStatus: Decodable, Sendable, Equatable {
public let maxContainers: Int
public let balancerPolicy: String
public let idleTimeoutSeconds: Int
/// "mesh" | "agent" | "heuristic" (ANTIMATTER_RUNNER §5); nil from a Worker that
/// predates the knob.
public let intelligenceMode: String?
}
public struct Instance: Decodable, Sendable, Equatable, Identifiable {
+71 -3
View File
@@ -17,7 +17,15 @@ import FoundationNetworking
/// static musl Linux build.
///
/// nucleic-smoke '<pairing-code>' --prompt "run `echo hi` in the shell" [--project name]
/// [--model sku] [--timeout 300] [--deny]
/// [--model sku] [--timeout 300] [--deny] [--expect-intelligence]
///
/// It also plays an **intelligence worker** (ANTIMATTER_RUNNER §5, item 6): it advertises
/// `canProvideIntelligence` (desktop class, so every priority tier is eligible) and answers any
/// delegated `intelligenceRequest` with deterministic canned output no model needed; the point
/// is proving the wire path. With `--expect-intelligence` the exercise PASSES when a delegated
/// `sessionName` job round-trips end to end: request received here, canned title answered, and
/// the host's `sessionUpdated` comes back carrying that exact title. The agent turn itself may
/// error (a bare container has no `claude`); the naming path is what's under test.
@main
struct Smoke {
struct Options {
@@ -27,8 +35,14 @@ struct Smoke {
var model: String?
var timeoutSeconds: Double = 300
var deny = false // answer approvals with deny instead of allow
/// PASS on a delegated-intelligence round trip instead of on turn completion.
var expectIntelligence = false
}
/// The canned title the worker answers a delegated `sessionName` job with and the exact
/// string `--expect-intelligence` waits to see come back in a `sessionUpdated`.
static let cannedSessionTitle = "Mesh Delegation Proven"
static func main() async {
var options = Options()
var args = Array(CommandLine.arguments.dropFirst())
@@ -41,6 +55,7 @@ struct Smoke {
case "--timeout":
options.timeoutSeconds = Double(args.isEmpty ? "" : args.removeFirst()) ?? 300
case "--deny": options.deny = true
case "--expect-intelligence": options.expectIntelligence = true
default:
if options.pairingCode.isEmpty, arg.contains("d=") { options.pairingCode = arg }
else { fail("unknown argument: \(arg)") }
@@ -48,7 +63,7 @@ struct Smoke {
}
guard !options.pairingCode.isEmpty else {
fail("usage: nucleic-smoke '<nucleic://pair?d=…>' [--prompt …] [--project …] "
+ "[--model …] [--timeout seconds] [--deny]")
+ "[--model …] [--timeout seconds] [--deny] [--expect-intelligence]")
}
let payload: PairingPayload
@@ -70,7 +85,14 @@ struct Smoke {
channel: channel, identity: identity, hostStaticKey: payload.hostStaticKey,
mode: .pair(secret: payload.pairingSecret),
deviceID: DeviceIdentity.hostID(ofStaticKey: identity.staticPublicKey),
deviceLabel: "nucleic-smoke", scopeClaim: .control)
deviceLabel: "nucleic-smoke", scopeClaim: .control,
// Offer this process as a mesh AFM worker (ANTIMATTER_RUNNER §5): desktop class so
// every priority tier may land here, with a fake top-rank chip so a mesh-mode
// runner's queue prefers it. Answers are canned the wire path is what's tested.
clientCaps: WireClientCapabilities(
mesh: 1, canProvideIntelligence: true,
intelligenceProfile: IntelligenceWorkerProfile(
deviceClass: .desktop, chip: "Apple M4 Max")))
// Hard deadline for the whole exercise a hung relay/turn must not wedge CI.
let timeout = Task {
@@ -90,6 +112,7 @@ struct Smoke {
var sessionID: SessionID?
var sawAssistantActivity = false
var approvalsAnswered = 0
var intelligenceServed = 0
for await event in await client.start() {
switch event {
@@ -124,6 +147,14 @@ struct Smoke {
projectID: project.id, message: options.prompt, model: options.model,
useWorktree: true, auto: false)))
case .sessionUpdated(let summary):
// The delegated-naming proof (--expect-intelligence): the canned title we
// answered the sessionName job with came back as the session's actual name
// the job round-tripped submit queue this worker rename broadcast.
if options.expectIntelligence, summary.title == cannedSessionTitle {
log("PASS — delegated intelligence round-tripped: session renamed to "
+ "\(summary.title)” (\(intelligenceServed) request(s) served)")
exit(0)
}
// The chat we started is the session that appears (or changes) after startChat.
if sessionID == nil, startedChat, summary.status.hasTurnInFlight {
sessionID = summary.sessionID
@@ -135,6 +166,12 @@ struct Smoke {
guard summary.sessionID == sessionID else { break }
log("status: \(summary.status.rawValue)")
if summary.status == .error {
// Under --expect-intelligence the turn is allowed to die (a bare container
// has no agent CLI) the naming push can still land after the error.
if options.expectIntelligence {
log("session errored (tolerated — waiting on the naming round trip)")
break
}
log("FAIL — session errored")
exit(1)
}
@@ -160,6 +197,11 @@ struct Smoke {
}
case .approvalRequested(let approval):
answer(approval, client: client, deny: options.deny, count: &approvalsAnswered)
case .intelligenceRequest(let request):
// A mesh-mode runner delegated one AFM job here (ANTIMATTER_RUNNER §5). Render
// the shared template (proves the kind decodes) and answer canned output.
intelligenceServed += 1
await serveIntelligence(request, client: client)
case .credentialNeeded(let need):
// The runner lacks credentials (ANTIMATTER_RUNNER §6) play the holding
// device: read the local files, seal each requested kind to the runner's key,
@@ -181,6 +223,32 @@ struct Smoke {
}
}
/// Answer one delegated intelligence job with deterministic canned output the worker
/// half of the mesh AFM queue, minus the model. `sessionName` answers the canned title the
/// `--expect-intelligence` proof watches for; `classifyTurn` answers the template's DONE
/// token; anything else gets a one-line placeholder. An unknown kind answers with `error`
/// set, exactly as a real executor does.
private static func serveIntelligence(
_ request: WireIntelligenceRequest, client: SyncClient
) async {
let priority = request.priority?.rawValue ?? "background"
guard IntelligenceDelegate.job(for: request) != nil else {
log("intelligence request \(request.kind.rawValue) (\(priority)) — unknown kind, answering error")
await client.send(.intelligenceResult(WireIntelligenceResult(
id: request.id, error: "unsupported kind: \(request.kind.rawValue)")))
return
}
let output: String
switch request.kind {
case .sessionName: output = cannedSessionTitle
case .classifyTurn: output = "DONE"
default: output = "Delegated \(request.kind.rawValue) output"
}
log("intelligence request \(request.kind.rawValue) (\(priority)) — answering “\(output)")
await client.send(.intelligenceResult(WireIntelligenceResult(
id: request.id, outputs: [output])))
}
/// Answer a `credentialNeeded`: seal every requested kind we can satisfy from this
/// machine's own files to the runner's sealing key (AAD = the kind string, so a box can't
/// be replayed as another kind) and send one provision envelope.
+4
View File
@@ -29,6 +29,9 @@ export function buildContainerEnv(base: {
instanceToken: string;
controlPlaneURL: string;
relayURL?: string;
/** How nucleicd replaces AFMs (ANTIMATTER_RUNNER §5, item 6): "mesh" | "agent" |
* "heuristic", from the pool settings. Omitted ⇒ nucleicd's own default (mesh). */
intelligenceMode?: string;
}): Record<string, string> {
const env: Record<string, string> = {
NUCLEIC_RUNNER_POOL_ID: base.poolId,
@@ -38,6 +41,7 @@ export function buildContainerEnv(base: {
NUCLEIC_RUNNER_CONTROL_URL: base.controlPlaneURL,
};
if (base.relayURL) env.NUCLEIC_RELAY_URL = base.relayURL;
if (base.intelligenceMode) env.NUCLEIC_RUNNER_INTELLIGENCE_MODE = base.intelligenceMode;
return env;
}
+1
View File
@@ -189,6 +189,7 @@ function makeDriver(env: Env): ContainerDriver {
epoch: extraEnv.NUCLEIC_RUNNER_EPOCH ?? "0",
instanceToken: extraEnv.NUCLEIC_RUNNER_INSTANCE_TOKEN ?? "",
controlPlaneURL: "https://runner.nucleic.blakeslee.xyz",
intelligenceMode: extraEnv.NUCLEIC_RUNNER_INTELLIGENCE_MODE,
});
const res = await stub.fetch("https://container/start", {
method: "POST",
+23 -1
View File
@@ -12,6 +12,12 @@
export type BalancerPolicy = "least-sessions" | "round-robin";
/** How a runner replaces Apple Foundation Models (docs/ANTIMATTER_RUNNER.md §5, item 6).
* Mirrors Swift's `RunnerIntelligenceMode` (keep in lockstep). Reaches the container as
* `NUCLEIC_RUNNER_INTELLIGENCE_MODE` on its next boot — a settings change never restarts a
* running container; the scale-to-zero lifecycle re-boots it soon enough. */
export type IntelligenceMode = "mesh" | "agent" | "heuristic";
export interface PoolSettings {
/** Total containers the pool may run at once, host-of-record included. 1 ⇒ tier-0 only:
* every session runs in place inside the host container. Mirrors `nucleic.macvm.maxConcurrent`. */
@@ -19,12 +25,14 @@ export interface PoolSettings {
balancerPolicy: BalancerPolicy;
/** Session sandboxes idle (zero sessions) longer than this are reaped by the alarm. */
idleTimeoutSeconds: number;
intelligenceMode: IntelligenceMode;
}
export const DEFAULT_SETTINGS: PoolSettings = {
maxContainers: 4,
balancerPolicy: "least-sessions",
idleTimeoutSeconds: 600,
intelligenceMode: "mesh",
};
export const MAX_CONTAINERS_RANGE = { min: 1, max: 16 } as const;
@@ -43,6 +51,14 @@ export function clampSettings(current: PoolSettings, patch: Partial<PoolSettings
if (patch && typeof patch.idleTimeoutSeconds === "number" && Number.isFinite(patch.idleTimeoutSeconds)) {
next.idleTimeoutSeconds = Math.min(IDLE_TIMEOUT_RANGE.max, Math.max(IDLE_TIMEOUT_RANGE.min, Math.floor(patch.idleTimeoutSeconds)));
}
if (
patch &&
(patch.intelligenceMode === "mesh" ||
patch.intelligenceMode === "agent" ||
patch.intelligenceMode === "heuristic")
) {
next.intelligenceMode = patch.intelligenceMode;
}
return next;
}
@@ -169,7 +185,10 @@ export class RunnerPoolCore {
}
private async settings(): Promise<PoolSettings> {
return (await this.state.storage.get<PoolSettings>(K_SETTINGS)) ?? { ...DEFAULT_SETTINGS };
// Merge over defaults so a record persisted before a field existed (e.g. an older pool
// predating `intelligenceMode`) reads the default rather than `undefined`.
const stored = await this.state.storage.get<Partial<PoolSettings>>(K_SETTINGS);
return { ...DEFAULT_SETTINGS, ...(stored ?? {}) };
}
private async instances(): Promise<InstanceRecord[]> {
@@ -257,6 +276,7 @@ export class RunnerPoolCore {
if (existing && existing.state !== "stopped") {
return json({ instanceId: "host", epoch: existing.epoch, alreadyRunning: true });
}
const settings = await this.settings();
const epoch = await this.bumpEpoch();
const token = await this.deps.mintInstanceToken(this.poolId, "host");
await this.deps.driver.start(this.poolId, "host", {
@@ -264,6 +284,7 @@ export class RunnerPoolCore {
NUCLEIC_RUNNER_INSTANCE_ID: "host",
NUCLEIC_RUNNER_EPOCH: String(epoch),
NUCLEIC_RUNNER_INSTANCE_TOKEN: token,
NUCLEIC_RUNNER_INTELLIGENCE_MODE: settings.intelligenceMode,
});
const now = this.now();
const record: InstanceRecord = {
@@ -321,6 +342,7 @@ export class RunnerPoolCore {
NUCLEIC_RUNNER_INSTANCE_ID: placement.instanceId,
NUCLEIC_RUNNER_EPOCH: String(epoch),
NUCLEIC_RUNNER_INSTANCE_TOKEN: token,
NUCLEIC_RUNNER_INTELLIGENCE_MODE: settings.intelligenceMode,
});
const now = this.now();
const record: InstanceRecord = {
+4 -1
View File
@@ -10,6 +10,7 @@ test("buildContainerEnv carries the full instance contract", () => {
instanceToken: "tok",
controlPlaneURL: "https://runner.example",
relayURL: "https://relay.example",
intelligenceMode: "agent",
});
assert.deepEqual(env, {
NUCLEIC_RUNNER_POOL_ID: "p1",
@@ -18,10 +19,11 @@ test("buildContainerEnv carries the full instance contract", () => {
NUCLEIC_RUNNER_INSTANCE_TOKEN: "tok",
NUCLEIC_RUNNER_CONTROL_URL: "https://runner.example",
NUCLEIC_RELAY_URL: "https://relay.example",
NUCLEIC_RUNNER_INTELLIGENCE_MODE: "agent",
});
});
test("relay URL is optional (production default lives in nucleicd)", () => {
test("relay URL and intelligence mode are optional (defaults live in nucleicd)", () => {
const env = buildContainerEnv({
poolId: "p1",
instanceId: "host",
@@ -30,6 +32,7 @@ test("relay URL is optional (production default lives in nucleicd)", () => {
controlPlaneURL: "https://runner.example",
});
assert.equal("NUCLEIC_RELAY_URL" in env, false);
assert.equal("NUCLEIC_RUNNER_INTELLIGENCE_MODE" in env, false);
});
test("nucleicd control port is pinned (pairing proxy contract)", () => {
+46
View File
@@ -40,6 +40,20 @@ test("clampSettings bounds maxContainers and idle timeout, keeps unknown-free",
assert.deepEqual(s4, DEFAULT_SETTINGS);
});
test("clampSettings accepts only known intelligence modes (item 6)", () => {
assert.equal(DEFAULT_SETTINGS.intelligenceMode, "mesh");
const agent = clampSettings(DEFAULT_SETTINGS, { intelligenceMode: "agent" });
assert.equal(agent.intelligenceMode, "agent");
const heuristic = clampSettings(agent, { intelligenceMode: "heuristic" });
assert.equal(heuristic.intelligenceMode, "heuristic");
// Garbage keeps the current value — the DO never trusts a client.
const bogus = clampSettings(agent, { intelligenceMode: "gpt-99" as never });
assert.equal(bogus.intelligenceMode, "agent");
// A patch that doesn't mention it leaves it untouched.
const untouched = clampSettings(agent, { maxContainers: 2 });
assert.equal(untouched.intelligenceMode, "agent");
});
test("least-sessions places on the emptiest live sandbox, ties break deterministically", () => {
const placement = placeSandbox({
instances: [
@@ -179,12 +193,44 @@ test("provision boots the host container once and is idempotent", async () => {
assert.equal(first.alreadyRunning, false);
assert.equal(started.length, 1);
assert.equal(started[0].env.NUCLEIC_RUNNER_INSTANCE_TOKEN, "tok-pool-1-host");
// Intelligence mode rides every boot (item 6) — the default when never patched.
assert.equal(started[0].env.NUCLEIC_RUNNER_INTELLIGENCE_MODE, "mesh");
const second = (await (await core.fetch(req("POST", "/provision", "pool"))).json()) as Record<string, unknown>;
assert.equal(second.alreadyRunning, true);
assert.equal(started.length, 1);
});
test("a patched intelligence mode reaches the next container boot (item 6)", async () => {
const { core, started } = makeCore();
const patched = (await (
await core.fetch(req("PATCH", "/settings", "pool", { intelligenceMode: "agent" }))
).json()) as { settings: PoolSettings };
assert.equal(patched.settings.intelligenceMode, "agent");
await core.fetch(req("POST", "/provision", "pool"));
assert.equal(started[0].env.NUCLEIC_RUNNER_INTELLIGENCE_MODE, "agent");
// Sandboxes booted by acquire carry it too.
const boot = (await (await core.fetch(req("POST", "/sandbox/acquire", "instance:host"))).json()) as Record<
string,
unknown
>;
assert.equal(boot.decision, "boot");
assert.equal(started[1].env.NUCLEIC_RUNNER_INTELLIGENCE_MODE, "agent");
});
test("settings stored before the intelligenceMode field read the default", async () => {
const { core, storage } = makeCore();
// A pre-item-6 pool persisted a settings record without the field.
await storage.put("settings", { maxContainers: 2, balancerPolicy: "round-robin", idleTimeoutSeconds: 120 });
const status = (await (await core.fetch(req("GET", "/status", "pool"))).json()) as {
settings: PoolSettings;
};
assert.equal(status.settings.intelligenceMode, "mesh");
assert.equal(status.settings.maxContainers, 2);
});
test("acquire boots a sandbox, then places on it once it heartbeats ready", async () => {
let now = NOW;
const { core, started } = makeCore({ now: () => now });