@runuai/host 0.9.153 → 0.9.155

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/src/main.ts CHANGED
@@ -92,8 +92,7 @@ import { cancelMcpCapture, completeMcpCapture, probeMcpCapture } from "../lib/mc
92
92
  import { captureGithub, mintSsh, readGithubRevision, revokeGithub } from "../lib/person-capture";
93
93
  import { handleMcpOp } from "../lib/mcp-connections";
94
94
  import {
95
- ENGINE_CREDENTIAL_FILES,
96
- ENGINE_ENV_CREDENTIAL_KEYS,
95
+ CLOUD_CREDENTIAL_KINDS,
97
96
  HOST_CLOUD_CREDENTIALS_FEATURE,
98
97
  parseCredentialsActivateResultFrame,
99
98
  runEpochFromWire,
@@ -178,6 +177,7 @@ import {
178
177
  reprobeContainerRuntimeMachineIdentity,
179
178
  } from "../lib/container-runtime";
180
179
  import { persistedTaskEnvironmentsMatchMachine, reconstructPersistedTaskEnvironment } from "../lib/task-environment";
180
+ import { reconcileRunningTaskPersonCredentials } from "../lib/task-person-credentials";
181
181
  import { logVnodePressure } from "../lib/vnode-pressure";
182
182
  import { resolveAppleTunnelRoute } from "./apple-tunnel-route";
183
183
  import {
@@ -300,6 +300,7 @@ import {
300
300
  HOST_ENGINE_DISCONNECT_PROTOCOL_FEATURE,
301
301
  HOST_ENGINE_LOGIN_PROTOCOL_FEATURE,
302
302
  LIVE_ROSTER_EDIT_PROTOCOL_FEATURE,
303
+ AGENT_TOKEN_GENERATION_PROTOCOL_FEATURE,
303
304
  isEngineAccountScope,
304
305
  HOST_LOGS_PROTOCOL_FEATURE,
305
306
  HOST_TASK_INVENTORY_PROTOCOL_FEATURE,
@@ -702,10 +703,6 @@ setCredentialsModeSource(() => credentialsActivation.mode());
702
703
  * so the operator turning them on reaches it within minutes. */
703
704
  const CREDENTIALS_ACTIVATION_RETRY_MS = 5 * 60_000;
704
705
  const CREDENTIALS_ACTIVATION_ANSWER_MS = 60_000;
705
- const LEASED_ENGINE_KINDS: ReadonlySet<string> = new Set([
706
- ...Object.keys(ENGINE_ENV_CREDENTIAL_KEYS),
707
- ...Object.keys(ENGINE_CREDENTIAL_FILES),
708
- ]);
709
706
  // Started here, first fired long after the bridge's state exists.
710
707
  const credentialsActivationTimer = setInterval(() => {
711
708
  if (stopping || !credentialFramesReady) return;
@@ -939,6 +936,7 @@ const cloudCredentials = new CloudCredentialRuntime({
939
936
  // delivery look again; /env reads it afresh on every command.
940
937
  envReplica: new ProjectEnvTaskReplicaManager({ onChange: () => noteProjectEnvViewChange() }),
941
938
  envServedChanged: () => noteProjectEnvViewChange(),
939
+ personCredentialsReady: reconcileRunningTaskPersonCredentials,
942
940
  files: taskFiles,
943
941
  });
944
942
  // Leaving its own credentials ends every task's env from the runner's
@@ -1396,6 +1394,7 @@ function buildCapabilities(): HostCapabilities {
1396
1394
  HOST_ENGINE_ACCOUNT_MANAGE_PROTOCOL_FEATURE,
1397
1395
  HOST_ENGINE_DISCONNECT_PROTOCOL_FEATURE,
1398
1396
  LIVE_ROSTER_EDIT_PROTOCOL_FEATURE,
1397
+ AGENT_TOKEN_GENERATION_PROTOCOL_FEATURE,
1399
1398
  // The echo adapter cannot execute the in-task CLI. Advertising typed
1400
1399
  // dispatch in mock mode would let the composer create a Secretary that
1401
1400
  // has no way to wake crew.
@@ -1897,10 +1896,11 @@ function flushStoppedTaskStatuses(socket: WebSocket | null = ws): void {
1897
1896
  // ADR-131 D3: a stored account (any scope) makes a kind advertisable even
1898
1897
  // when the host's own credential is absent — see hasStoredEngineAccounts.
1899
1898
  setAdvertisementFallback((kind) => hasStoredEngineAccounts(kind));
1900
- // ADR-154: on cloud credentials the runner holds none of its own, so a kind
1901
- // is advertised when the cloud can lease an account for it (Kimi, with no
1902
- // lane yet, is not), whatever this process's env says.
1903
- setLeasedAdvertisement(() => (credentialsMode() === "active" ? LEASED_ENGINE_KINDS : null));
1899
+ // ADR-154 §10: on cloud credentials the runner holds none of its own, so it
1900
+ // advertises the kinds a leased task can run, whatever this process's env
1901
+ // says: the leased lanes and the Managed Engine, which leases nothing (Kimi,
1902
+ // with no lane yet, is left out).
1903
+ setLeasedAdvertisement(() => (credentialsMode() === "active" ? CLOUD_CREDENTIAL_KINDS : null));
1904
1904
  onRegistryChange(() => sendCapabilities());
1905
1905
  onContainerRuntimeChange((next, previous) => {
1906
1906
  if (stopping) return;
@@ -5416,6 +5416,12 @@ function expectTaskAgents(value: unknown): TaskAgent[] {
5416
5416
  const perms = row.permissions.filter((p): p is string => typeof p === "string");
5417
5417
  if (perms.length > 0) out.permissions = perms;
5418
5418
  }
5419
+ // ADR-157: the built-in Astronaut's marker, set only by the cloud.
5420
+ if (row.builtIn === "astronaut") out.builtIn = "astronaut";
5421
+ // ADR-157 §3: the generation its task token must carry.
5422
+ if (typeof row.tokenGeneration === "string" && row.tokenGeneration.length > 0) {
5423
+ out.tokenGeneration = row.tokenGeneration;
5424
+ }
5419
5425
  return out;
5420
5426
  });
5421
5427
  }
package/src/protocol.ts CHANGED
@@ -82,6 +82,14 @@ export type HostCommandResult<T> =
82
82
  * An unknown FIELD is ignored by old readers; an unknown CODE is not.
83
83
  */
84
84
  origin?: "cloud";
85
+ /**
86
+ * With `origin: "cloud"`: the protocol feature the command's payload
87
+ * needs and this runner does not advertise, so the bridge never sent it
88
+ * (ADR-157 §3: a roster with token generations needs
89
+ * `agent-token-generation-v1`). Additive for the same reason as
90
+ * `origin`; never set by a host.
91
+ */
92
+ requiresFeature?: string;
85
93
  };
86
94
 
87
95
  /** Capability ids shared by host advertisement and fail-closed cloud gates. */
@@ -156,6 +164,12 @@ export const HOST_ENGINE_ACCOUNT_SCOPES_PROTOCOL_FEATURE =
156
164
  * unrelated respawn and never stops a removed one, so the cloud refuses to
157
165
  * edit or remove a running task's agents on it. */
158
166
  export const LIVE_ROSTER_EDIT_PROTOCOL_FEATURE = "live-roster-edit-v1";
167
+ /** ADR-157 §3: the host signs an agent's `tokenGeneration` into its task token
168
+ * (claim `g`). The cloud stamps a fresh generation on an agent that joins a
169
+ * running task only on a host advertising this, and refuses a token whose
170
+ * generation is not its roster entry's, so a token minted for an earlier agent
171
+ * under the same handle stays dead. */
172
+ export const AGENT_TOKEN_GENERATION_PROTOCOL_FEATURE = "agent-token-generation-v1";
159
173
  /** ADR-153 declarations + owner bindings. Advertising this means the host
160
174
  * understands every additive env frame field, persists bindings separately
161
175
  * from values, and reports immutable repository schema observations. Keep it
@@ -2956,6 +2970,29 @@ export const MAX_HOST_TASK_INVENTORY_TASK_ID_CHARS = 128;
2956
2970
  export const ENROLLMENT_REPLAY_PROTOCOL_HEADER = "x-uai-enrollment-replay";
2957
2971
  export const ENROLLMENT_REPLAY_PROTOCOL_VERSION = "same-host-v1";
2958
2972
  export const COMMUNICATOR_EXECUTION_PROFILE = "communicator";
2973
+ /**
2974
+ * ADR-157: the built-in Astronaut. An Astronaut task has exactly one lead agent with
2975
+ * this id, marked `builtIn: "astronaut"` by the cloud when it builds the entry;
2976
+ * no client may send the marker. The lead runs under the `astronaut` execution
2977
+ * profile: normal tools, but no native subagents (Claude's Agent/Task, Codex's
2978
+ * multi-agent), so every helper it uses is an accounted crew agent.
2979
+ */
2980
+ export const ASTRONAUT_AGENT_ID = "astronaut";
2981
+ export const ASTRONAUT_BUILT_IN = "astronaut";
2982
+ export const ASTRONAUT_EXECUTION_PROFILE = "astronaut";
2983
+
2984
+ /** Whether `agent` is this task's built-in Astronaut (ADR-157 §1). */
2985
+ export function isBuiltInAstronaut(
2986
+ task: { mode?: string | null; secretaryAgentId?: string | null },
2987
+ agent: { id: string; builtIn?: string | null } | null | undefined,
2988
+ ): boolean {
2989
+ return (
2990
+ task.mode === "secretary" &&
2991
+ task.secretaryAgentId === ASTRONAUT_AGENT_ID &&
2992
+ agent?.id === ASTRONAUT_AGENT_ID &&
2993
+ agent.builtIn === ASTRONAUT_BUILT_IN
2994
+ );
2995
+ }
2959
2996
  export const MAX_AGENT_ID_CHARS = 128;
2960
2997
  export const MAX_SECRETARY_DISPATCH_RECIPIENTS = 16;
2961
2998
  export const MAX_SECRETARY_DISPATCH_INSTRUCTION_CHARS = 8_000;
@@ -3023,6 +3060,11 @@ export interface TaskAgent {
3023
3060
  }>;
3024
3061
  /** ADR-048: the source persona's flat permission list for the `uai` CLI. */
3025
3062
  permissions?: string[];
3063
+ /** ADR-157: set by the cloud on the built-in Astronaut only. */
3064
+ builtIn?: "astronaut";
3065
+ /** ADR-157 §3: this agent's token generation, stamped by the cloud when it
3066
+ * joined the running task; signed into its task token as `g`. */
3067
+ tokenGeneration?: string;
3026
3068
  }
3027
3069
 
3028
3070
  export type ContainerRuntimePreference = "auto" | "docker" | "apple-container";