@hue-run/sdk 0.5.0 → 0.6.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.
package/CLI.md CHANGED
@@ -42,7 +42,7 @@ project manifest are refused because managers can update ancestor locks; Python
42
42
  The generated `hue.setup.mjs` or `hue_setup.py` always selects `captureContent: false` /
43
43
  `capture_content=False`. For a supported application, setup installs the dependency and adds the
44
44
  managed import and middleware registration to the existing entrypoint; an unreferenced helper is
45
- not a completed integration. TypeScript uses `@hue-run/sdk@0.5.0`, `@opentelemetry/api@1.9.1` and
45
+ not a completed integration. TypeScript uses `@hue-run/sdk@0.6.0`, `@opentelemetry/api@1.9.1` and
46
46
  `@opentelemetry/context-async-hooks@2.11.0`; Python uses published `hue-run==0.2.2`.
47
47
  Content capture requires an ordinary account-managed key and a later explicit application decision.
48
48
 
@@ -145,14 +145,17 @@ handoff does not create a trial, reset quota or rerun business work.
145
145
  `hue login` stores keys that a person creates in Hue; it never mints one, because setup
146
146
  credentials are deliberately isolated from ordinary project keys. It prints the key settings page
147
147
  (`<origin>/settings/integrations`, opened in a browser only when a terminal is attached and
148
- `--no-browser` is absent), then reads each requested key from stdin without echo. A
149
- **Tracing and evaluations** key is validated with `GET /api/v1/projects/current` and stored as
150
- `HUE_API_KEY` with `HUE_BASE_URL`; a **Coding agent (read + evaluations)** key is validated with an
151
- MCP `tools/list` request and stored as `HUE_MCP_KEY` with `HUE_MCP_URL`. A rejected key (`401` or
152
- `403`) exits `1` and stores nothing for that key.
148
+ `--no-browser` is absent), then reads one **Read and write** key from stdin without echo. By
149
+ default that single key serves both uses: it is checked with `GET /api/v1/projects/current` and
150
+ `GET /api/v1/datasets` (a **Read** or **Tracing only** key is refused because it cannot use
151
+ evaluations) and with an MCP `tools/list` request, then stored as `HUE_API_KEY` with
152
+ `HUE_BASE_URL` and `HUE_MCP_KEY` with `HUE_MCP_URL` in one write. A rejected key (`401` or `403`)
153
+ exits `1` and stores nothing. `--keys evaluations` stores only `HUE_API_KEY`; `--keys coding-agent`
154
+ stores only `HUE_MCP_KEY` and also accepts a **Read** key, for an agent that should only inspect the
155
+ project. Run them separately with different keys if you want to revoke either use alone.
153
156
 
154
157
  ```sh
155
- hue login # both keys into ./.env.hue
158
+ hue login # one key for both uses into ./.env.hue
156
159
  hue login --keys coding-agent --gitignore # only HUE_MCP_KEY; add .env.hue to .gitignore
157
160
  hue login --origin https://staging.hue.run --env-file .env.staging
158
161
  ```
@@ -163,7 +166,7 @@ replaced only with `--force`. Empty values, whitespace and URLs are refused befo
163
166
  The MCP endpoint is `https://mcp.hue.run/mcp` for `https://app.hue.run`,
164
167
  `https://mcp.staging.hue.run/mcp` for `https://staging.hue.run` and `<origin>/api/mcp` otherwise;
165
168
  plain HTTP origins are accepted for loopback test servers only. Output names variables and lengths
166
- (`Stored HUE_API_KEY (NN chars)`), never values. When git does not ignore the env file, the command
169
+ (`Stored the key (NN chars) as HUE_API_KEY and HUE_MCP_KEY`), never values. When git does not ignore the env file, the command
167
170
  warns; `--gitignore` appends the file name to the `.gitignore` next to it. Exit codes: `0` stored,
168
171
  `1` failed, `2` usage error, `130` interrupted.
169
172
 
@@ -297,7 +300,7 @@ node packages/sdk-typescript/scripts/verify-package.mjs --artifacts-dir .artifac
297
300
  # Set project to an existing supported fixture; use the same directory on resume.
298
301
  project=/absolute/path/to/supported-fixture
299
302
  node packages/sdk-typescript/scripts/verify-setup-live.mjs \
300
- --archive .artifacts/typescript/hue-run-sdk-0.5.0.tgz \
303
+ --archive .artifacts/typescript/hue-run-sdk-0.6.0.tgz \
301
304
  --origin https://STAGING_ORIGIN \
302
305
  --project "$project" --command setup \
303
306
  --evidence .context/setup-staging-before-claim.json
@@ -308,7 +311,7 @@ the private local handoff and finish the real browser claim, then reconcile the
308
311
 
309
312
  ```sh
310
313
  node packages/sdk-typescript/scripts/verify-setup-live.mjs \
311
- --archive .artifacts/typescript/hue-run-sdk-0.5.0.tgz \
314
+ --archive .artifacts/typescript/hue-run-sdk-0.6.0.tgz \
312
315
  --origin https://STAGING_ORIGIN \
313
316
  --project "$project" --command claim \
314
317
  --evidence .context/setup-staging-after-claim.json
@@ -330,14 +333,14 @@ Publication under a candidate dist-tag, registry acceptance, production activati
330
333
  the same version to `latest`, and clean-project literal `@latest` smoke remain separate gates; see
331
334
  [RELEASING.md](../../RELEASING.md).
332
335
 
333
- ## Evaluate an agent against a Scenario
336
+ ## Evaluate an agent against a case
334
337
 
335
- `hue eval` runs a developer's local agent against a published Hue Scenario or a saved eval set
338
+ `hue eval` runs a developer's local agent against a published Hue case or a saved eval set
336
339
  and prints Hue's verdicts. It is the command-line form of `runSimulation()` (one-shot) and
337
340
  `runLocalAgent()` (worker): the agent, its prompts and its provider credentials stay in the local
338
341
  process, Hue creates one isolated simulated world per case, and Hue-owned outcome checks grade
339
- the sealed world. Hue never executes the agent. `HUE_API_KEY` must be a **Tracing and
340
- evaluations** project key (a **Tracing only** key cannot read Scenarios or create experiments);
342
+ the sealed world. Hue never executes the agent. `HUE_API_KEY` must be a **Read and
343
+ write** project key (a **Read** or **Tracing only** key cannot read cases or create runs);
341
344
  the CLI never prints it. The optional `zod` peer of `@hue-run/sdk/evals` must be installed.
342
345
 
343
346
  Write an adapter module that hands the case inputs and the world's tools or MCP connection to
@@ -359,16 +362,18 @@ export default function runMyAgent(inputs: JsonValue, context: SimulationTargetC
359
362
  ```
360
363
 
361
364
  ```sh
362
- hue eval --scenario "Refund an eligible charge" ./hue-agent.ts --env-file .env.hue
363
- hue eval --scenario https://app.hue.run/projects/demo/scenarios/<id> ./hue-agent.ts --baseline <experiment id>
365
+ hue eval --case "Refund an eligible charge" ./hue-agent.ts --env-file .env.hue
366
+ hue eval --case https://app.hue.run/projects/demo/scenarios/<id> ./hue-agent.ts --baseline <run id>
364
367
  hue eval --set "Billing regressions" --scorer-version <id> ./hue-agent.ts --save-version
365
- hue eval --scenario "Refund an eligible charge" --command "python agent.py" --timeout 120 --content
368
+ hue eval --case "Refund an eligible charge" --command "python agent.py" --timeout 120 --content
366
369
  hue eval --worker ./hue-agent.ts --agent-key support-agent --env-file .env.hue
367
370
  ```
368
371
 
