@hue-run/sdk 0.19.0 → 0.20.0

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.
Files changed (54) hide show
  1. package/CLI.md +27 -10
  2. package/ENVIRONMENTS.md +5 -2
  3. package/EVALUATIONS.md +6 -1
  4. package/README.md +12 -4
  5. package/WORLDS.md +122 -0
  6. package/dist/blobs.d.ts +2 -1
  7. package/dist/blobs.js +7 -5
  8. package/dist/cli/eval.d.ts +4 -0
  9. package/dist/cli/eval.js +434 -186
  10. package/dist/cli/listen.js +1 -1
  11. package/dist/environment/client.d.ts +2 -95
  12. package/dist/environment/client.js +2 -283
  13. package/dist/environment/tools.d.ts +2 -35
  14. package/dist/environment/tools.js +2 -30
  15. package/dist/environment/types.d.ts +2 -623
  16. package/dist/environment/types.js +2 -1
  17. package/dist/environment/world.d.ts +2 -65
  18. package/dist/environment/world.js +2 -171
  19. package/dist/environment.d.ts +7 -7
  20. package/dist/environment.js +3 -3
  21. package/dist/evals/attempt.d.ts +1 -1
  22. package/dist/evals/client.d.ts +3 -2
  23. package/dist/evals/client.js +2 -1
  24. package/dist/evals/environment-target.d.ts +3 -3
  25. package/dist/evals/environment-target.js +3 -3
  26. package/dist/evals/failure.js +1 -1
  27. package/dist/evals/local-worker.d.ts +8 -3
  28. package/dist/evals/local-worker.js +10 -2
  29. package/dist/evals/simulation.d.ts +6 -3
  30. package/dist/evals/simulation.js +2 -1
  31. package/dist/evals/types.d.ts +2 -2
  32. package/dist/inline-files.d.ts +21 -4
  33. package/dist/inline-files.js +100 -14
  34. package/dist/large-media.d.ts +2 -1
  35. package/dist/large-media.js +7 -4
  36. package/dist/version.d.ts +1 -1
  37. package/dist/version.js +1 -1
  38. package/dist/world/client.d.ts +150 -0
  39. package/dist/world/client.js +368 -0
  40. package/dist/world/definitions.d.ts +47 -0
  41. package/dist/world/definitions.js +1 -0
  42. package/dist/world/model-types.d.ts +117 -0
  43. package/dist/world/model-types.js +1 -0
  44. package/dist/world/models.d.ts +40 -0
  45. package/dist/world/models.js +56 -0
  46. package/dist/world/tools.d.ts +35 -0
  47. package/dist/world/tools.js +30 -0
  48. package/dist/world/types.d.ts +625 -0
  49. package/dist/world/types.js +1 -0
  50. package/dist/world/world.d.ts +65 -0
  51. package/dist/world/world.js +175 -0
  52. package/dist/world.d.ts +13 -0
  53. package/dist/world.js +5 -0
  54. package/package.json +8 -2
