@hue-run/sdk 0.13.0 → 0.13.1

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.13.0`, `@opentelemetry/api@1.9.1` and
45
+ not a completed integration. TypeScript uses `@hue-run/sdk@0.13.1`, `@opentelemetry/api@1.9.1` and
46
46
  `@opentelemetry/context-async-hooks@2.11.0`; Python setup uses its separately tested package pin.
47
47
  Content capture requires an ordinary account-managed key and a later explicit application decision.
48
48
 
@@ -469,7 +469,7 @@ node packages/sdk-typescript/scripts/verify-package.mjs --artifacts-dir .artifac
469
469
  # Set project to an existing supported fixture; use the same directory on resume.
470
470
  project=/absolute/path/to/supported-fixture
471
471
  node packages/sdk-typescript/scripts/verify-setup-live.mjs \
472
- --archive .artifacts/typescript/hue-run-sdk-0.13.0.tgz \
472
+ --archive .artifacts/typescript/hue-run-sdk-0.13.1.tgz \
473
473
  --origin https://STAGING_ORIGIN \
474
474
  --project "$project" --command setup \
475
475
  --evidence .context/setup-staging-before-claim.json
@@ -480,7 +480,7 @@ the private local handoff and finish the real browser claim, then reconcile the
480
480
 
481
481
  ```sh
482
482
  node packages/sdk-typescript/scripts/verify-setup-live.mjs \
483
- --archive .artifacts/typescript/hue-run-sdk-0.13.0.tgz \
483
+ --archive .artifacts/typescript/hue-run-sdk-0.13.1.tgz \
484
484
  --origin https://STAGING_ORIGIN \
485
485
  --project "$project" --command claim \
486
486
  --evidence .context/setup-staging-after-claim.json
@@ -512,12 +512,38 @@ the sealed world. Hue never executes the agent. `HUE_API_KEY` must be a **Read a
512
512
  write** project key (a **Read** or **Tracing only** key cannot read cases or create runs);
513
513
  the CLI never prints it. The optional `zod` peer of `@hue-run/sdk/evals` must be installed.
514
514
 
