@runuai/host 0.9.90 → 0.9.91

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.
@@ -21,6 +21,7 @@ import "./kimi";
21
21
  import "./grok";
22
22
  import "./cursor";
23
23
  import "./opencode";
24
+ import "./uai";
24
25
 
25
26
  import { agentClisReady } from "../standard-image";
26
27
  import { factoryFor, supportsExecutionProfile } from "./registry";
@@ -54,6 +54,9 @@ export const RosterAgent = z.object({
54
54
  initialPrompt: z.string().optional(), // first-turn instructions, this task only
55
55
  skills: z.array(RosterSkill).optional(), // ADR-046 reference material
56
56
  permissions: z.array(z.string()).optional(), // ADR-048 `uai` CLI capabilities
57
+ // ADR-128: which upstream protocol a Uai-engine model speaks — resolved
58
+ // cloud-side from the model catalog; selects the delegated adapter.
59
+ uaiProvider: z.enum(["anthropic", "openai"]).optional(),
57
60
  });
58
61
  export type RosterAgent = z.infer<typeof RosterAgent>;
59
62
 
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The Uai Managed Engine (ADR-128) — not a harness, a delegation.
3
+ *
4
+ * The customer picks "Uai Managed Engine" and one of Uai's models; under
5
+ * the hood this adapter spawns the EXISTING claude or codex adapter
6
+ * (chosen by the model's provider, resolved cloud-side into
7
+ * `agent.uaiProvider`) with the environment pointed at the inference
8
+ * gateway: base URL swapped, the agent's own task token as the API
9
+ * credential, and the host's real provider credentials explicitly
10
+ * BLANKED so a BYO host's Claude subscription can never be billed for a
11
+ * Uai-engine turn. The gateway meters usage server-side and the cloud
12
+ * debits credits — this file never sees money.
13
+ *
14
+ * The model slug passes through verbatim: the underlying CLI sends it as
15
+ * the request's `model`, and the gateway maps slug → upstream id.
16
+ */
17
+
18
+ import { factoryFor, register } from "./registry";
19
+ import type { AgentSession, AgentSessionFactory } from "./types";
20
+
21
+ const DEFAULT_GATEWAY_URL = "https://inference.runuai.com";
22
+
23
+ export function gatewayUrl(): string {
24
+ return process.env.UAI_INFERENCE_GATEWAY_URL || DEFAULT_GATEWAY_URL;
25
+ }
26
+
27
+ /** The env override set for a delegated session. Exported for tests. */
28
+ export function uaiGatewayEnv(
29
+ provider: "anthropic" | "openai",
30
+ url: string,
31
+ taskToken: string,
32
+ ): Record<string, string> {
33
+ if (provider === "openai") {
34
+ return {
35
+ OPENAI_BASE_URL: url,
36
+ OPENAI_API_KEY: taskToken,
37
+ };
38
+ }
39
+ return {
40
+ ANTHROPIC_BASE_URL: url,
41
+ ANTHROPIC_AUTH_TOKEN: taskToken,
42
+ // Explicit blanks WIN over the claude adapter's passEnv inheritance
43
+ // (same mechanism its communicator profile uses to scrub) — the host's
44
+ // real credential must never ride a gateway-billed session.
45
+ ANTHROPIC_API_KEY: "",
46
+ CLAUDE_CODE_OAUTH_TOKEN: "",
47
+ };
48
+ }
49
+
50
+ const create: AgentSessionFactory["create"] = async (args): Promise<AgentSession> => {
51
+ const provider = args.agent.uaiProvider === "openai" ? "openai" : "anthropic";
52
+ const delegateKind = provider === "openai" ? "codex" : "claude";
53
+ const delegate = factoryFor(delegateKind);
54
+ if (!delegate) {
55
+ throw new Error(`uai engine: no ${delegateKind} adapter registered to delegate to`);
56
+ }
57
+ const taskToken = args.agentEnv?.UAI_TASK_TOKEN;
58
+ if (!taskToken) {
59
+ // agentCliEnv mints no token for a permissionless non-secretary agent;
60
+ // the gateway authenticates with exactly that token, so the engine
61
+ // needs it. The default crew grants permissions, so this is rare.
62
+ throw new Error(
63
+ "uai engine: this agent has no task token (no CLI permissions) — grant it at least one permission or use a direct engine",
64
+ );
65
+ }
66
+ return delegate.create({
67
+ ...args,
68
+ agentEnv: {
69
+ ...(args.agentEnv ?? {}),
70
+ ...uaiGatewayEnv(provider, gatewayUrl(), taskToken),
71
+ },
72
+ });
73
+ };
74
+
75
+ register({
76
+ kind: "uai",
77
+ label: "Uai Managed Engine",
78
+ // The model catalog is cloud data (ADR-128); the composer sources it from
79
+ // /api/org/inference/models, never from host capabilities. Advertising an
80
+ // empty list here simply marks the kind spawnable on this host.
81
+ supportedModels: () => [],
82
+ supportedEfforts: () => [],
83
+ // Always available: the engine needs no host-side credential — auth is
84
+ // the per-agent task token, billing is the org's credits.
85
+ available: () => true,
86
+ create,
87
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runuai/host",
3
- "version": "0.9.90",
3
+ "version": "0.9.91",
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>",
package/src/main.ts CHANGED
@@ -2919,6 +2919,9 @@ function expectTaskAgents(value: unknown): TaskAgent[] {
2919
2919
  // effort was previously dropped here even though the protocol + adapters
2920
2920
  // carry it — fixed alongside skills (ADR-046).
2921
2921
  if (typeof row.effort === "string") out.effort = row.effort;
2922
+ if (row.uaiProvider === "anthropic" || row.uaiProvider === "openai") {
2923
+ out.uaiProvider = row.uaiProvider;
2924
+ }
2922
2925
  if (typeof row.defaultPrompt === "string") {
2923
2926
  out.defaultPrompt = row.defaultPrompt;
2924
2927
  }
package/src/protocol.ts CHANGED
@@ -173,6 +173,8 @@ export type PermissionDecision = { kind: "accept" } | { kind: "decline" };
173
173
  * `effort` (when set) is the CLI reasoning level, passed through like `model`.
174
174
  */
175
175
  export interface TaskAgent {
176
+ /** ADR-128: upstream protocol of a Uai-engine model (kind "uai"). */
177
+ uaiProvider?: "anthropic" | "openai";
176
178
  id: string;
177
179
  label: string;
178
180
  kind: string;