@@ -0,0 +1,65 @@
1
+ import type { EnvironmentRun, LegacyMcpCapability, WorldHandoff } from "./types.js";
2
+ /**
3
+ * The world an agent acts on through provider mirrors, read from a create or replay response.
4
+ * Null for a world created while the gateway was off: that world has Hue-native tools and no
5
+ * mirror URLs. The handoff carries the world token; keep it out of logs and checkpoints.
6
+ */
7
+ export declare function worldHandoff(run: EnvironmentRun): WorldHandoff | null;
8
+ /**
9
+ * The world's clock: what an agent reads for today's date in place of the wall clock, so a
10
+ * date-relative request (tomorrow, `newer_than:7d`, this month) lands on the dates the world
11
+ * holds however long after the recording the run starts. From the handoff's `now`, else from
12
+ * `HUE_WORLD_NOW` in the given environment (this process's by default, where `hue eval
13
+ * --command` and `agentEnvironment` set it). Null when neither names it.
14
+ */
15
+ export declare function worldNow(source?: WorldHandoff | Record<string, string | undefined>): Date | null;
16
+ /** Variables that authenticate against Hue's control plane rather than a simulated provider. */
17
+ export declare const HUE_CONTROL_PLANE_VARIABLES: readonly string[];
18
+ /** Prefixes of the Hue control-plane credentials the server issues: project and setup keys,
19
+ * attempt grants, the project MCP key, invocation and installation tokens, and OAuth tokens and
20
+ * secrets. World tokens (`hue_world_`) and MCP capabilities (`hue_sim_`) are what the agent holds. */
21
+ export declare const HUE_CONTROL_PLANE_CREDENTIAL_PREFIXES: readonly string[];
22
+ /** True for a control-plane variable by name, or for a value carrying a Hue control-plane
23
+ * credential anywhere in it, including wrapped, percent-encoded or JSON-Unicode-escaped values. */
24
+ export declare function isHueControlPlaneCredential(name: string, value: string | undefined): boolean;
25
+ /** The parent's variables without Hue control-plane credentials; `undefined` values are dropped. */
26
+ export declare function stripHueControlPlaneCredentials(parent: Record<string, string | undefined>): Record<string, string>;
27
+ /** Options for {@link agentEnvironment}. */
28
+ export interface AgentEnvironmentOptions {
29
+ /** The environment to start from; defaults to this process's. */
30
+ parent?: Record<string, string | undefined>;
31
+ /** Keep Hue control-plane credentials in the child. Off by default: the agent gets world
32
+ * tokens only (plan section 10.3) and the project key stays with the runner. */
33
+ includeHueCredentials?: boolean;
34
+ /** Also set `HUE_MCP_URL`, `HUE_MCP_TOKEN` and `HUE_MCP_EXPIRES_AT` from the world's first
35
+ * MCP mirror, the names the `hue_sim_` bridge used, for one compatibility release. On by
36
+ * default; the canonical carriers are the world's own `env`. */
37
+ legacyMcpVariables?: boolean;
38
+ }
39
+ /** `environment` without any variable a world or its server owns. */
40
+ export declare function withoutWorldVariables(environment: Record<string, string>): Record<string, string>;
41
+ /**
42
+ * The environment for an agent child process running one case (the coordination briefs'
43
+ * recommended policy): the parent's variables minus Hue control-plane credentials and any
44
+ * world-scoped variable, then the world's carriers. Nothing here is logged.
45
+ */
46
+ export declare function agentEnvironment(world: WorldHandoff, options?: AgentEnvironmentOptions): Record<string, string>;
47
+ /** The `{ url, token, expiresAt }` shape the `hue_sim_` capability had, projected from the
48
+ * world's first MCP mirror so an adapter written for the bridge keeps working through the
49
+ * compatibility release. Undefined for a world without an MCP surface. */
50
+ export declare function legacyMcpCapability(world: WorldHandoff): LegacyMcpCapability | undefined;
51
+ /** The owner-only `mcpServers` file {@link writeMcpConfig} wrote. */
52
+ export interface McpConfigFile {
53
+ /** The owner-only file; pass its path to the harness and dispose after the run. */
54
+ path: string;
55
+ /** Removes the file and its private directory; safe to call twice. */
56
+ dispose(): Promise<void>;
57
+ }
58
+ /**
59
+ * Writes the world's `mcpConfig` as an owner-only file in a private directory (plan section
60
+ * 4.9): the file carries the world token, so it is created with mode 0600 inside a 0700
61
+ * directory, is never logged, and `dispose` removes the directory after the run.
62
+ */
63
+ export declare function writeMcpConfig(world: WorldHandoff, options?: {
64
+ directory?: string;
65
+ }): Promise<McpConfigFile>;
@@ -0,0 +1,175 @@
1
+ import { chmod, mkdtemp, rm, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ /**
5
+ * The world an agent acts on through provider mirrors, read from a create or replay response.
6
+ * Null for a world created while the gateway was off: that world has Hue-native tools and no
7
+ * mirror URLs. The handoff carries the world token; keep it out of logs and checkpoints.
8
+ */
9
+ export function worldHandoff(run) {
10
+ if (!run.token || !run.env || !run.mcpConfig || !run.surfaces)
11
+ return null;
12
+ const id = run.worldId ?? run.id;
13
+ return {
14
+ id,
15
+ token: run.token,
16
+ expiresAt: run.expiresAt,
17
+ lifecycle: run.lifecycle ?? "live",
18
+ completingUntil: run.completingUntil ?? null,
19
+ traceparent: run.traceparent ?? null,
20
+ baggage: run.baggage ?? `hue-world=${id}`,
21
+ now: run.now ?? run.env.HUE_WORLD_NOW ?? null,
22
+ surfaces: run.surfaces.map((surface) => ({ ...surface })),
23
+ env: { ...run.env },
24
+ mcpConfig: {
25
+ mcpServers: Object.fromEntries(Object.entries(run.mcpConfig.mcpServers).map(([name, server]) => [
26
+ name,
27
+ { type: server.type, url: server.url, headers: { ...server.headers } },
28
+ ])),
29
+ },
30
+ };
31
+ }
32
+ /**
33
+ * The world's clock: what an agent reads for today's date in place of the wall clock, so a
34
+ * date-relative request (tomorrow, `newer_than:7d`, this month) lands on the dates the world
35
+ * holds however long after the recording the run starts. From the handoff's `now`, else from
36
+ * `HUE_WORLD_NOW` in the given environment (this process's by default, where `hue eval
37
+ * --command` and `agentEnvironment` set it). Null when neither names it.
38
+ */
39
+ export function worldNow(source = process.env) {
40
+ const text = "env" in source && typeof source.env === "object" && source.env !== null
41
+ ? (source.now ?? source.env.HUE_WORLD_NOW)
42
+ : source.HUE_WORLD_NOW;
43
+ if (!text)
44
+ return null;
45
+ const date = new Date(text);
46
+ return Number.isNaN(date.getTime()) ? null : date;
47
+ }
48
+ /** Variables that authenticate against Hue's control plane rather than a simulated provider. */
49
+ export const HUE_CONTROL_PLANE_VARIABLES = [
50
+ "HUE_API_KEY",
51
+ "HUE_MCP_KEY",
52
+ "HUE_PROJECT_KEY",
53
+ "HUE_SERVICE_KEY",
54
+ ];
55
+ /** Prefixes of the Hue control-plane credentials the server issues: project and setup keys,
56
+ * attempt grants, the project MCP key, invocation and installation tokens, and OAuth tokens and
57
+ * secrets. World tokens (`hue_world_`) and MCP capabilities (`hue_sim_`) are what the agent holds. */
58
+ export const HUE_CONTROL_PLANE_CREDENTIAL_PREFIXES = [
59
+ "hue_sk_",
60
+ "hue_attempt_",
61
+ "hue_mcp_",
62
+ "hue_setup_",
63
+ "hue_inv_",
64
+ "hue_install_",
65
+ "hue_at_",
66
+ "hue_rt_",
67
+ "hue_oauth_",
68
+ ];
69
+ /** A Hue credential anywhere in a value, including JSON Unicode escapes, not inside a longer word. */
70
+ const HUE_CREDENTIAL_SHAPE = new RegExp(`(?:^|[^A-Za-z0-9_])(?:${HUE_CONTROL_PLANE_CREDENTIAL_PREFIXES.join("|")})`);
71
+ const ASCII_PERCENT_ESCAPE = /%[0-7][0-9a-f]/i;
72
+ const ASCII_JSON_UNICODE_ESCAPE = /\\u00([0-7][0-9a-fA-F])/g;
73
+ function decodeAsciiJsonUnicodeEscapes(value) {
74
+ return value.replace(ASCII_JSON_UNICODE_ESCAPE, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)));
75
+ }
76
+ function hasHueControlPlaneCredential(value) {
77
+ let candidates = new Set([value]);
78
+ for (let layer = 0; layer <= 4; layer++) {
79
+ const jsonDecodedCandidates = new Set();
80
+ for (const candidate of candidates) {
81
+ jsonDecodedCandidates.add(candidate);
82
+ jsonDecodedCandidates.add(decodeAsciiJsonUnicodeEscapes(candidate));
83
+ }
84
+ if ([...jsonDecodedCandidates].some((candidate) => HUE_CREDENTIAL_SHAPE.test(candidate)))
85
+ return true;
86
+ if (layer === 4)
87
+ return [...jsonDecodedCandidates].some((candidate) => ASCII_PERCENT_ESCAPE.test(candidate));
88
+ candidates = new Set([...jsonDecodedCandidates]
89
+ .filter((candidate) => ASCII_PERCENT_ESCAPE.test(candidate))
90
+ .map((candidate) => candidate.replace(/%([0-7][0-9a-f])/gi, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)))));
91
+ if (candidates.size === 0)
92
+ return false;
93
+ }
94
+ return false;
95
+ }
96
+ /** True for a control-plane variable by name, or for a value carrying a Hue control-plane
97
+ * credential anywhere in it, including wrapped, percent-encoded or JSON-Unicode-escaped values. */
98
+ export function isHueControlPlaneCredential(name, value) {
99
+ if (HUE_CONTROL_PLANE_VARIABLES.includes(name))
100
+ return true;
101
+ return typeof value === "string" && hasHueControlPlaneCredential(value);
102
+ }
103
+ /** The parent's variables without Hue control-plane credentials; `undefined` values are dropped. */
104
+ export function stripHueControlPlaneCredentials(parent) {
105
+ const child = {};
106
+ for (const [name, value] of Object.entries(parent)) {
107
+ if (value === undefined || isHueControlPlaneCredential(name, value))
108
+ continue;
109
+ child[name] = value;
110
+ }
111
+ return child;
112
+ }
113
+ /** Variables a world or its server owns: a parent's value belongs to another world (a runner
114
+ * started inside a case, a shell left from an earlier run) or to the server (a signing key), and
115
+ * would send this world's token to a mirror this world does not have. The whole `HUE_WORLD_`
116
+ * prefix is the world's. */
117
+ const WORLD_SCOPED = /^(?:HUE_WORLD_[A-Z0-9_]+|HUE_MCP_(?:CONFIG|URL|TOKEN|EXPIRES_AT)|HUE_SIM_[A-Z0-9_]+_(?:URL|ALIAS|TOKEN))$/;
118
+ /** `environment` without any variable a world or its server owns. */
119
+ export function withoutWorldVariables(environment) {
120
+ return Object.fromEntries(Object.entries(environment).filter(([name]) => !WORLD_SCOPED.test(name)));
121
+ }
122
+ /**
123
+ * The environment for an agent child process running one case (the coordination briefs'
124
+ * recommended policy): the parent's variables minus Hue control-plane credentials and any
125
+ * world-scoped variable, then the world's carriers. Nothing here is logged.
126
+ */
127
+ export function agentEnvironment(world, options = {}) {
128
+ const parent = options.parent ?? process.env;
129
+ const child = withoutWorldVariables(options.includeHueCredentials
130
+ ? Object.fromEntries(Object.entries(parent).filter((entry) => entry[1] !== undefined))
131
+ : stripHueControlPlaneCredentials(parent));
132
+ Object.assign(child, world.env);
133
+ if (options.legacyMcpVariables ?? true) {
134
+ const legacy = legacyMcpCapability(world);
135
+ if (legacy) {
136
+ child.HUE_MCP_URL = legacy.url;
137
+ child.HUE_MCP_TOKEN = legacy.token;
138
+ child.HUE_MCP_EXPIRES_AT = legacy.expiresAt;
139
+ }
140
+ }
141
+ return child;
142
+ }
143
+ /** The `{ url, token, expiresAt }` shape the `hue_sim_` capability had, projected from the
144
+ * world's first MCP mirror so an adapter written for the bridge keeps working through the
145
+ * compatibility release. Undefined for a world without an MCP surface. */
146
+ export function legacyMcpCapability(world) {
147
+ const server = Object.values(world.mcpConfig.mcpServers)[0];
148
+ if (!server)
149
+ return undefined;
150
+ const authorization = server.headers?.Authorization;
151
+ const scoped = authorization === undefined ? undefined : /^Bearer ([^\s]+)$/.exec(authorization);
152
+ if (authorization !== undefined && !scoped)
153
+ return undefined;
154
+ return { url: server.url, token: scoped?.[1] ?? world.token, expiresAt: world.expiresAt };
155
+ }
156
+ /**
157
+ * Writes the world's `mcpConfig` as an owner-only file in a private directory (plan section
158
+ * 4.9): the file carries the world token, so it is created with mode 0600 inside a 0700
159
+ * directory, is never logged, and `dispose` removes the directory after the run.
160
+ */
161
+ export async function writeMcpConfig(world, options = {}) {
162
+ const directory = await mkdtemp(join(options.directory ?? tmpdir(), "hue-world-"));
163
+ const dispose = () => rm(directory, { recursive: true, force: true });
164
+ try {
165
+ await chmod(directory, 0o700);
166
+ const path = join(directory, "mcp.json");
167
+ await writeFile(path, `${JSON.stringify(world.mcpConfig, null, 2)}\n`, { mode: 0o600 });
168
+ return { path, dispose };
169
+ }
170
+ catch (error) {
171
+ // A half-written file would hold the token; never leave it behind.
172
+ await dispose();
173
+ throw error;
174
+ }
175
+ }
@@ -0,0 +1,13 @@
1
+ /** World API client and isolated instance handoff helpers. */
2
+ export { createWorldClient, WorldClient, HueWorldError, isTransientWorldError, } from "./world/client.js";
3
+ export type { WorldClientOptions, CreateInstanceInput } from "./world/client.js";
4
+ export { bindEnvironmentTools as bindWorldTools } from "./world/tools.js";
5
+ export type { BindEnvironmentToolsOptions as BindWorldToolsOptions, EnvironmentTool as WorldTool, } from "./world/tools.js";
6
+ export type * from "./world/types.js";
7
+ export type { EnvironmentVersionSummary as WorldVersionSummary, EnvironmentRun as WorldInstance, EnvironmentCoverage as WorldCoverage, } from "./world/types.js";
8
+ export { agentEnvironment, HUE_CONTROL_PLANE_CREDENTIAL_PREFIXES, HUE_CONTROL_PLANE_VARIABLES, isHueControlPlaneCredential, legacyMcpCapability, stripHueControlPlaneCredentials, worldHandoff, worldNow, writeMcpConfig, } from "./world/world.js";
9
+ export type { AgentEnvironmentOptions, McpConfigFile } from "./world/world.js";
10
+ export { WorldModelClient } from "./world/models.js";
11
+ export type { WorldInstanceResult } from "./world/models.js";
12
+ export type * from "./world/model-types.js";
13
+ export type * from "./world/definitions.js";
package/dist/world.js ADDED
@@ -0,0 +1,5 @@
1
+ /** World API client and isolated instance handoff helpers. */
2
+ export { createWorldClient, WorldClient, HueWorldError, isTransientWorldError, } from "./world/client.js";
3
+ export { bindEnvironmentTools as bindWorldTools } from "./world/tools.js";
4
+ export { agentEnvironment, HUE_CONTROL_PLANE_CREDENTIAL_PREFIXES, HUE_CONTROL_PLANE_VARIABLES, isHueControlPlaneCredential, legacyMcpCapability, stripHueControlPlaneCredentials, worldHandoff, worldNow, writeMcpConfig, } from "./world/world.js";
5
+ export { WorldModelClient } from "./world/models.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hue-run/sdk",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -33,6 +33,7 @@
33
33
  "setup-events.schema.json",
34
34
  "README.md",
35
35
  "EVALUATIONS.md",
36
+ "WORLDS.md",
36
37
  "ENVIRONMENTS.md",
37
38
  "MANAGED_TARGETS.md",
38
39
  "LICENSE"
@@ -76,7 +77,12 @@
76
77
  "default": "./dist/setup.js"
77
78
  },
78
79
  "./setup-events.schema.json": "./setup-events.schema.json",
79
- "./package.json": "./package.json"
80
+ "./package.json": "./package.json",
81
+ "./world": {
82
+ "types": "./dist/world.d.ts",
83
+ "import": "./dist/world.js",
84
+ "default": "./dist/world.js"
85
+ }
80
86
  },
81
87
  "scripts": {
82
88
  "build": "node scripts/write-version.mjs && tsc -p tsconfig.build.json",