515
- Write an adapter module that hands the case inputs and the world's tools or MCP connection to
516
- the real agent. The module exports `default` or `runMyAgent`; `context` is the SDK's
517
- `SimulationTargetContext` (`world`, `mcp`, `tools`, `config`, `item`, `executionId`,
518
- `environmentRunId`, `files`, `outputDirectory`, `signal`; `world` carries the provider mirror URLs,
519
- the world token, `env` and `mcpConfig` where Hue's simulation gateway serves the world). When the
520
- case also pins input files, `files` holds verified copies of its agent-visible ones (see
515
+ Run the agent's own start command with `--command`. `hue eval` spawns it once per case with the
516
+ world's environment (below) and `{"inputs", "config"}` on stdin, and stores its stdout as the
517
+ answer. The agent reaches each app through Hue's mirror with its own production client: the one
518
+ committed helper described in [Make your agent eval-ready](https://docs.hue.run/evaluations/eval-ready-agent)
519
+ reads `HUE_SIM_<SURFACE ID>_URL` and `HUE_WORLD_TOKEN` when `HUE_WORLD_TOKEN` is set, fails
520
+ closed when a needed variable is missing, and returns the production URL and credential
521
+ otherwise. Never hand the agent Hue-native tools (`context.tools`, `context.mcp`) or a tool its
522
+ production agent lacks. Pin the SDK and bring its `zod` peer along, so the agent's repository
523
+ gains no dependency:
524
+
525
+ ```sh
526
+ npx --yes --package @hue-run/sdk@0.13.1 --package "zod@^4.6.5" hue eval --case "Refund an eligible charge" --command "node agent.js" --env-file .env.hue
527
+ npx --yes --package @hue-run/sdk@0.13.1 --package "zod@^4.6.5" hue eval --case https://app.hue.run/projects/demo/scenarios/<id> --command "python agent.py" --timeout 120 --baseline <experiment id>
528
+ npx --yes --package @hue-run/sdk@0.13.1 --package "zod@^4.6.5" hue eval --set "Billing regressions" --scorer-version <id> --command "node agent.js" --save-version
529
+ npx --yes --package @hue-run/sdk@0.13.1 --package "zod@^4.6.5" hue eval --worker --command "node agent.js" --agent-key support-agent --env-file .env.hue
530
+ ```
531
+
532
+ Without the second `--package`, `hue eval` exits with `hue eval needs zod`, even for `--help`;
533
+ installing zod in the project does not help when the SDK itself is not installed there. An
534
+ unpinned `--package @hue-run/sdk` runs a local, possibly older, copy in a project that already
535
+ depends on it.
536
+
537
+ An adapter module is the in-process alternative for a TypeScript agent. It exports `default` or
538
+ `runMyAgent`; `context` is the SDK's `SimulationTargetContext` (`world`, `mcp`, `tools`, `config`,
539
+ `item`, `executionId`, `environmentRunId`, `files`, `outputDirectory`, `signal`; `world` carries
540
+ the provider mirror URLs, the world token, `env` and `mcpConfig` where Hue's simulation gateway
541
+ serves the world). An adapter runs inside `hue eval`'s own process, where `HUE_WORLD_TOKEN` and
542
+ `HUE_EXECUTION_ID` are not set, so a helper that reads the process environment would return
543
+ production values: throw when `context.world` is missing, then pass `context.world.env` as the
544
+ helper's environment argument or spawn the agent with `agentEnvironment(context.world)`. Never
545
+ set `process.env` yourself; with `--concurrency` above 1 that leaks one case's world into another.
546
+ When the case also pins input files, `files` holds verified copies of its agent-visible ones (see
521
547
  [Evaluate a document eval set](#evaluate-a-document-eval-set)); return
522
548
  `withFiles(output, files)` from `@hue-run/sdk/evals` to upload documents the agent produced:
523
549
 
@@ -527,19 +553,16 @@ import type { JsonValue, SimulationTargetContext } from "@hue-run/sdk/evals";
527
553
  import { runAgent } from "./src/agent.js"; // The application's existing entry point.
528
554
 
529
555
  export default function runMyAgent(inputs: JsonValue, context: SimulationTargetContext) {
530
- // Hand context.tools or context.mcp to the agent's real tool boundary; configuration alone
531
- // does not redirect provider calls. Pass context.signal for cooperative cancellation.
532
- const { config, tools, mcp, signal } = context;
533
- return runAgent({ inputs, config, tools, mcp, signal });
556
+ if (!context.world) throw new Error("This case has no world handoff; refusing to run against the real app");
557
+ // The agent's own app clients read the mirrors through the committed helper, given the
558
+ // world's environment; never hand it context.tools or context.mcp. Pass context.signal for
559
+ // cooperative cancellation.
560
+ return runAgent({ inputs, config: context.config, env: context.world.env, signal: context.signal });
534
561
  }
535
562
  ```
536
563
 
537
564
  ```sh
538
- hue eval --case "Refund an eligible charge" ./hue-agent.ts --content --env-file .env.hue
539
- hue eval --case https://app.hue.run/projects/demo/scenarios/<id> ./hue-agent.ts --content --baseline <run id>
540
- hue eval --set "Billing regressions" --scorer-version <id> ./hue-agent.ts --content --save-version
541
- hue eval --case "Refund an eligible charge" --command "python agent.py" --timeout 120 --content
542
- hue eval --worker ./hue-agent.ts --agent-key support-agent --content --env-file .env.hue
565
+ npx --yes --package @hue-run/sdk@0.13.1 --package "zod@^4.6.5" hue eval --case "Refund an eligible charge" ./hue-agent.ts --content --env-file .env.hue
543
566
  ```
544
567
 
545
568
  `--scenario` remains an alias for `--case` for existing scripts. Pass one selection flag.
@@ -576,8 +599,8 @@ Hue-owned `world_outcome` checks are graded after the world seals. An experiment
576
599
  every case of the saved version; there is no case subset.
577
600
 
578
601
  `--command "<shell command>"` spawns the command once per case with the world's environment
579
- (`HUE_WORLD_ID`, `HUE_WORLD_TOKEN`, `HUE_WORLD_NOW` (the world's clock at creation, RFC 3339: read
580
- today's date from it, never from the wall clock), one `HUE_SIM_<SURFACE ID>_URL` per provider
602
+ (`HUE_WORLD_ID`, `HUE_WORLD_TOKEN`, `HUE_WORLD_NOW` (the world's clock at creation, RFC 3339; `worldNow()` reads it and returns
603
+ `null` without it, so fall back explicitly with `worldNow() ?? new Date()`), one `HUE_SIM_<SURFACE ID>_URL` per provider
581
604
  mirror,
582
605
  `HUE_MCP_CONFIG` naming an owner-only `mcpServers` file that is removed after the case, and
583
606
  `HUE_MCP_URL`, `HUE_MCP_TOKEN`, `HUE_MCP_EXPIRES_AT` for the first MCP mirror; these name the
package/ENVIRONMENTS.md CHANGED
@@ -328,8 +328,11 @@ type; see [EVALUATIONS.md](EVALUATIONS.md#direct-cases-and-files).
328
328
  A refusal such as 409 `simulation_gateway_required` exposes its validated server code as `HueEnvironmentError.diagnostic`.
329
329
 
330
330
  `agentEnvironment` removes Hue control-plane credentials from the child by default: `HUE_API_KEY`,
331
- `HUE_MCP_KEY` and any variable whose value is a `hue_sk_`, `hue_mcp_` or `hue_attempt_`
332
- credential. Pass `includeHueCredentials: true` only for an agent that must call Hue's own API.
331
+ `HUE_MCP_KEY`, and any value carrying a Hue control-plane credential (`hue_sk_`, `hue_setup_`,
332
+ `hue_attempt_`, `hue_mcp_`, `hue_inv_`, `hue_install_`, `hue_at_`, `hue_rt_`, `hue_oauth_`)
333
+ anywhere in it, including wrapped, percent-encoded or JSON-Unicode-escaped values. World tokens and
334
+ `hue_sim_` capabilities pass. Pass `includeHueCredentials: true` only for an agent that must call
335
+ Hue's own API.
333
336
  Nothing in these helpers logs the token; keep it out of your own logs and checkpoints.
334
337
 
335
338
  Finish answers `lifecycle: "completing"` with `sealedAt: null` for a gateway world: the seal
package/EVALUATIONS.md CHANGED
@@ -107,6 +107,24 @@ evidence and scoring while retaining this runner's checkpoint guarantees. See
107
107
  Cases whose inputs are a task plus pinned documents, and rescoring over documents a run already
108
108
  saved, use the same runner without a world. See [Direct cases and files](#direct-cases-and-files).
109
109
 
110
+ ## Tags
111
+
112
+ Tags group datasets, scorers and experiments, for example by the area of the agent they test.
113
+ Methods take tag names: a name the project has no tag for creates one, and names match without
114
+ case. `createDataset`, `createScorer` and `createExperiment` accept `tags`; `updateDataset`,
115
+ `updateScorer` and `updateExperiment` replace an item's tags; `listDatasets` and `listScorers`
116
+ filter by `tags`, matching any of them; and `listTags()` returns the project's tags. An experiment
117
+ also shows its dataset's tags, which experiment filters match as well. The product-named methods
118
+ (`createEvalSet`, `updateEvalSet`, `listEvalSets`, `createEvaluator`, `updateEvaluator`,
119
+ `listEvaluators`, `createRun`, `updateRun`) take the same `tags`. Tags require a Hue server that
120
+ supports them.
121
+
122
+ ```ts
123
+ await client.createEvalSet({ name: "Refunds", slug: "refunds", tags: ["billing"] });
124
+ await client.updateEvaluator(evaluatorId, { tags: ["billing", "regression"] });
125
+ const billing = await client.listEvalSets({ tags: ["billing"] });
126
+ ```
127
+
110
128
  ## Content and result states
111
129
 
112
130
  Both choices are required and independent:
@@ -146,7 +164,7 @@ A callback receives `{inputs,hasOutput,output?,hasExpected,expected?,metadata,ex
146
164
  - `{state:"error",error:{type,message?}}`.
147
165
  - `{state:"skipped",explanation:"reason"}`.
148
166
 
149
- All declared metrics must appear exactly once and satisfy pinned types, bounds and categories. The binding must match language, entrypoint, SHA-256 source digest and metric definitions. The digest is an authenticated caller declaration; it does not attest closures, dependency versions or actual execution. Callback source is never downloaded or evaluated. Callbacks are trusted local code; they have **no execution timeout or side-effect cancellation**. Concurrency limits active cases to 1–16 (default 1), with scorers evaluated sequentially within each case.
167
+ All declared metrics must appear exactly once and satisfy pinned types, bounds and categories. Each metric may include an optional `description` (1–500 characters) shown with its check in Hue's run view. The binding must match language, entrypoint, SHA-256 source digest and metric definitions. The digest is an authenticated caller declaration; it does not attest closures, dependency versions or actual execution. Callback source is never downloaded or evaluated. Callbacks are trusted local code; they have **no execution timeout or side-effect cancellation**. Concurrency limits active cases to 1–16 (default 1), with scorers evaluated sequentially within each case.
150
168
 
151
169
  `files` is present only when the runner handled files for that execution: every pinned input file
152
170
  (all roles, including the evaluator-only `org_template`) and every generated output, each with its
@@ -15,7 +15,12 @@ export declare function worldHandoff(run: EnvironmentRun): WorldHandoff | null;
15
15
  export declare function worldNow(source?: WorldHandoff | Record<string, string | undefined>): Date | null;
16
16
  /** Variables that authenticate against Hue's control plane rather than a simulated provider. */
17
17
  export declare const HUE_CONTROL_PLANE_VARIABLES: readonly string[];
18
- /** True for a control-plane variable by name, or for any variable holding a Hue credential. */
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. */
19
24
  export declare function isHueControlPlaneCredential(name: string, value: string | undefined): boolean;
20
25
  /** The parent's variables without Hue control-plane credentials; `undefined` values are dropped. */
21
26
  export declare function stripHueControlPlaneCredentials(parent: Record<string, string | undefined>): Record<string, string>;
@@ -52,13 +52,53 @@ export const HUE_CONTROL_PLANE_VARIABLES = [
52
52
  "HUE_PROJECT_KEY",
53
53
  "HUE_SERVICE_KEY",
54
54
  ];
55
- /** Hue's own credential shapes: project keys, attempt grants and the project MCP key. */
56
- const HUE_CREDENTIAL_SHAPE = /^hue_(sk|attempt|mcp)_/;
57
- /** True for a control-plane variable by name, or for any variable holding a Hue credential. */
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. */
58
98
  export function isHueControlPlaneCredential(name, value) {
59
99
  if (HUE_CONTROL_PLANE_VARIABLES.includes(name))
60
100
  return true;
61
- return typeof value === "string" && HUE_CREDENTIAL_SHAPE.test(value.trim());
101
+ return typeof value === "string" && hasHueControlPlaneCredential(value);
62
102
  }
63
103
  /** The parent's variables without Hue control-plane credentials; `undefined` values are dropped. */
64
104
  export function stripHueControlPlaneCredentials(parent) {
@@ -3,5 +3,5 @@ export type { EnvironmentClientOptions } from "./environment/client.js";
3
3
  export { bindEnvironmentTools } from "./environment/tools.js";
4
4
  export type { BindEnvironmentToolsOptions, EnvironmentTool } from "./environment/tools.js";
5
5
  export type * from "./environment/types.js";
6
- export { agentEnvironment, HUE_CONTROL_PLANE_VARIABLES, isHueControlPlaneCredential, legacyMcpCapability, stripHueControlPlaneCredentials, worldHandoff, worldNow, writeMcpConfig, } from "./environment/world.js";
6
+ export { agentEnvironment, HUE_CONTROL_PLANE_CREDENTIAL_PREFIXES, HUE_CONTROL_PLANE_VARIABLES, isHueControlPlaneCredential, legacyMcpCapability, stripHueControlPlaneCredentials, worldHandoff, worldNow, writeMcpConfig, } from "./environment/world.js";
7
7
  export type { AgentEnvironmentOptions, McpConfigFile } from "./environment/world.js";
@@ -1,3 +1,3 @@
1
1
  export { createEnvironmentClient, EnvironmentClient, HueEnvironmentError, isTransientEnvironmentError, } from "./environment/client.js";
2
2
  export { bindEnvironmentTools } from "./environment/tools.js";
3
- export { agentEnvironment, HUE_CONTROL_PLANE_VARIABLES, isHueControlPlaneCredential, legacyMcpCapability, stripHueControlPlaneCredentials, worldHandoff, worldNow, writeMcpConfig, } from "./environment/world.js";
3
+ export { agentEnvironment, HUE_CONTROL_PLANE_CREDENTIAL_PREFIXES, HUE_CONTROL_PLANE_VARIABLES, isHueControlPlaneCredential, legacyMcpCapability, stripHueControlPlaneCredentials, worldHandoff, worldNow, writeMcpConfig, } from "./environment/world.js";
@@ -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, 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";
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, 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, IdentityUpdate, Tag, TaggedIdentity, UpdatedRun } 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. */
@@ -79,8 +79,12 @@ export declare class EvaluationClient {
79
79
  private registryPage;
80
80
  /** Reads the current project to confirm the key and origin. */
81
81
  checkConnection(): Promise<ProjectConnection>;
82
- /** Creates a dataset with an initial draft version. */
83
- createDataset(input: Identity): Promise<Dataset>;
82
+ /** Creates a dataset with an initial draft version; `tags` names the tags it starts with. */
83
+ createDataset(input: TaggedIdentity): Promise<Dataset>;
84
+ /** Renames a dataset or replaces its tags by name; the same values again change nothing. */
85
+ updateDataset(id: string, input: IdentityUpdate & {
86
+ slug?: string;
87
+ }): Promise<Omit<Dataset, "versions">>;
84
88
  /** Reads a dataset and its versions. */
85
89
  getDataset(id: string): Promise<Dataset>;
86
90
  /** Lists datasets without their versions. */
@@ -103,7 +107,14 @@ export declare class EvaluationClient {
103
107
  /** Freezes a draft version at the given revision; frozen versions are immutable. */
104
108
  freezeDatasetVersion(id: string, expectedRevision: number): Promise<DatasetVersion>;
105
109
  /** Creates a scorer identity; publish definitions with {@link publishScorerVersion}. */
106
- createScorer(input: Identity): Promise<Scorer>;
110
+ createScorer(input: TaggedIdentity): Promise<Scorer>;
111
+ /** Renames a scorer or replaces its tags by name; the same values again change nothing. */
112
+ updateScorer(id: string, input: IdentityUpdate): Promise<Scorer>;
113
+ /** Lists the project's tags in the order they were created. */
114
+ listTags(): Promise<{
115
+ /** The project's tags. */
116
+ items: Tag[];
117
+ }>;
107
118
  /** Reads a scorer and its published versions. */
108
119
  getScorer(id: string): Promise<Scorer>;
109
120
  /** Lists scorers. */
@@ -113,7 +124,11 @@ export declare class EvaluationClient {
113
124
  /** Reads a published scorer version. */
114
125
  getScorerVersion(id: string): Promise<ScorerVersion>;
115
126
  /** Creates an eval set using the existing v1 registry path. */
116
- createEvalSet(input: Identity): Promise<EvalSet>;
127
+ createEvalSet(input: TaggedIdentity): Promise<EvalSet>;
128
+ /** Renames an eval set or replaces its tags by name. */
129
+ updateEvalSet(id: string, input: IdentityUpdate & {
130
+ slug?: string;
131
+ }): Promise<Omit<EvalSet, "versions">>;
117
132
  /** Reads an eval set and its versions. */
118
133
  getEvalSet(id: string): Promise<EvalSet>;
119
134
  /** Lists eval sets. */
@@ -136,7 +151,9 @@ export declare class EvaluationClient {
136
151
  /** Freezes a draft eval set version at its expected revision. */
137
152
  freezeEvalSetVersion(id: string, expectedRevision: number): Promise<EvalSetVersion>;
138
153
  /** Creates an evaluator identity. */
139
- createEvaluator(input: Identity): Promise<Evaluator>;
154
+ createEvaluator(input: TaggedIdentity): Promise<Evaluator>;
155
+ /** Renames an evaluator or replaces its tags by name. */
156
+ updateEvaluator(id: string, input: IdentityUpdate): Promise<Evaluator>;
140
157
  /** Reads an evaluator and its published versions. */
141
158
  getEvaluator(id: string): Promise<Evaluator>;
142
159
  /** Lists evaluators. */
@@ -152,6 +169,8 @@ export declare class EvaluationClient {
152
169
  datasetVersionId: string;
153
170
  scorerVersionIds: string[];
154
171
  config: JsonValue;
172
+ /** The experiment's own tag names; it also shows its dataset's tags. */
173
+ tags?: string[];
155
174
  }): Promise<{
156
175
  /** Experiment ID. */
157
176
  id: string;
@@ -160,6 +179,11 @@ export declare class EvaluationClient {
160
179
  }>;
161
180
  /** Reads an experiment with its evaluation run and execution counts. */
162
181
  getExperiment(id: string): Promise<Experiment>;
182
+ /** Renames an experiment or replaces its own tags by name. */
183
+ updateExperiment(id: string, input: {
184
+ name?: string;
185
+ tags?: string[];
186
+ }): Promise<UpdatedRun>;
163
187
  /** Lists an experiment's cases with their latest executions. */
164
188
  listExperimentItems(id: string, page?: PageOptions): Promise<Page<ExperimentItem>>;
165
189
  /** Reads one frozen case of an experiment. */
@@ -261,6 +285,8 @@ export declare class EvaluationClient {
261
285
  evalSetVersionId: string;
262
286
  evaluatorVersionIds: string[];
263
287
  config: JsonValue;
288
+ /** The run's own tag names; it also shows its eval set's tags. */
289
+ tags?: string[];
264
290
  }): Promise<{
265
291
  /** Run ID. */
266
292
  id: string;
@@ -271,6 +297,11 @@ export declare class EvaluationClient {
271
297
  }>;
272
298
  /** Reads a run with its scoring progress. */
273
299
  getRun(id: string): Promise<Run>;
300
+ /** Renames a run or replaces its own tags by name; its eval set's tags are not its own. */
301
+ updateRun(id: string, input: {
302
+ name?: string;
303
+ tags?: string[];
304
+ }): Promise<UpdatedRun>;
274
305
  /** Lists a run's cases with their latest executions. */
275
306
  listRunItems(id: string, page?: PageOptions): Promise<Page<ExperimentItem>>;
276
307
  /** Reads one frozen case of a run. */
@@ -89,6 +89,15 @@ function productRegistryFields(value) {
89
89
  }
90
90
  return result;
91
91
  }
92
+ /** Tag names travel as given; anything but a list of strings is a caller error. */
93
+ function tagNames(tags) {
94
+ if (!Array.isArray(tags) || !tags.every((tag) => typeof tag === "string"))
95
+ throw new TypeError("tags must be a list of tag names");
96
+ return tags;
97
+ }
98
+ function withTagNames(input) {
99
+ return input.tags === undefined ? input : { ...input, tags: tagNames(input.tags) };
100
+ }
92
101
  const runResponseAliases = [
93
102
  ["datasetId", "evalSetId"],
94
103
  ["datasetName", "evalSetName"],
@@ -388,15 +397,21 @@ export class EvaluationClient {
388
397
  const query = new URLSearchParams(this.page(options).slice(1));
389
398
  if (options.includeArchived !== undefined)
390
399
  query.set("includeArchived", String(options.includeArchived));
400
+ for (const tag of tagNames(options.tags ?? []))
401
+ query.append("tag", tag);
391
402
  return query.size ? `?${query}` : "";
392
403
  }
393
404
  /** Reads the current project to confirm the key and origin. */
394
405
  checkConnection() {
395
406
  return this.request("GET", "/projects/current");
396
407
  }
397
- /** Creates a dataset with an initial draft version. */
408
+ /** Creates a dataset with an initial draft version; `tags` names the tags it starts with. */
398
409
  createDataset(input) {
399
- return this.request("POST", "/datasets", input);
410
+ return this.request("POST", "/datasets", withTagNames(input));
411
+ }
412
+ /** Renames a dataset or replaces its tags by name; the same values again change nothing. */
413
+ updateDataset(id, input) {
414
+ return this.request("PATCH", `/datasets/${uuid(id)}`, withTagNames(input), undefined, undefined, true);
400
415
  }
401
416
  /** Reads a dataset and its versions. */
402
417
  getDataset(id) {
@@ -430,7 +445,15 @@ export class EvaluationClient {
430
445
  }
431
446
  /** Creates a scorer identity; publish definitions with {@link publishScorerVersion}. */
432
447
  createScorer(input) {
433
- return this.request("POST", "/scorers", input);
448
+ return this.request("POST", "/scorers", withTagNames(input));
449
+ }
450
+ /** Renames a scorer or replaces its tags by name; the same values again change nothing. */
451
+ updateScorer(id, input) {
452
+ return this.request("PATCH", `/scorers/${uuid(id)}`, withTagNames(input), undefined, undefined, true);
453
+ }
454
+ /** Lists the project's tags in the order they were created. */
455
+ listTags() {
456
+ return this.request("GET", "/tags");
434
457
  }
435
458
  /** Reads a scorer and its published versions. */
436
459
  getScorer(id) {
@@ -452,6 +475,10 @@ export class EvaluationClient {
452
475
  async createEvalSet(input) {
453
476
  return productRegistryFields(await this.createDataset(input));
454
477
  }
478
+ /** Renames an eval set or replaces its tags by name. */
479
+ async updateEvalSet(id, input) {
480
+ return productRegistryFields(await this.updateDataset(id, input));
481
+ }
455
482
  /** Reads an eval set and its versions. */
456
483
  async getEvalSet(id) {
457
484
  return productRegistryFields(await this.getDataset(id));
@@ -484,6 +511,10 @@ export class EvaluationClient {
484
511
  async createEvaluator(input) {
485
512
  return productRegistryFields(await this.createScorer(input));
486
513
  }
514
+ /** Renames an evaluator or replaces its tags by name. */
515
+ async updateEvaluator(id, input) {
516
+ return productRegistryFields(await this.updateScorer(id, input));
517
+ }
487
518
  /** Reads an evaluator and its published versions. */
488
519
  async getEvaluator(id) {
489
520
  return productRegistryFields(await this.getScorer(id));
@@ -502,12 +533,16 @@ export class EvaluationClient {
502
533
  }
503
534
  /** Creates an experiment over a frozen dataset version with pinned scorer versions and a configuration. */
504
535
  createExperiment(input) {
505
- return this.request("POST", "/experiments", input);
536
+ return this.request("POST", "/experiments", withTagNames(input));
506
537
  }
507
538
  /** Reads an experiment with its evaluation run and execution counts. */
508
539
  getExperiment(id) {
509
540
  return this.request("GET", `/experiments/${uuid(id)}`);
510
541
  }
542
+ /** Renames an experiment or replaces its own tags by name. */
543
+ updateExperiment(id, input) {
544
+ return this.request("PATCH", `/experiments/${uuid(id)}`, withTagNames(input), undefined, undefined, true);
545
+ }
511
546
  /** Lists an experiment's cases with their latest executions. */
512
547
  listExperimentItems(id, page) {
513
548
  return this.request("GET", `/experiments/${uuid(id)}/items${this.page(page)}`);
@@ -656,12 +691,16 @@ export class EvaluationClient {
656
691
  }
657
692
  /** Creates a run using product request fields on the existing v1 path. */
658
693
  async createRun(input) {
659
- return productRunFields(await this.request("POST", "/experiments", input), "run");
694
+ return productRunFields(await this.request("POST", "/experiments", withTagNames(input)), "run");
660
695
  }
661
696
  /** Reads a run with its scoring progress. */
662
697
  async getRun(id) {
663
698
  return productRunFields(await this.getExperiment(id), "run");
664
699
  }
700
+ /** Renames a run or replaces its own tags by name; its eval set's tags are not its own. */
701
+ async updateRun(id, input) {
702
+ return this.updateExperiment(id, input);
703
+ }
665
704
  /** Lists a run's cases with their latest executions. */
666
705
  async listRunItems(id, page) {
667
706
  return productRunFields(await this.listExperimentItems(id, page), "run");
@@ -5,18 +5,25 @@ const metricName = z
5
5
  .min(1)
6
6
  .max(64)
7
7
  .regex(/^[a-zA-Z][a-zA-Z0-9_]*$/);
8
+ const metricDescription = z.string().trim().min(1).max(500).optional();
8
9
  const metric = z.discriminatedUnion("type", [
9
- z.strictObject({ name: metricName, type: z.literal("boolean") }),
10
- z.strictObject({ name: metricName, type: z.literal("text") }),
10
+ z.strictObject({
11
+ name: metricName,
12
+ type: z.literal("boolean"),
13
+ description: metricDescription,
14
+ }),
15
+ z.strictObject({ name: metricName, type: z.literal("text"), description: metricDescription }),
11
16
  z.strictObject({
12
17
  name: metricName,
13
18
  type: z.literal("number"),
19
+ description: metricDescription,
14
20
  min: z.number().finite().optional(),
15
21
  max: z.number().finite().optional(),
16
22
  }),
17
23
  z.strictObject({
18
24
  name: metricName,
19
25
  type: z.literal("category"),
26
+ description: metricDescription,
20
27
  categories: z.array(z.string()).min(1),
21
28
  }),
22
29
  ]);
@@ -32,7 +32,9 @@ export function defineLocalScorer(options) {
32
32
  language: "typescript",
33
33
  entrypoint: options.entrypoint,
34
34
  sourceDigest: sourceDigest(options.source),
35
- metrics: options.metrics,
35
+ metrics: options.metrics.map((metric) => metric.description === undefined
36
+ ? metric
37
+ : { ...metric, description: metric.description.trim() }),
36
38
  },
37
39
  score: options.score,
38
40
  };
@@ -19,6 +19,8 @@ export interface PageOptions {
19
19
  export interface RegistryPageOptions extends PageOptions {
20
20
  /** Include archived identities so callers can diagnose slug conflicts explicitly. */
21
21
  includeArchived?: boolean;
22
+ /** Only identities carrying any of these tag names, matched without case. */
23
+ tags?: string[];
22
24
  }
23
25
  /** Human-readable identity of a dataset or scorer. */
24
26
  export interface Identity {
@@ -29,12 +31,50 @@ export interface Identity {
29
31
  /** Optional description. */
30
32
  description?: string;
31
33
  }
34
+ /** A dataset or scorer to create, with the names of the tags it starts with. */
35
+ export interface TaggedIdentity extends Identity {
36
+ /** Tag names; a name the project has no tag for creates one. */
37
+ tags?: string[];
38
+ }
39
+ /** Identity fields and tags to change on a dataset or scorer; omitted fields stay. */
40
+ export interface IdentityUpdate {
41
+ /** New display name. */
42
+ name?: string;
43
+ /** New description. */
44
+ description?: string;
45
+ /** Every tag name the item should carry; replaces its tags. */
46
+ tags?: string[];
47
+ }
48
+ /** An experiment's (run's) name and tags after an update. */
49
+ export interface UpdatedRun {
50
+ /** Experiment (run) ID. */
51
+ id: string;
52
+ /** Display name. */
53
+ name: string;
54
+ /** The experiment's own tags. */
55
+ tags: Tag[];
56
+ /** Its dataset's (eval set's) tags. */
57
+ evalSetTags: Tag[];
58
+ }
59
+ /** The colors a tag can take. */
60
+ export type TagColor = "default" | "gray" | "brown" | "orange" | "yellow" | "green" | "blue" | "purple" | "pink" | "red";
61
+ /** A project's tag: a named, colored label that groups datasets, scorers and experiments. */
62
+ export interface Tag {
63
+ /** Tag ID. */
64
+ id: string;
65
+ /** Name, unique in the project without case. */
66
+ name: string;
67
+ /** Display color. */
68
+ color: TagColor;
69
+ }
32
70
  /** A dataset and its versions. */
33
71
  export interface Dataset extends Identity {
34
72
  /** Dataset ID. */
35
73
  id: string;
36
74
  /** Archive timestamp on current servers; absent on older compatible responses. */
37
75
  archivedAt?: string | null;
76
+ /** The dataset's tags on servers that support tags; absent on older responses. */
77
+ tags?: Tag[];
38
78
  /** Versions of this dataset. */
39
79
  versions: DatasetVersion[];
40
80
  }
@@ -139,11 +179,15 @@ export type MetricDefinition = {
139
179
  name: string;
140
180
  /** Boolean or free-text metric. */
141
181
  type: "boolean" | "text";
182
+ /** What the metric checks, shown with its check in Hue's run view (1–500 characters). */
183
+ description?: string;
142
184
  } | {
143
185
  /** Metric name. */
144
186
  name: string;
145
187
  /** Numeric metric. */
146
188
  type: "number";
189
+ /** What the metric checks, shown with its check in Hue's run view (1–500 characters). */
190
+ description?: string;
147
191
  /** Inclusive lower bound. */
148
192
  min?: number;
149
193
  /** Inclusive upper bound. */
@@ -153,6 +197,8 @@ export type MetricDefinition = {
153
197
  name: string;
154
198
  /** Categorical metric. */
155
199
  type: "category";
200
+ /** What the metric checks, shown with its check in Hue's run view (1–500 characters). */
201
+ description?: string;
156
202
  /** Allowed values. */
157
203
  categories: string[];
158
204
  };
@@ -397,6 +443,8 @@ export interface Scorer extends Identity {
397
443
  id: string;
398
444
  /** Archive timestamp on current servers; absent on older compatible responses. */
399
445
  archivedAt?: string | null;
446
+ /** The scorer's tags on servers that support tags; absent on older responses. */
447
+ tags?: Tag[];
400
448
  /** Published versions, when included in the response. */
401
449
  versions?: ScorerVersion[];
402
450
  }
@@ -486,6 +534,10 @@ export interface Experiment {
486
534
  name: string;
487
535
  /** Frozen dataset version under test. */
488
536
  datasetVersionId: string;
537
+ /** The experiment's own tags on servers that support tags; absent on older responses. */
538
+ tags?: Tag[];
539
+ /** Its dataset's tags, which tag filters match as well; absent on older responses. */
540
+ evalSetTags?: Tag[];
489
541
  /** Configuration handed to the target. */
490
542
  config: JsonValue;
491
543
  /** Digest of `config`. */
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.13.0";
2
+ export declare const sdkVersion = "0.13.1";
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.13.0";
3
+ export const sdkVersion = "0.13.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hue-run/sdk",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "publishConfig": {