369
- The one-shot mode resolves the selection (`--scenario` by name, ID or URL; `--set` by name, ID or
372
+ `--scenario` remains an alias for `--case` for existing scripts. Pass one selection flag.
373
+
374
+ The one-shot mode resolves the selection (`--case` by name, ID or URL; `--set` by name, ID or
370
375
  URL with explicit `--scorer-version` pins; or `--dataset-version` with `--scorer-version`),
371
- creates a fresh experiment from those immutable pins named `<scenario> · <agent key> · <revision>`
376
+ creates a fresh run from those immutable pins named `<case> · <agent key> · <revision>`
372
377
  (`--name` overrides), prints `Run: <url>` and `Experiment: <id>` as soon as the experiment exists,
373
378
  one line per case event (world created, agent started, world sealed), then
374
379
  `Waiting for Hue checks...` and a table with one row per case: boolean metrics as `PASS`/`FAIL`,
@@ -418,7 +423,7 @@ on any Node.js version.
418
423
 
419
424
  Eval sets whose cases pin no simulated world — a task plus pinned input files, answered with
420
425
  generated documents — run as **direct** cases through `runExperiment()`. `hue eval` detects this
421
- from the saved version (`--mode direct|simulation` overrides the detection; `--scenario` is always
426
+ from the saved version (`--mode direct|simulation` overrides the detection; `--case` is always
422
427
  a simulation). `--set` accepts the eval set's slug, name, ID or URL; `--set-version <n>` pins a
423
428
  saved version other than the latest; `--scorer <slug|name|id>` pins an evaluator at its newest
424
429
  published version, beside or instead of explicit `--scorer-version` IDs.
package/ENVIRONMENTS.md CHANGED
@@ -66,7 +66,7 @@ to the callback and is never written to checkpoints.
66
66
  published immutable pins, such as a Scenario's frozen case and Hue-owned outcome checks or a saved
67
67
  eval set with explicitly chosen scorer versions. `resolveScenarioPins(client, selector)` reads
68
68
  those pins from a Scenario ID, its Hue URL or its name (`listScenarios` and `getScenario` expose
69
- the underlying reads; a Tracing and evaluations key is required), and `resolveEvalSetPins`
69
+ the underlying reads; a Read and write key is required), and `resolveEvalSetPins`
70
70
  resolves an eval set to its latest saved version. `runSimulation` creates the experiment directly
71
71
  (`name` defaults to the dataset name, `config` to `{}`) and binds the pins and configuration into
72
72
  the checkpoint identity, so resuming with different pins is refused like the other kinds.
@@ -79,7 +79,7 @@ until every item has a terminal result for every pin or the budget elapses (`com
79
79
  `summarizeVerdicts` turns results into per-case rows with `passed` and totals; `compareVerdicts`
80
80
  diffs two summaries by case key; and `collectExperimentVerdicts` combines those reads for one
81
81
  experiment. The `hue eval` command uses the same path; see
82
- [Evaluate an agent against a Scenario](CLI.md#evaluate-an-agent-against-a-scenario).
82
+ [Evaluate an agent against a case](CLI.md#evaluate-an-agent-against-a-case).
83
83
 
84
84
  ### Pinned provider-profile preflight
85
85
 
package/EVALUATIONS.md CHANGED
@@ -9,7 +9,7 @@ npm install @hue-run/sdk zod
9
9
 
10
10
  The SDK executes targets and scorers on your machine. Hue stores pinned definitions, experiment progress and results. It does not execute uploaded source code. Follow the [installation guide](https://docs.hue.run/installation) to add `@hue-run/sdk` to your application.
11
11
 
12
- Create a **Tracing and evaluations** project service key under **Settings → Integrations & API keys** and expose it to this server-side process as `HUE_API_KEY`. A **Tracing only** key cannot author datasets or evaluation runs.
12
+ Create a **Read and write** project service key under **Settings → Integrations & API keys** and expose it to this server-side process as `HUE_API_KEY`. A **Tracing only** key cannot author datasets or evaluation runs.
13
13
 
14
14
  ```ts
15
15
  import { randomUUID } from "node:crypto";
@@ -70,6 +70,25 @@ try {
70
70
  }
71
71
  ```
72
72
 
73
+ For new registry code, use `createEvalSet`, `getEvalSet`, `listEvalSets`,
74
+ `createEvalSetVersion`, `getEvalSetVersion`, `listEvalSetCases`, `addEvalSetCase`,
75
+ and `freezeEvalSetVersion`. Evaluators use `createEvaluator`, `getEvaluator`,
76
+ `listEvaluators`, `publishEvaluatorVersion`, and `getEvaluatorVersion`. These
77
+ methods return product fields such as `evalSetId` and `evalSetVersionId` alongside
78
+ the existing v1 fields. Evaluator versions include `evaluatorId` when the server
79
+ supplies their owning identity; older v1 responses may omit it. They use the existing v1 paths;
80
+ the earlier method names remain callable for existing integrations.
81
+
82
+ For new run and scoring code, use `createRun`, `getRun`, `listRunItems`,
83
+ `getRunCase`, `startRunExecution`, `getRunExecution`,
84
+ `completeRunExecution`, and `finishRun`. Use `createScoring`, `getScoring`,
85
+ `listScorings`, `listScoringItems`, `getScoringSubject`,
86
+ `submitScoringResults`, `listScoringResults`, and `getScoringResult` to score
87
+ saved subjects. `createRun` accepts `evalSetVersionId` and
88
+ `evaluatorVersionIds`; `createScoring` and `submitScoringResults` use evaluator
89
+ version IDs. A run ID and a scoring ID identify different records. The methods
90
+ use the existing v1 paths and leave existing runner entry points callable.
91
+
73
92
  Create another experiment with the same frozen version and different `config` to compare configurations. The runner reads the exact experiment case/version and scorer definitions; it never resolves a mutable latest version. `rescore` accepts an existing evaluation-run ID and has no target callback. Subject IDs refer to immutable saved outputs and trace evidence.
74
93
 
75
94
  `rescore` preserves terminal scores already recorded for each item and evaluator version,
package/README.md CHANGED
@@ -445,12 +445,12 @@ their agents with `runLocalAgent()`; setup does not register workers or launch s
445
445
  ### Command-line evaluation
446
446
 
447
447
  The unreleased `hue eval` command wraps `runSimulation()` and `runLocalAgent()` for an adapter
448
- file or a shell command: `hue eval --scenario "<name>" ./hue-agent.ts` creates a fresh experiment
449
- from a published Scenario's immutable pins, runs the agent in one isolated world per case, waits
448
+ file or a shell command: `hue eval --case "<name>" ./hue-agent.ts` creates a fresh run
449
+ from a published case's immutable pins, runs the agent in one isolated world per case, waits
450
450
  for Hue's outcome checks and prints the run URL and per-case PASS/FAIL verdicts with an exit code;
451
- `--worker` registers the same adapter for runs launched from Hue. It needs a Tracing and
452
- evaluations key in `HUE_API_KEY` (never printed) and keeps content capture off unless `--content`
453
- is passed. See [Evaluate an agent against a Scenario](CLI.md#evaluate-an-agent-against-a-scenario).
451
+ `--worker` registers the same adapter for runs launched from Hue. It needs a Read and
452
+ write key in `HUE_API_KEY` (never printed) and keeps content capture off unless `--content`
453
+ is passed. See [Evaluate an agent against a case](CLI.md#evaluate-an-agent-against-a-case).
454
454
  Eval sets whose cases pin files instead of a world run as direct cases through `runExperiment()`:
455
455
  `hue eval --set <slug> --scorer <slug> --command "…"` hands the agent each case's pinned files in a
456
456
  private directory, uploads the documents it writes and waits for Hue's grading executor to score
package/dist/cli/eval.js CHANGED
@@ -18,10 +18,11 @@ import { runSimulation, } from "../evals/simulation.js";
18
18
  import { collectExperimentVerdicts, compareVerdicts, metricPassed, } from "../evals/verdicts.js";
19
19
  const USAGE = `Usage: hue eval [adapter-file] [options]
20
20
 
21
- Run a local agent against a Hue Scenario or eval set, then print Hue's verdicts.
21
+ Run a local agent against a published case or eval set, then print Hue's verdicts.
22
22
 
23
23
  Selection (exactly one, not used with --worker):
24
- --scenario <name|id|url> Published Scenario to run
24
+ --case <name|id|url> Published case to run
25
+ --scenario <name|id|url> Legacy alias for --case
25
26
  --set <name|id|url> Saved eval set; requires --scorer or --scorer-version
26
27
  --set-version <n> Saved version number of the eval set (default: latest saved)
27
28
  --dataset-version <id> Frozen dataset version; requires --scorer or --scorer-version
@@ -51,7 +52,7 @@ Connection:
51
52
  --origin <url> Hue origin (default: HUE_BASE_URL or https://app.hue.run)
52
53
 
53
54
  Output and limits:
54
- --name <run name> Experiment name (default: <scenario> · <agent key> · <revision>)
55
+ --name <run name> Run name (default: <case> · <agent key> · <revision>)
55
56
  --baseline <experiment id|url> Compare verdicts with a previous experiment
56
57
  --json Print one JSON document on stdout; progress goes to stderr
57
58
  --content Capture telemetry content; one-shot also persists
@@ -63,7 +64,7 @@ Output and limits:
63
64
  --wait <seconds> Verdict wait after the run finishes (default: 300)
64
65
  -h, --help Show this help
65
66
 
66
- HUE_API_KEY must be a "Tracing and evaluations" project key; it is never printed.
67
+ HUE_API_KEY must be a "Read and write" project key; it is never printed.
67
68
  Code evaluators pinned to a direct run are graded by Hue's executor after the upload; the wait
68
69
  covers them. Exit codes: 0 every case passed, 1 a case failed, errored or is incomplete,
69
70
  2 usage error, 130 interrupted.
@@ -82,6 +83,7 @@ function parse(argv) {
82
83
  allowPositionals: true,
83
84
  strict: true,
84
85
  options: {
86
+ case: { type: "string" },
85
87
  scenario: { type: "string" },
86
88
  set: { type: "string" },
87
89
  "set-version": { type: "string" },
@@ -414,7 +416,7 @@ function redact(message, secrets) {
414
416
  }
415
417
  function explain(error) {
416
418
  if (error instanceof HueApiError && (error.status === 401 || error.status === 403))
417
- return `${error.message}. Check that HUE_API_KEY is a "Tracing and evaluations" project key for this origin.`;
419
+ return `${error.message}. Check that HUE_API_KEY is a "Read and write" project key for this origin.`;
418
420
  if (error instanceof Error) {
419
421
  const causes = [];
420
422
  let cause = error.cause;
@@ -508,15 +510,16 @@ async function resolveSelection(client, values) {
508
510
  const extra = [...(values["scorer-version"] ?? [])];
509
511
  for (const selector of values.scorer ?? [])
510
512
  extra.push(await resolveScorerVersion(client, selector));
511
- if (values.scenario) {
513
+ const caseSelector = values.case ?? values.scenario;
514
+ if (caseSelector) {
512
515
  if (values["set-version"])
513
516
  throw new UsageError("--set-version applies to --set only");
514
- const pins = await resolveScenarioPins(client, values.scenario);
517
+ const pins = await resolveScenarioPins(client, caseSelector);
515
518
  pins.scorerVersionIds = [...new Set([...pins.scorerVersionIds, ...extra])];
516
519
  return pins;
517
520
  }
518
521
  if (!extra.length)
519
- throw new UsageError(`${values.set ? "--set" : "--dataset-version"} needs at least one --scorer or --scorer-version; use --scenario for published pins`);
522
+ throw new UsageError(`${values.set ? "--set" : "--dataset-version"} needs at least one --scorer or --scorer-version; use --case for published pins`);
520
523
  if (values.set) {
521
524
  const pins = await resolveEvalSetPins(client, values.set, { scorerVersionIds: extra });
522
525
  if (values["set-version"] === undefined)
@@ -918,11 +921,11 @@ export async function runEvalCommand(argv) {
918
921
  const adapterFile = positionals[0];
919
922
  if ((adapterFile ? 1 : 0) + (values.command ? 1 : 0) !== 1)
920
923
  throw new UsageError("Pass exactly one agent: an adapter file or --command");
921
- const selections = [values.scenario, values.set, values["dataset-version"]].filter((value) => value !== undefined).length;
924
+ const selections = [values.case, values.scenario, values.set, values["dataset-version"]].filter((value) => value !== undefined).length;
922
925
  if (values.worker && selections)
923
926
  throw new UsageError("--worker takes no selection; Hue chooses the run to execute");
924
927
  if (!values.worker && selections !== 1)
925
- throw new UsageError("Pass exactly one of --scenario, --set or --dataset-version");
928
+ throw new UsageError("Pass exactly one of --case, --set or --dataset-version");
926
929
  if (values["env-file"]) {
927
930
  try {
928
931
  process.loadEnvFile(resolve(values["env-file"]));
@@ -933,7 +936,7 @@ export async function runEvalCommand(argv) {
933
936
  }
934
937
  const apiKey = process.env.HUE_API_KEY?.trim();
935
938
  if (!apiKey)
936
- throw new UsageError('HUE_API_KEY is required: a "Tracing and evaluations" project key, set in the environment or an ignored --env-file');
939
+ throw new UsageError('HUE_API_KEY is required: a "Read and write" project key, set in the environment or an ignored --env-file');
937
940
  secrets.push(apiKey);
938
941
  const baseUrl = values.origin ?? process.env.HUE_BASE_URL?.trim() ?? "https://app.hue.run";
939
942
  const connection = { apiKey, baseUrl };
@@ -14,7 +14,7 @@ export interface LoginCommandIo {
14
14
  fetch?: typeof fetch;
15
15
  openBrowser?: (url: string) => Promise<boolean>;
16
16
  }
17
- export declare const LOGIN_USAGE = "Usage: hue login [--origin URL] [--env-file PATH] [--keys evaluations|coding-agent|both]\n [--no-browser] [--force] [--gitignore]\n\nStore keys you created in Hue in a private env file. Each key is validated against Hue before it\nis stored; key values are never printed.\n\nOptions:\n --origin URL Hue origin (default https://app.hue.run)\n --env-file PATH Env file to write (default .env.hue in the current directory)\n --keys KIND evaluations (HUE_API_KEY), coding-agent (HUE_MCP_KEY) or both (default both)\n --no-browser Do not open the key settings page in a browser\n --force Replace an existing different value in the env file\n --gitignore Add the env file to .gitignore when a git repository does not ignore it\n -h, --help Show this help";
17
+ export declare const LOGIN_USAGE = "Usage: hue login [--origin URL] [--env-file PATH] [--keys evaluations|coding-agent|both]\n [--no-browser] [--force] [--gitignore]\n\nStore the key you created in Hue in a private env file. By default one \"Read and write\" key serves\nboth evaluations (HUE_API_KEY) and your coding agent (HUE_MCP_KEY); it is validated against Hue\nbefore it is stored, and key values are never printed.\n\nOptions:\n --origin URL Hue origin (default https://app.hue.run)\n --env-file PATH Env file to write (default .env.hue in the current directory)\n --keys KIND evaluations (HUE_API_KEY), coding-agent (HUE_MCP_KEY) or both from one key\n (default both)\n --no-browser Do not open the key settings page in a browser\n --force Replace an existing different value in the env file\n --gitignore Add the env file to .gitignore when a git repository does not ignore it\n -h, --help Show this help";
18
18
  /** Hue MCP endpoint that pairs with an application origin. */
19
19
  export declare function mcpUrlForOrigin(origin: string): string;
20
20
  /** Normalizes a Hue origin: HTTPS, or HTTP for loopback only; no credentials, path, query or hash. */
package/dist/cli/login.js CHANGED
@@ -19,24 +19,26 @@ const KEY_KINDS = {
19
19
  evaluations: {
20
20
  variable: "HUE_API_KEY",
21
21
  urlVariable: "HUE_BASE_URL",
22
- preset: "Tracing and evaluations",
23
22
  },
24
23
  "coding-agent": {
25
24
  variable: "HUE_MCP_KEY",
26
25
  urlVariable: "HUE_MCP_URL",
27
- preset: "Coding agent (read + evaluations)",
28
26
  },
29
27
  };
28
+ /** Settings preset that authorizes both evaluations and the coding agent's MCP reads and writes. */
29
+ const KEY_PRESET = "Read and write";
30
30
  export const LOGIN_USAGE = `Usage: hue login [--origin URL] [--env-file PATH] [--keys evaluations|coding-agent|both]
31
31
  [--no-browser] [--force] [--gitignore]
32
32
 
33
- Store keys you created in Hue in a private env file. Each key is validated against Hue before it
34
- is stored; key values are never printed.
33
+ Store the key you created in Hue in a private env file. By default one "Read and write" key serves
34
+ both evaluations (HUE_API_KEY) and your coding agent (HUE_MCP_KEY); it is validated against Hue
35
+ before it is stored, and key values are never printed.
35
36
 
36
37
  Options:
37
38
  --origin URL Hue origin (default ${DEFAULT_ORIGIN})
38
39
  --env-file PATH Env file to write (default ${DEFAULT_ENV_FILE} in the current directory)
39
- --keys KIND evaluations (HUE_API_KEY), coding-agent (HUE_MCP_KEY) or both (default both)
40
+ --keys KIND evaluations (HUE_API_KEY), coding-agent (HUE_MCP_KEY) or both from one key
41
+ (default both)
40
42
  --no-browser Do not open the key settings page in a browser
41
43
  --force Replace an existing different value in the env file
42
44
  --gitignore Add the env file to .gitignore when a git repository does not ignore it
@@ -216,7 +218,10 @@ async function readBoundedText(response) {
216
218
  function isRecord(value) {
217
219
  return typeof value === "object" && value !== null && !Array.isArray(value);
218
220
  }
219
- /** Mirrors `checkConnection()`: `GET /api/v1/projects/current` with the key, no redirects. */
221
+ /**
222
+ * Mirrors `checkConnection()` with `GET /api/v1/projects/current`, then confirms evaluation access
223
+ * with `GET /api/v1/datasets`. No redirects are followed.
224
+ */
220
225
  async function checkEvaluationsKey(fetchImpl, origin, apiKey) {
221
226
  let response;
222
227
  try {
@@ -246,15 +251,44 @@ async function checkEvaluationsKey(fetchImpl, origin, apiKey) {
246
251
  detail: `Hue answered HTTP ${response.status} while checking the evaluations key.`,
247
252
  };
248
253
  }
254
+ let projectName;
249
255
  try {
250
256
  const project = JSON.parse(await readBoundedText(response));
251
257
  if (!isRecord(project) || typeof project.name !== "string")
252
258
  throw new Error("Invalid project");
253
- return { ok: true, detail: project.name };
259
+ projectName = project.name;
254
260
  }
255
261
  catch {
256
262
  return { ok: false, rejected: false, detail: "Hue returned an unexpected project response." };
257
263
  }
264
+ // Every valid key reaches the project check; only a key with write access can list eval sets,
265
+ // so a Read or Tracing only key is refused here instead of failing later in `hue eval`.
266
+ let evaluations;
267
+ try {
268
+ evaluations = await fetchImpl(`${origin}/api/v1/datasets`, {
269
+ method: "GET",
270
+ headers: { authorization: `Bearer ${apiKey}`, accept: "application/json" },
271
+ redirect: "error",
272
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MILLIS),
273
+ });
274
+ }
275
+ catch {
276
+ return { ok: false, rejected: false, detail: `Could not reach ${origin}.` };
277
+ }
278
+ await evaluations.body?.cancel();
279
+ if (evaluations.status === 401 || evaluations.status === 403)
280
+ return {
281
+ ok: false,
282
+ rejected: true,
283
+ detail: `This key cannot use evaluations (HTTP ${evaluations.status}); it is a Read or Tracing only key.`,
284
+ };
285
+ if (!evaluations.ok)
286
+ return {
287
+ ok: false,
288
+ rejected: false,
289
+ detail: `Hue answered HTTP ${evaluations.status} while checking evaluation access.`,
290
+ };
291
+ return { ok: true, detail: projectName };
258
292
  }
259
293
  /** Reads JSON-RPC messages from a JSON body or a `text/event-stream` body. */
260
294
  function parseJsonRpcMessages(text, contentType) {
@@ -574,6 +608,9 @@ export async function runLoginCommand(argv, io = {}) {
574
608
  if (keysOption !== "evaluations" && keysOption !== "coding-agent" && keysOption !== "both")
575
609
  return fail(`--keys must be evaluations, coding-agent or both.\n\n${LOGIN_USAGE}`, 2);
576
610
  const kinds = keysOption === "both" ? ["evaluations", "coding-agent"] : [keysOption];
611
+ // Every requested variable comes from one pasted key: the same "Read and write" preset serves
612
+ // evaluations and the coding agent, so asking twice would only add a step.
613
+ const variables = kinds.map((kind) => KEY_KINDS[kind].variable).join(" and ");
577
614
  const origin = parseHueOrigin(parsed.values.origin ?? DEFAULT_ORIGIN);
578
615
  if (!origin)
579
616
  return fail("--origin must be an HTTPS origin such as https://app.hue.run (plain HTTP is accepted for loopback test servers only).", 2);
@@ -588,10 +625,9 @@ export async function runLoginCommand(argv, io = {}) {
588
625
  return fail(error.message);
589
626
  }
590
627
  const settingsUrl = `${origin}${KEY_SETTINGS_PATH}`;
591
- out("Hue keys are created in the app; this command validates and stores them locally.");
592
- out(`Create keys at: ${settingsUrl}`);
593
- for (const kind of kinds)
594
- out(` ${KEY_KINDS[kind].variable}: a "${KEY_KINDS[kind].preset}" key`);
628
+ out("Hue keys are created in the app; this command validates and stores one locally.");
629
+ out(`Create a "${KEY_PRESET}" key at: ${settingsUrl}`);
630
+ out(` It is stored as ${variables}.`);
595
631
  if (!parsed.values["no-browser"] && isTTY(stdout)) {
596
632
  const opened = await openBrowser(settingsUrl).catch(() => false);
597
633
  if (opened)
@@ -603,75 +639,71 @@ export async function runLoginCommand(argv, io = {}) {
603
639
  out(`${variable} is already stored in ${envDisplay}; a different value requires --force.`);
604
640
  }
605
641
  const stored = [];
606
- // A key written before a later prompt fails still has to reach the ignore protection below, so
607
- // the loop records why it stopped instead of returning past it.
642
+ // Validation and the write record why they stopped instead of returning, so a key that did land
643
+ // in the file still reaches the ignore protection below.
608
644
  let failure;
609
645
  const prompter = isTTY(stdin)
610
646
  ? createTerminalPrompter(stdin, stdout)
611
647
  : createLinePrompter(stdin, stdout);
612
- const nothingElse = () => (stored.length ? "No further key was stored." : "Nothing was stored.");
613
648
  try {
649
+ const answer = await prompter.ask(`Paste the "${KEY_PRESET}" key (${variables}): `);
650
+ const value = answer?.trim() ?? "";
651
+ const reason = answer === null ? null : invalidKeyReason(value);
652
+ const updates = {};
614
653
  for (const kind of kinds) {
615
- const { variable, urlVariable, preset } = KEY_KINDS[kind];
616
- const urlValue = kind === "evaluations" ? origin : mcpUrl;
617
- const answer = await prompter.ask(`Paste the "${preset}" key (${variable}): `);
618
- if (answer === null) {
619
- failure = { message: `No ${variable} was entered; input ended.` };
620
- break;
621
- }
622
- const value = answer.trim();
623
- const reason = invalidKeyReason(value);
624
- if (reason) {
625
- failure = {
626
- message: `${reason} Create a "${preset}" key at ${settingsUrl} and paste it.`,
627
- };
628
- break;
629
- }
630
- if (!parsed.values.force) {
631
- for (const [name, next] of [
632
- [variable, value],
633
- [urlVariable, urlValue],
634
- ]) {
635
- const current = readEnvValue(envFile.text, name);
636
- if (current !== undefined && current !== next)
637
- failure ??= {
638
- message: `${name} in ${envDisplay} already has a different value; rerun with --force to replace it.`,
639
- };
640
- }
641
- if (failure)
642
- break;
654
+ const { variable, urlVariable } = KEY_KINDS[kind];
655
+ updates[variable] = value;
656
+ updates[urlVariable] = kind === "evaluations" ? origin : mcpUrl;
657
+ }
658
+ if (answer === null)
659
+ failure = { message: `No key was entered; input ended.` };
660
+ else if (reason)
661
+ failure = {
662
+ message: `${reason} Create a "${KEY_PRESET}" key at ${settingsUrl} and paste it.`,
663
+ };
664
+ else if (!parsed.values.force) {
665
+ for (const [name, next] of Object.entries(updates)) {
666
+ const current = readEnvValue(envFile.text, name);
667
+ if (current !== undefined && current !== next)
668
+ failure ??= {
669
+ message: `${name} in ${envDisplay} already has a different value; rerun with --force to replace it.`,
670
+ };
643
671
  }
672
+ }
673
+ for (const kind of failure ? [] : kinds) {
644
674
  const check = kind === "evaluations"
645
675
  ? await checkEvaluationsKey(fetchImpl, origin, value)
646
676
  : await checkCodingAgentKey(fetchImpl, mcpUrl, value);
647
677
  if (!check.ok) {
648
678
  failure = {
649
679
  message: check.rejected
650
- ? `${check.detail} Create a "${preset}" key at ${settingsUrl} and try again.`
651
- : `${check.detail} ${nothingElse()}`,
680
+ ? `${check.detail} Create a "${KEY_PRESET}" key at ${settingsUrl} and try again. Nothing was stored.`
681
+ : `${check.detail} Nothing was stored.`,
652
682
  };
653
683
  break;
654
684
  }
655
685
  out(kind === "evaluations"
656
- ? `Evaluations key accepted for project "${check.detail}".`
657
- : `Coding-agent key accepted; the Hue MCP server lists ${check.detail} tools.`);
658
- const text = mergeEnvText(envFile.text, { [variable]: value, [urlVariable]: urlValue });
686
+ ? `Evaluations access accepted for project "${check.detail}".`
687
+ : `Coding-agent access accepted; the Hue MCP server lists ${check.detail} tools.`);
688
+ }
689
+ if (!failure) {
690
+ const text = mergeEnvText(envFile.text, updates);
659
691
  try {
660
692
  await writePrivateFile(envPath, text, 0o600);
693
+ envFile = { text, exists: true };
694
+ stored.push(...kinds);
695
+ const urls = kinds.map((kind) => KEY_KINDS[kind].urlVariable).join(" and ");
696
+ out(`Stored the key (${value.length} chars) as ${variables}, with ${urls}, in ${envDisplay}.`);
661
697
  }
662
698
  catch (error) {
663
699
  failure = { message: `Could not write ${envDisplay}: ${error.message}` };
664
- break;
665
700
  }
666
- envFile = { text, exists: true };
667
- stored.push(kind);
668
- out(`Stored ${variable} (${value.length} chars) and ${urlVariable} in ${envDisplay}.`);
669
701
  }
670
702
  }
671
703
  catch (error) {
672
704
  failure =
673
705
  error instanceof PromptInterrupted
674
- ? { message: `Interrupted; ${nothingElse().toLowerCase()}`, code: 130 }
706
+ ? { message: "Interrupted; nothing was stored.", code: 130 }
675
707
  : { message: `hue login failed: ${error.message}` };
676
708
  }
677
709
  finally {
@@ -707,6 +739,6 @@ export async function runLoginCommand(argv, io = {}) {
707
739
  // `hue mcp install` defaults to production; a non-default origin needs its own endpoint.
708
740
  out(` hue mcp install --client claude-code${mcpUrl === mcpUrlForOrigin(DEFAULT_ORIGIN) ? "" : ` --url ${mcpUrl}`}`);
709
741
  if (stored.includes("evaluations"))
710
- out(` hue eval --scenario "<name>" ./hue-agent.ts --env-file ${envDisplay}`);
742
+ out(` hue eval --case "<name>" ./hue-agent.ts --env-file ${envDisplay}`);
711
743
  return 0;
712
744
  }
@@ -1,6 +1,6 @@
1
1
  import type { ProjectConnection } from "../types.js";
2
2
  import { type AttemptConnectionBundleV2, type PrepareAttemptRequestV2 } from "./attempt.js";
3
- import type { ArtifactReservation, ArtifactUpload, CaseConversion, CaseConversionSummary, CaseWrite, CompleteExecution, Completion, Dataset, DatasetCase, DatasetVersion, EvaluationItem, EvaluationRun, EvaluationRunSummary, EnvironmentEvidenceSnapshot, Execution, Experiment, ExperimentCase, ExperimentItem, Identity, JsonValue, LocalAgentClaim, LocalAgentRegistration, RegisteredLocalAgent, JudgeBudget, JudgeJob, Page, PageOptions, RegistryPageOptions, Result, ResultSummary, Scorer, ScorerDefinition, ScorerVersion, SimulationMcpCapability, StartExecution, Subject, StoredResult } from "./types.js";
3
+ import type { ArtifactReservation, ArtifactUpload, CaseConversion, CaseConversionSummary, CaseWrite, CompleteExecution, Completion, Dataset, DatasetCase, DatasetVersion, EvalSet, EvalSetCase, EvalSetVersion, EvaluationItem, EvaluationRun, EvaluationRunSummary, EnvironmentEvidenceSnapshot, Execution, Experiment, ExperimentCase, ExperimentItem, Evaluator, EvaluatorVersion, Identity, JsonValue, LocalAgentClaim, LocalAgentRegistration, RegisteredLocalAgent, JudgeBudget, JudgeJob, Page, PageOptions, RegistryPageOptions, Result, ResultSummary, Run, RunCase, Scorer, ScorerDefinition, ScorerVersion, Scoring, ScoringResultInput, ScoringResultSummary, ScoringSubject, ScoringSummary, SimulationMcpCapability, StartExecution, Subject, StoredResult, StoredScoringResult } from "./types.js";
4
4
  /** Connection options for {@link createEvaluationClient}. */
5
5
  export interface EvaluationClientOptions {
6
6
  /** Project service key sent as a Bearer token; server side only. */
@@ -71,6 +71,39 @@ export declare class EvaluationClient {
71
71
  publishScorerVersion(id: string, definition: ScorerDefinition): Promise<ScorerVersion>;
72
72
  /** Reads a published scorer version. */
73
73
  getScorerVersion(id: string): Promise<ScorerVersion>;
74
+ /** Creates an eval set using the existing v1 registry path. */
75
+ createEvalSet(input: Identity): Promise<EvalSet>;
76
+ /** Reads an eval set and its versions. */
77
+ getEvalSet(id: string): Promise<EvalSet>;
78
+ /** Lists eval sets. */
79
+ listEvalSets(page?: RegistryPageOptions): Promise<Page<Omit<EvalSet, "versions">>>;
80
+ /** Creates a draft eval set version, optionally copying cases from another version. */
81
+ createEvalSetVersion(id: string, input?: {
82
+ fromVersionId?: string;
83
+ }): Promise<EvalSetVersion>;
84
+ /** Reads an eval set version. */
85
+ getEvalSetVersion(id: string): Promise<EvalSetVersion>;
86
+ /** Lists cases in an eval set version. */
87
+ listEvalSetCases(id: string, page?: PageOptions): Promise<Page<EvalSetCase>>;
88
+ /** Adds a case to a draft eval set version at its expected revision. */
89
+ addEvalSetCase(id: string, input: CaseWrite): Promise<{
90
+ /** The stored case. */
91
+ item: EvalSetCase;
92
+ /** The version with its new revision. */
93
+ version: EvalSetVersion;
94
+ }>;
95
+ /** Freezes a draft eval set version at its expected revision. */
96
+ freezeEvalSetVersion(id: string, expectedRevision: number): Promise<EvalSetVersion>;
97
+ /** Creates an evaluator identity. */
98
+ createEvaluator(input: Identity): Promise<Evaluator>;
99
+ /** Reads an evaluator and its published versions. */
100
+ getEvaluator(id: string): Promise<Evaluator>;
101
+ /** Lists evaluators. */
102
+ listEvaluators(page?: RegistryPageOptions): Promise<Page<Evaluator>>;
103
+ /** Publishes an immutable evaluator version. */
104
+ publishEvaluatorVersion(id: string, definition: ScorerDefinition): Promise<EvaluatorVersion>;
105
+ /** Reads a published evaluator version. */
106
+ getEvaluatorVersion(id: string): Promise<EvaluatorVersion>;
74
107
  /** Creates an experiment over a frozen dataset version with pinned scorer versions and a configuration. */
75
108
  createExperiment(input: {
76
109
  idempotencyKey: string;
@@ -175,6 +208,70 @@ export declare class EvaluationClient {
175
208
  listResults(id: string, page?: PageOptions): Promise<Page<ResultSummary>>;
176
209
  /** Reads a full stored result. */
177
210
  getResult(id: string): Promise<StoredResult>;
211
+ /** Creates a run using product request fields on the existing v1 path. */
212
+ createRun(input: {
213
+ idempotencyKey: string;
214
+ name: string;
215
+ evalSetVersionId: string;
216
+ evaluatorVersionIds: string[];
217
+ config: JsonValue;
218
+ }): Promise<{
219
+ /** Run ID. */
220
+ id: string;
221
+ /** ID of the run's scoring pass. */
222
+ scoringId: string;
223
+ /** Existing v1 field for the scoring pass ID. */
224
+ evaluationRunId: string;
225
+ }>;
226
+ /** Reads a run with its scoring progress. */
227
+ getRun(id: string): Promise<Run>;
228
+ /** Lists a run's cases with their latest executions. */
229
+ listRunItems(id: string, page?: PageOptions): Promise<Page<ExperimentItem>>;
230
+ /** Reads one frozen case of a run. */
231
+ getRunCase(id: string, caseId: string): Promise<RunCase>;
232
+ /** Starts or replays a target execution for a run case. */
233
+ startRunExecution(id: string, caseId: string, input: StartExecution): Promise<Execution>;
234
+ /** Reads one run execution. */
235
+ getRunExecution(id: string): Promise<Execution>;
236
+ /** Saves a run execution's outcome. */
237
+ completeRunExecution(id: string, input: CompleteExecution): Promise<Completion>;
238
+ /** Marks a run finished. */
239
+ finishRun(id: string, idempotencyKey: string): Promise<{
240
+ /** Run ID. */
241
+ id: string;
242
+ /** When the run finished. */
243
+ finishedAt: string;
244
+ }>;
245
+ /** Creates a standalone scoring pass over saved subjects. */
246
+ createScoring(input: {
247
+ idempotencyKey: string;
248
+ name: string;
249
+ subjectIds: string[];
250
+ evaluatorVersionIds: string[];
251
+ }): Promise<{
252
+ /** Scoring pass ID. */
253
+ id: string;
254
+ }>;
255
+ /** Reads a scoring pass and its pinned evaluators. */
256
+ getScoring(id: string): Promise<Scoring>;
257
+ /** Lists scoring passes in the project. */
258
+ listScorings(page?: PageOptions): Promise<Page<ScoringSummary>>;
259
+ /** Lists the subjects of a scoring pass. */
260
+ listScoringItems(id: string, page?: PageOptions): Promise<Page<EvaluationItem>>;
261
+ /** Reads a saved subject with product-named source fields. */
262
+ getScoringSubject(id: string): Promise<ScoringSubject>;
263
+ /** Uploads evaluator results for a scoring pass using product request fields. */
264
+ submitScoringResults(id: string, input: {
265
+ idempotencyKey: string;
266
+ results: ScoringResultInput[];
267
+ }): Promise<{
268
+ /** Stored result IDs, in input order. */
269
+ ids: string[];
270
+ }>;
271
+ /** Lists result summaries for a scoring pass. */
272
+ listScoringResults(id: string, page?: PageOptions): Promise<Page<ScoringResultSummary>>;
273
+ /** Reads a stored evaluator result. */
274
+ getScoringResult(id: string): Promise<StoredScoringResult>;
178
275
  /** Dispatches hosted judge jobs for `llm_judge` pins; check {@link getJudgeBudget} first. */
179
276
  createJudgeJobs(id: string, input: {
180
277
  idempotencyKey: string;
@@ -230,7 +327,7 @@ export declare class EvaluationClient {
230
327
  /** Acknowledged terminal queue state. */
231
328
  state: "completed" | "attention";
232
329
  }>;
233
- /** Lists Scenarios (draft and published) of the project; requires a Tracing and evaluations key. */
330
+ /** Lists Scenarios (draft and published) of the project; requires a Read and write key. */
234
331
  listCaseConversions(page?: PageOptions): Promise<Page<CaseConversionSummary>>;
235
332
  /** Reads one Scenario with its immutable publication pins. */
236
333
  getCaseConversion(id: string): Promise<CaseConversion>;
@@ -12,6 +12,85 @@ export class HueApiError extends Error {
12
12
  this.name = "HueApiError";
13
13
  }
14
14
  }
15
+ const registryFieldAliases = [
16
+ ["datasetId", "evalSetId"],
17
+ ["datasetVersionId", "evalSetVersionId"],
18
+ ["scorerId", "evaluatorId"],
19
+ ["scorerVersionId", "evaluatorVersionId"],
20
+ ];
21
+ const registryEnvelopes = new Set(["items", "item", "versions", "version"]);
22
+ // Only Hue response envelopes are traversed. Case inputs, metadata, and evaluator
23
+ // definitions are customer JSON and must retain their original field names.
24
+ function productRegistryFields(value) {
25
+ if (Array.isArray(value))
26
+ return value.map((item) => productRegistryFields(item));
27
+ if (value === null || typeof value !== "object")
28
+ return value;
29
+ const result = { ...value };
30
+ for (const key of registryEnvelopes) {
31
+ if (Object.hasOwn(result, key))
32
+ result[key] = productRegistryFields(result[key]);
33
+ }
34
+ for (const [legacy, product] of registryFieldAliases) {
35
+ if (Object.hasOwn(result, legacy)) {
36
+ if (Object.hasOwn(result, product) && result[legacy] !== result[product])
37
+ throw new HueApiError();
38
+ result[product] = result[legacy];
39
+ }
40
+ }
41
+ return result;
42
+ }
43
+ const runResponseAliases = [
44
+ ["datasetId", "evalSetId"],
45
+ ["datasetName", "evalSetName"],
46
+ ["datasetDisplayName", "evalSetDisplayName"],
47
+ ["datasetVersion", "evalSetVersion"],
48
+ ["datasetVersionId", "evalSetVersionId"],
49
+ ["datasetVersionIds", "evalSetVersionIds"],
50
+ ["scorerId", "evaluatorId"],
51
+ ["scorerName", "evaluatorName"],
52
+ ["scorerVersion", "evaluatorVersion"],
53
+ ["scorerVersionId", "evaluatorVersionId"],
54
+ ["scorerVersionIds", "evaluatorVersionIds"],
55
+ ["scorerVersions", "evaluatorVersions"],
56
+ ["evaluationRunId", "scoringId"],
57
+ ];
58
+ const runResponseEnvelopes = new Set([
59
+ "items",
60
+ "item",
61
+ "versions",
62
+ "version",
63
+ "scorerVersions",
64
+ "evaluatorVersions",
65
+ ]);
66
+ function productRunFields(value, kind) {
67
+ if (Array.isArray(value))
68
+ return value.map((item) => productRunFields(item, kind));
69
+ if (value === null || typeof value !== "object")
70
+ return value;
71
+ const result = { ...value };
72
+ for (const key of runResponseEnvelopes) {
73
+ if (Object.hasOwn(result, key))
74
+ result[key] = productRunFields(result[key], kind);
75
+ }
76
+ for (const key of ["evaluation", "scoring"]) {
77
+ if (Object.hasOwn(result, key))
78
+ result[key] = productRunFields(result[key], "scoring");
79
+ }
80
+ const identityAlias = kind === "result" ? ["runId", "scoringId"] : ["experimentId", "runId"];
81
+ const aliases = [...runResponseAliases, identityAlias];
82
+ for (const [legacy, product] of aliases) {
83
+ if (Object.hasOwn(result, legacy)) {
84
+ if (Object.hasOwn(result, product) &&
85
+ JSON.stringify(result[legacy]) !== JSON.stringify(result[product]))
86
+ throw new HueApiError();
87
+ result[product] = result[legacy];
88
+ }
89
+ }
90
+ if (Object.hasOwn(result, "evaluation") && !Object.hasOwn(result, "scoring"))
91
+ result.scoring = result.evaluation;
92
+ return result;
93
+ }
15
94
  /**
16
95
  * Typed client for Hue's evaluation REST API: datasets, scorers, experiments, executions, runs,
17
96
  * results and hosted judge jobs. No implicit mutation retry: callers retain stable idempotency keys
@@ -225,6 +304,58 @@ export class EvaluationClient {
225
304
  getScorerVersion(id) {
226
305
  return this.request("GET", `/scorer-versions/${uuid(id)}`);
227
306
  }
307
+ /** Creates an eval set using the existing v1 registry path. */
308
+ async createEvalSet(input) {
309
+ return productRegistryFields(await this.createDataset(input));
310
+ }
311
+ /** Reads an eval set and its versions. */
312
+ async getEvalSet(id) {
313
+ return productRegistryFields(await this.getDataset(id));
314
+ }
315
+ /** Lists eval sets. */
316
+ async listEvalSets(page) {
317
+ return productRegistryFields(await this.listDatasets(page));
318
+ }
319
+ /** Creates a draft eval set version, optionally copying cases from another version. */
320
+ async createEvalSetVersion(id, input = {}) {
321
+ return productRegistryFields(await this.createDatasetVersion(id, input));
322
+ }
323
+ /** Reads an eval set version. */
324
+ async getEvalSetVersion(id) {
325
+ return productRegistryFields(await this.getDatasetVersion(id));
326
+ }
327
+ /** Lists cases in an eval set version. */
328
+ async listEvalSetCases(id, page) {
329
+ return productRegistryFields(await this.listCases(id, page));
330
+ }
331
+ /** Adds a case to a draft eval set version at its expected revision. */
332
+ async addEvalSetCase(id, input) {
333
+ return productRegistryFields(await this.addCase(id, input));
334
+ }
335
+ /** Freezes a draft eval set version at its expected revision. */
336
+ async freezeEvalSetVersion(id, expectedRevision) {
337
+ return productRegistryFields(await this.freezeDatasetVersion(id, expectedRevision));
338
+ }
339
+ /** Creates an evaluator identity. */
340
+ async createEvaluator(input) {
341
+ return productRegistryFields(await this.createScorer(input));
342
+ }
343
+ /** Reads an evaluator and its published versions. */
344
+ async getEvaluator(id) {
345
+ return productRegistryFields(await this.getScorer(id));
346
+ }
347
+ /** Lists evaluators. */
348
+ async listEvaluators(page) {
349
+ return productRegistryFields(await this.listScorers(page));
350
+ }
351
+ /** Publishes an immutable evaluator version. */
352
+ async publishEvaluatorVersion(id, definition) {
353
+ return productRegistryFields(await this.publishScorerVersion(id, definition));
354
+ }
355
+ /** Reads a published evaluator version. */
356
+ async getEvaluatorVersion(id) {
357
+ return productRegistryFields(await this.getScorerVersion(id));
358
+ }
228
359
  /** Creates an experiment over a frozen dataset version with pinned scorer versions and a configuration. */
229
360
  createExperiment(input) {
230
361
  return this.request("POST", "/experiments", input);
@@ -376,6 +507,70 @@ export class EvaluationClient {
376
507
  getResult(id) {
377
508
  return this.request("GET", `/evaluation-results/${uuid(id)}`);
378
509
  }
510
+ /** Creates a run using product request fields on the existing v1 path. */
511
+ async createRun(input) {
512
+ return productRunFields(await this.request("POST", "/experiments", input), "run");
513
+ }
514
+ /** Reads a run with its scoring progress. */
515
+ async getRun(id) {
516
+ return productRunFields(await this.getExperiment(id), "run");
517
+ }
518
+ /** Lists a run's cases with their latest executions. */
519
+ async listRunItems(id, page) {
520
+ return productRunFields(await this.listExperimentItems(id, page), "run");
521
+ }
522
+ /** Reads one frozen case of a run. */
523
+ async getRunCase(id, caseId) {
524
+ return productRunFields(await this.getExperimentCase(id, caseId), "run");
525
+ }
526
+ /** Starts or replays a target execution for a run case. */
527
+ async startRunExecution(id, caseId, input) {
528
+ return productRunFields(await this.startExecution(id, caseId, input), "run");
529
+ }
530
+ /** Reads one run execution. */
531
+ async getRunExecution(id) {
532
+ return productRunFields(await this.getExecution(id), "run");
533
+ }
534
+ /** Saves a run execution's outcome. */
535
+ async completeRunExecution(id, input) {
536
+ return productRunFields(await this.completeExecution(id, input), "run");
537
+ }
538
+ /** Marks a run finished. */
539
+ async finishRun(id, idempotencyKey) {
540
+ return productRunFields(await this.finishExperiment(id, idempotencyKey), "run");
541
+ }
542
+ /** Creates a standalone scoring pass over saved subjects. */
543
+ async createScoring(input) {
544
+ return productRunFields(await this.request("POST", "/evaluation-runs", input), "scoring");
545
+ }
546
+ /** Reads a scoring pass and its pinned evaluators. */
547
+ async getScoring(id) {
548
+ return productRunFields(await this.getEvaluationRun(id), "scoring");
549
+ }
550
+ /** Lists scoring passes in the project. */
551
+ async listScorings(page) {
552
+ return productRunFields(await this.listEvaluationRuns(page), "scoring");
553
+ }
554
+ /** Lists the subjects of a scoring pass. */
555
+ async listScoringItems(id, page) {
556
+ return productRunFields(await this.listEvaluationItems(id, page), "scoring");
557
+ }
558
+ /** Reads a saved subject with product-named source fields. */
559
+ async getScoringSubject(id) {
560
+ return productRunFields(await this.getSubject(id), "run");
561
+ }
562
+ /** Uploads evaluator results for a scoring pass using product request fields. */
563
+ async submitScoringResults(id, input) {
564
+ return productRunFields(await this.request("POST", `/evaluation-runs/${uuid(id)}/results`, input), "result");
565
+ }
566
+ /** Lists result summaries for a scoring pass. */
567
+ async listScoringResults(id, page) {
568
+ return productRunFields(await this.listResults(id, page), "result");
569
+ }
570
+ /** Reads a stored evaluator result. */
571
+ async getScoringResult(id) {
572
+ return productRunFields(await this.getResult(id), "result");
573
+ }
379
574
  /** Dispatches hosted judge jobs for `llm_judge` pins; check {@link getJudgeBudget} first. */
380
575
  createJudgeJobs(id, input) {
381
576
  return this.request("POST", `/evaluation-runs/${uuid(id)}/judge-jobs`, input);
@@ -412,7 +607,7 @@ export class EvaluationClient {
412
607
  completeLocalAgentRun(input) {
413
608
  return this.request("POST", "/local-agent-worker/runs/complete", input);
414
609
  }
415
- /** Lists Scenarios (draft and published) of the project; requires a Tracing and evaluations key. */
610
+ /** Lists Scenarios (draft and published) of the project; requires a Read and write key. */
416
611
  listCaseConversions(page) {
417
612
  return this.request("GET", `/case-conversions${this.page(page)}`);
418
613
  }
@@ -21,9 +21,9 @@ export interface ScenarioPins {
21
21
  }
22
22
  /** Subset of {@link EvaluationClient} used to resolve Scenario pins. */
23
23
  export type ScenarioClient = Pick<EvaluationClient, "listCaseConversions" | "getCaseConversion" | "getDataset" | "getDatasetVersion" | "listDatasets">;
24
- /** Lists Scenarios of the project; requires a Tracing and evaluations key. */
24
+ /** Lists Scenarios of the project; requires a Read and write key. */
25
25
  export declare function listScenarios(client: Pick<EvaluationClient, "listCaseConversions">, page?: PageOptions): Promise<Page<CaseConversionSummary>>;
26
- /** Reads one Scenario with its publication pins; requires a Tracing and evaluations key. */
26
+ /** Reads one Scenario with its publication pins; requires a Read and write key. */
27
27
  export declare function getScenario(client: Pick<EvaluationClient, "getCaseConversion">, id: string): Promise<CaseConversion>;
28
28
  /** How a selector was interpreted: a UUID, a Hue URL or a display name. */
29
29
  export type ScenarioSelector = {
@@ -1,11 +1,11 @@
1
1
  const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2
2
  /** Scenarios listed while resolving a name; bounds the registry reads of one selection. */
3
3
  const MAX_LISTED = 200;
4
- /** Lists Scenarios of the project; requires a Tracing and evaluations key. */
4
+ /** Lists Scenarios of the project; requires a Read and write key. */
5
5
  export function listScenarios(client, page) {
6
6
  return client.listCaseConversions(page);
7
7
  }
8
- /** Reads one Scenario with its publication pins; requires a Tracing and evaluations key. */
8
+ /** Reads one Scenario with its publication pins; requires a Read and write key. */
9
9
  export function getScenario(client, id) {
10
10
  return client.getCaseConversion(id);
11
11
  }
@@ -76,6 +76,21 @@ export interface DatasetCase {
76
76
  /** Immutable input-file manifest identity, when files are attached. */
77
77
  artifactManifestId?: string | null;
78
78
  }
79
+ /** An eval set and its versions. Both field names remain available during v1 compatibility. */
80
+ export type EvalSet = Omit<Dataset, "versions"> & {
81
+ /** Versions of this eval set. */
82
+ versions: EvalSetVersion[];
83
+ };
84
+ /** A version of an eval set, with its product field name. */
85
+ export type EvalSetVersion = DatasetVersion & {
86
+ /** Owning eval set ID. */
87
+ evalSetId: string;
88
+ };
89
+ /** A stored case in an eval set version. */
90
+ export type EvalSetCase = DatasetCase & {
91
+ /** Eval set version containing this case. */
92
+ evalSetVersionId: string;
93
+ };
79
94
  /** One pinned input file of a case or subject, as recorded in Hue's immutable manifest. */
80
95
  export interface CaseFile {
81
96
  /** Hue artifact identity of the pinned bytes. */
@@ -346,6 +361,16 @@ export interface ScorerVersion {
346
361
  /** The pinned definition. */
347
362
  definition: ScorerDefinition;
348
363
  }
364
+ /** An evaluator and its published versions. */
365
+ export type Evaluator = Omit<Scorer, "versions"> & {
366
+ /** Published versions, when included in the response. */
367
+ versions?: EvaluatorVersion[];
368
+ };
369
+ /** An immutable published evaluator definition. */
370
+ export type EvaluatorVersion = ScorerVersion & {
371
+ /** Owning evaluator ID when the server supplies it; older v1 responses may omit it. */
372
+ evaluatorId?: string;
373
+ };
349
374
  /** Final state of a target execution. */
350
375
  export type TerminalState = "succeeded" | "error" | "cancelled";
351
376
  /** One attempt to run the target for a case. */
@@ -439,6 +464,30 @@ export interface Experiment {
439
464
  cancelled: number;
440
465
  };
441
466
  }
467
+ /** A run over a frozen eval set version and configuration. */
468
+ export type Run = Experiment & {
469
+ /** Frozen eval set version under test. */
470
+ evalSetVersionId: string;
471
+ /** Scoring pass created for this run. */
472
+ scoring: Scoring;
473
+ };
474
+ /** One frozen case in a run. */
475
+ export type RunCase = ExperimentCase & {
476
+ /** Source eval set version ID. */
477
+ evalSetVersionId: string;
478
+ };
479
+ /** A scoring pass over saved subjects with pinned evaluators. */
480
+ export type Scoring = EvaluationRun & {
481
+ /** Evaluator versions pinned to this scoring pass. */
482
+ evaluatorVersions: EvaluatorVersion[];
483
+ /** Linked run ID, or null for standalone scoring. */
484
+ runId?: string | null;
485
+ };
486
+ /** One row in the project's scoring list. */
487
+ export type ScoringSummary = EvaluationRunSummary & {
488
+ /** Linked run ID, or null for standalone scoring. */
489
+ runId: string | null;
490
+ };
442
491
  /** A sanitized error type with an optional bounded message. */
443
492
  export interface TypedError {
444
493
  /** Stable error type. */
@@ -549,6 +598,13 @@ export interface Subject {
549
598
  /** The target's declared primary generated artifact, or `null`. */
550
599
  primaryArtifactId?: string | null;
551
600
  }
601
+ /** An immutable saved subject with product-named source fields. */
602
+ export type ScoringSubject = Subject & {
603
+ /** Source eval set version ID. */
604
+ evalSetVersionId: string;
605
+ /** Source run ID. */
606
+ runId: string;
607
+ };
552
608
  /** A reported metric value. */
553
609
  export interface Metric {
554
610
  /** Declared metric name. */
@@ -588,6 +644,27 @@ export type Result = Score & {
588
644
  /** Source digest of the local scorer, for `local_code` pins. */
589
645
  sourceDigest?: string;
590
646
  };
647
+ /** A score uploaded through the product-named scoring method. */
648
+ export type ScoringResultInput = Score & {
649
+ /** Scoring item this result belongs to. */
650
+ evaluationItemId: string;
651
+ /** Evaluator version that produced the score. */
652
+ evaluatorVersionId: string;
653
+ /** Source digest for a local code evaluator. */
654
+ sourceDigest?: string;
655
+ };
656
+ /** A result as listed by {@link EvaluationClient.listScoringResults}. */
657
+ export type ScoringResultSummary = ResultSummary & {
658
+ /** Evaluator version that produced this result. */
659
+ evaluatorVersionId: string;
660
+ };
661
+ /** A full stored result from {@link EvaluationClient.getScoringResult}. */
662
+ export type StoredScoringResult = StoredResult & {
663
+ /** Scoring pass containing this result. */
664
+ scoringId: string;
665
+ /** Evaluator version that produced this result. */
666
+ evaluatorVersionId: string;
667
+ };
591
668
  /** What a local scorer callback receives. */
592
669
  export interface ScoreContext {
593
670
  /** Case inputs. */
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Package version shared by the instrumentation scope and the export User-Agent. */
2
- export declare const sdkVersion = "0.5.0";
2
+ export declare const sdkVersion = "0.6.0";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Generated by scripts/write-version.mjs from package.json; do not edit by hand.
2
2
  /** Package version shared by the instrumentation scope and the export User-Agent. */
3
- export const sdkVersion = "0.5.0";
3
+ export const sdkVersion = "0.6.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hue-run/sdk",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "publishConfig": {