@hue-run/sdk 0.2.2 → 0.3.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 +52 -0
- package/ENVIRONMENTS.md +24 -6
- package/EVALUATIONS.md +69 -2
- package/README.md +20 -0
- package/dist/environment/client.d.ts +2 -2
- package/dist/environment/types.d.ts +33 -1
- package/dist/evals/client.d.ts +30 -1
- package/dist/evals/client.js +16 -0
- package/dist/evals/environment-target.d.ts +84 -0
- package/dist/evals/environment-target.js +201 -0
- package/dist/evals/local-worker.d.ts +88 -0
- package/dist/evals/local-worker.js +171 -0
- package/dist/evals/runner.d.ts +1 -1
- package/dist/evals/runner.js +22 -12
- package/dist/evals/scorer-publication.js +17 -1
- package/dist/evals/scorers.d.ts +10 -4
- package/dist/evals/scorers.js +11 -8
- package/dist/evals/simulation.d.ts +6 -6
- package/dist/evals/simulation.js +45 -222
- package/dist/evals/types.d.ts +39 -1
- package/dist/evals.d.ts +2 -0
- package/dist/evals.js +1 -0
- package/dist/setup/checkpoint.d.ts +14 -0
- package/dist/setup/checkpoint.js +186 -0
- package/dist/setup/cli.d.ts +2 -0
- package/dist/setup/cli.js +150 -0
- package/dist/setup/detect.d.ts +3 -0
- package/dist/setup/detect.js +146 -0
- package/dist/setup/machine.d.ts +109 -0
- package/dist/setup/machine.js +43 -0
- package/dist/setup/render.d.ts +16 -0
- package/dist/setup/render.js +111 -0
- package/dist/setup/runner.d.ts +101 -0
- package/dist/setup/runner.js +117 -0
- package/dist/setup/types.d.ts +145 -0
- package/dist/setup/types.js +2 -0
- package/dist/setup.d.ts +6 -0
- package/dist/setup.js +6 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +12 -1
- package/setup-events.schema.json +134 -0
package/CLI.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Hue setup-session CLI contract
|
|
2
|
+
|
|
3
|
+
Unreleased TypeScript `0.3.1` includes the merged local setup-session CLI core:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
hue setup
|
|
7
|
+
hue setup --agent
|
|
8
|
+
hue resume
|
|
9
|
+
hue status
|
|
10
|
+
hue claim # reports account attachment unavailable in this local core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
These commands belong only to an installer setup session. They do not create or launch a Hue Run,
|
|
14
|
+
Scenario, evaluation, or worker. `claim` only means attaching the anonymous setup project to an
|
|
15
|
+
account; there is no generic `connect` command or local-agent connection in this CLI.
|
|
16
|
+
|
|
17
|
+
`setup` only inspects bounded manifest and lockfile metadata. It does not execute repository code,
|
|
18
|
+
change project files, open a browser, ask a question, create a trial, or contact Hue. `claim` also
|
|
19
|
+
makes no network request in this build and reports that account attachment is unavailable. `resume`
|
|
20
|
+
deterministically continues the same setup-session checkpoint; `status` reads it without changing
|
|
21
|
+
it.
|
|
22
|
+
|
|
23
|
+
Checkpoints are secret-free JSON files outside the project, under the operating system's user state
|
|
24
|
+
directory. On POSIX, directories use mode `0700` and files use mode `0600`; Windows uses its
|
|
25
|
+
per-user local state directory without interpreting POSIX mode bits. Writes are atomic, and a
|
|
26
|
+
configured state location inside the project is rejected. Checkpoints contain project categories
|
|
27
|
+
and hashes, never environment values, credentials, source contents, or claim URLs.
|
|
28
|
+
|
|
29
|
+
`--agent` is explicitly noninteractive JSONL. It never uses ANSI, stdin, or a browser, and each
|
|
30
|
+
invocation emits exactly one terminal `run.completed` or `run.failed` installer event. Those names
|
|
31
|
+
describe the setup-session lifecycle, not a Hue Run. Human output is an append-only inline
|
|
32
|
+
transcript. Plain and JSONL output contain no ANSI; `NO_COLOR`, `TERM=dumb`, CI, and non-TTY output
|
|
33
|
+
select plain mode automatically.
|
|
34
|
+
|
|
35
|
+
Every JSONL record carries `contractVersion: 1`. The TypeScript union is exported from
|
|
36
|
+
`@hue-run/sdk/setup`; the JSON Schema is exported as
|
|
37
|
+
`@hue-run/sdk/setup-events.schema.json`. Consumers must ignore neither unknown versions nor terminal
|
|
38
|
+
failures.
|
|
39
|
+
|
|
40
|
+
The future Fern implementation plugs into `SetupBackendAdapter`. Its three installer operations
|
|
41
|
+
create an anonymous setup trial hard-pinned to `trial_metadata_v1`, verify instrumentation-only
|
|
42
|
+
receipt evidence, and read account-claim state. Inputs carry deterministic idempotency keys and an
|
|
43
|
+
optional abort signal. Adapter results must use bounded, non-secret IDs; claim URLs may be sensitive
|
|
44
|
+
and therefore must never be checkpointed. Receipt verification does not prove that task or
|
|
45
|
+
environment content was captured and must never authorize Scenario publication. The adapter must
|
|
46
|
+
not create a Scenario, evaluation, worker, or Hue Run. No live implementation ships in this slice.
|
|
47
|
+
|
|
48
|
+
The V1 handoff is deliberately staged: setup verifies the anonymous instrumentation trace; `hue claim`
|
|
49
|
+
preserves the project and trace history; then the user performs an explicit content-approved capture
|
|
50
|
+
or rerun, with a prepared tester as the first golden path. Only that content-approved trace passes to
|
|
51
|
+
the separate review/publication flow for a Scenario. The actual URL and that handoff contract remain
|
|
52
|
+
deferred. Setup itself does not capture content, create, publish, or run a Scenario.
|
package/ENVIRONMENTS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Simulated environments
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The provider-aware environment APIs shipped in TypeScript `0.3.0` and are publicly available:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm install @hue-run/sdk zod
|
|
@@ -77,11 +77,12 @@ them to checkpoints or progress events, and it never mutates global `process.env
|
|
|
77
77
|
world seal cannot be confirmed, the checkpoint remains uncertain and resume neither reacquires
|
|
78
78
|
credentials nor invokes the callback again.
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
The SDK consumes the versioned connection contract but does not itself establish provider
|
|
81
|
+
fidelity. Public package acceptance exercises local control-plane responses and connection-bundle
|
|
82
|
+
handling; it does not call the issued facade endpoint. Exact installed-registry-package to hosted-
|
|
83
|
+
facade acceptance remains a post-publication Fern integration gate. No public SDK test calls the
|
|
84
|
+
official Gmail service, and passing these tests is not evidence of universal Gmail or Slack
|
|
85
|
+
parity. A matching Hue deployment and verified provider profile remain required.
|
|
85
86
|
|
|
86
87
|
## Repository-authored scenarios
|
|
87
88
|
|
|
@@ -97,6 +98,13 @@ identity-affecting defaults as Hue before resolving versions and rejects unknown
|
|
|
97
98
|
kinds. In particular, `document_verifier` is not part of this SDK contract and is rejected rather
|
|
98
99
|
than published with a guessed digest.
|
|
99
100
|
|
|
101
|
+
The extendable `EnvironmentDefinition` name remains the V1 contract and is also exported as
|
|
102
|
+
`EnvironmentDefinitionV1`. Use `EnvironmentDefinitionV2` to add immutable Gmail
|
|
103
|
+
`providerInstances`; `PublishableEnvironmentDefinition` is the publication/repository union.
|
|
104
|
+
Hue canonicalizes valid synthetic-principal UUIDs to lowercase, and repository resolution does
|
|
105
|
+
the same before comparing immutable digests, so casing-only UUID changes reuse the stored
|
|
106
|
+
version without dropping provider bindings.
|
|
107
|
+
|
|
100
108
|
```ts
|
|
101
109
|
const scenario = {
|
|
102
110
|
kind: "repository" as const,
|
|
@@ -180,3 +188,13 @@ that cannot be confirmed stays uncertain and never causes the agent to be replay
|
|
|
180
188
|
The hosted MCP connection exposes Hue's bounded native actions; it is not general Gmail or
|
|
181
189
|
Slack HTTP parity and does not proxy arbitrary provider traffic. Forking, in-place reset and
|
|
182
190
|
arbitrary-step diffs are outside this interface.
|
|
191
|
+
|
|
192
|
+
## Candidate context migration
|
|
193
|
+
|
|
194
|
+
This candidate-context restriction shipped in `@hue-run/sdk@0.3.0`.
|
|
195
|
+
|
|
196
|
+
`runSimulation` now supplies `context.item` as `{ id, externalKey }`. Read candidate inputs
|
|
197
|
+
from the callback's first argument. Expected outcomes, case metadata and environment-version
|
|
198
|
+
pins are available to evaluation and scoring code, and are omitted from the candidate callback.
|
|
199
|
+
Inputs and configuration are cloned before invocation so candidate mutations cannot change
|
|
200
|
+
pinned grading data. The generic `runExperiment` evaluator interface is unchanged.
|
package/EVALUATIONS.md
CHANGED
|
@@ -9,6 +9,8 @@ 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.
|
|
13
|
+
|
|
12
14
|
```ts
|
|
13
15
|
import { randomUUID } from "node:crypto";
|
|
14
16
|
import { createHue } from "@hue-run/sdk";
|
|
@@ -118,7 +120,11 @@ All declared metrics must appear exactly once and satisfy pinned types, bounds a
|
|
|
118
120
|
|
|
119
121
|
### Hosted and manual scorer pins
|
|
120
122
|
|
|
121
|
-
|
|
123
|
+
In TypeScript `0.3.1` (unreleased), the local runner executes only the three known built-in entries and bound `local_code` scorers. It leaves every other pin pending and reports its ID in `deferredScorerVersionIds`, including kinds and built-in entries introduced by a newer server. It never uploads a placeholder result that would occupy the immutable result slot, including placeholders already saved in an older SDK's checkpoint. Direct `scoreLocally()` calls reject pins that require another executor.
|
|
124
|
+
|
|
125
|
+
`world_outcome` pins run inside Hue and need no local callback or executable source digest. A supporting server owns their execution from saved world evidence. Legacy `local_code` pins still require the exact registered callback; changing the worker cannot convert those immutable pins into hosted ones.
|
|
126
|
+
|
|
127
|
+
Manual results require a human session. Hosted model-judge dispatch is an explicit separate API operation: inspect `getJudgeBudget()`, then call `createJudgeJobs(runId,{idempotencyKey,jobs:[{evaluationItemId,scorerVersionId}]})`. `listJudgeJobs`, `getJudgeJob` and `cancelJudgeJob` expose job progress and cancellation requests. These methods never claim that local execution has hosted provenance. Hosted job endpoints are covered by HTTP contract tests here; live hosted model execution is a separate platform acceptance phase. `listResults` and `getResult` read recorded local or hosted results.
|
|
122
128
|
|
|
123
129
|
When present, the budget's `authentication` reports credential resolution only. An
|
|
124
130
|
`available` status or `configured: true` does not prove that a provider accepted the
|
|
@@ -150,4 +156,65 @@ The runner stops scheduling more cases after an operational failure and waits fo
|
|
|
150
156
|
|
|
151
157
|
## Verification boundaries
|
|
152
158
|
|
|
153
|
-
`scripts/verify-package.mjs` installs a real packed tarball outside the monorepo and runs HTTP contract tests against a synthetic service plus actual OpenTelemetry exporters. It checks two configurations, rescoring without target invocation, absent/null output, upload resume, uncertain execution, exclusive checkpoints, source/metric contracts, content policy and terminating schema workers. `scripts/verify-evaluation-api.mjs` is a separate opt-in acceptance against a real Hue receiver/API; it creates synthetic datasets/scorers/experiments in the project associated with the supplied development key.
|
|
159
|
+
`scripts/verify-package.mjs` installs a real packed tarball outside the monorepo and runs HTTP contract tests against a synthetic service plus actual OpenTelemetry exporters. It checks two configurations, rescoring without target invocation, absent/null output, upload resume, uncertain execution, exclusive checkpoints, source/metric contracts, content policy and terminating schema workers. It also exercises the local worker's ready, incomplete and uncertain provider-attempt control-plane paths, but does not call an issued provider facade. `scripts/verify-evaluation-api.mjs` is a separate opt-in acceptance against a real Hue receiver/API; it creates synthetic datasets/scorers/experiments in the project associated with the supplied development key.
|
|
160
|
+
|
|
161
|
+
## Outbound local agent worker
|
|
162
|
+
|
|
163
|
+
`runLocalAgent` shipped in `@hue-run/sdk@0.3.0` and is publicly available. Install
|
|
164
|
+
`npm install @hue-run/sdk zod`. Queue registration, claims, scoped MCP capabilities and sealed
|
|
165
|
+
evidence require a supporting Hue server and project access; the package version alone does not
|
|
166
|
+
establish hosted provider availability.
|
|
167
|
+
|
|
168
|
+
`runLocalAgent` registers one fixed application callback and polls for queued runs. Hue selects
|
|
169
|
+
the registered key/revision; it does not send executable code or shell commands. Keep the
|
|
170
|
+
checkpoint directory private and durable. The worker persists result content and requires
|
|
171
|
+
acknowledged trace and sealed environment evidence.
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
import { createHue } from "@hue-run/sdk";
|
|
175
|
+
import { createEnvironmentClient } from "@hue-run/sdk/environment";
|
|
176
|
+
import { createEvaluationClient, runLocalAgent } from "@hue-run/sdk/evals";
|
|
177
|
+
import { runMyAgent } from "./agent.js"; // Your existing application entry point.
|
|
178
|
+
|
|
179
|
+
const connection = { apiKey: process.env.HUE_API_KEY! };
|
|
180
|
+
const hue = createHue({ ...connection, serviceName: "local-worker", captureContent: false });
|
|
181
|
+
try {
|
|
182
|
+
await runLocalAgent({
|
|
183
|
+
client: createEvaluationClient(connection),
|
|
184
|
+
environmentClient: createEnvironmentClient(connection),
|
|
185
|
+
hue,
|
|
186
|
+
agent: { key: "support-agent", name: "Support agent", revision: "1" },
|
|
187
|
+
checkpointDirectory: ".hue-checkpoints/support-agent",
|
|
188
|
+
scorers: [], // Hue-executed scorers require no local callback registration.
|
|
189
|
+
target: (inputs, tools, context) => runMyAgent({ inputs, tools, config: context.config }),
|
|
190
|
+
});
|
|
191
|
+
} finally {
|
|
192
|
+
await hue.shutdownSafe();
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The callback receives cloned inputs, local tools, and an allowlisted context containing
|
|
197
|
+
`config`, `item: {id, externalKey}`, `executionId`, `environmentRunId`,
|
|
198
|
+
`trace: {traceId,spanId}` and a short-lived `mcp` capability. Expected outcomes, case metadata
|
|
199
|
+
and original source pins remain private to grading. Pass the tools or scoped MCP capability into
|
|
200
|
+
the agent's actual tool boundary; their presence does not redirect provider calls. Capabilities
|
|
201
|
+
are not written to checkpoints. `maxRuns` limits completed runs for one-shot workers, while
|
|
202
|
+
`signal` stops polling. A stop signal does not forcibly cancel an already executing callback.
|
|
203
|
+
|
|
204
|
+
For an experiment with an immutable V2 attempt baseline, also supply `actualAgentManifest`, the
|
|
205
|
+
exact ordered `requestedProviders`, and an `mcpSurface` selected from that request. The worker
|
|
206
|
+
creates the world and prepares once before target code. A ready response exposes the memory-only
|
|
207
|
+
`connectionBundle` and keeps `context.mcp` as its selected MCP projection; it never mints the
|
|
208
|
+
legacy generic capability for that attempt. An incomplete response seals the world as completed
|
|
209
|
+
without invoking the target or scorers. A lost preparation acknowledgement remains uncertain
|
|
210
|
+
and is never recovered through binding reads, credential refresh or target replay. Synthetic
|
|
211
|
+
acceptance does not contact official Gmail or claim universal provider parity.
|
|
212
|
+
|
|
213
|
+
Completion or result-upload failures keep the run claimed by the durable worker identity.
|
|
214
|
+
Restart with the same checkpoint directory to resume saved uploads without invoking the
|
|
215
|
+
candidate again. A lost world-seal acknowledgement is recovered by reading authoritative world
|
|
216
|
+
state. If the seal or candidate outcome cannot be confirmed, or an outcome cannot be serialized,
|
|
217
|
+
the worker reports `attention` and stops; operator investigation is required. Such runs are not
|
|
218
|
+
automatically reclaimed, and presenting the same uncertain checkpoint again cannot replay the
|
|
219
|
+
candidate. Public package acceptance proves this lifecycle against local fixtures; exact
|
|
220
|
+
installed-registry-package to hosted-facade acceptance remains a post-publication Fern gate.
|
package/README.md
CHANGED
|
@@ -395,6 +395,26 @@ published. The chatbot README describes running that external installation.
|
|
|
395
395
|
|
|
396
396
|
The optional `@hue-run/sdk/evals` entry point supports dataset/scorer registration, frozen-version experiments, local built-in/custom scoring, upload resume, and historical rescoring. See the [evaluation guide](https://docs.hue.run/evaluations/first-evaluation) for the complete journey, content policy and checkpoint recovery contract.
|
|
397
397
|
|
|
398
|
+
### App-launched local workers
|
|
399
|
+
|
|
400
|
+
`runLocalAgent()` shipped in TypeScript `0.3.0` and is available from npm. It connects a fixed local
|
|
401
|
+
callback to app-launched simulation work while keeping the agent and provider orchestration in
|
|
402
|
+
the developer's process. It shares
|
|
403
|
+
`runSimulation()`'s provider-aware world lifecycle, keeps scoped credentials in callback memory,
|
|
404
|
+
skips target/scorer execution for incomplete environments, and never reacquires or replays after
|
|
405
|
+
an uncertain preparation. See the
|
|
406
|
+
[outbound worker contract](EVALUATIONS.md#outbound-local-agent-worker) and
|
|
407
|
+
[simulated environment guide](ENVIRONMENTS.md). Tests exercise local control-plane fixtures, not
|
|
408
|
+
an issued facade endpoint or the official Gmail service, and do not claim universal provider
|
|
409
|
+
parity.
|
|
410
|
+
|
|
411
|
+
Unreleased TypeScript `0.3.1` defers all scorers the SDK does not execute locally. Only built-ins
|
|
412
|
+
and bound `local_code` callbacks run here; other pins remain pending for their authorized executor.
|
|
413
|
+
See [scorer execution](EVALUATIONS.md#hosted-and-manual-scorer-pins).
|
|
414
|
+
|
|
415
|
+
The [setup CLI](CLI.md) is a resumable local inspection core. Existing customers connect their
|
|
416
|
+
agents with `runLocalAgent()`; setup does not register workers or launch Scenarios.
|
|
417
|
+
|
|
398
418
|
## Managed targets
|
|
399
419
|
|
|
400
420
|
Start a frozen dataset run in Hue while your agent stays in your application. Expose a
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActionInput, ActionResult, CoverageGapInput, CoverageGapResult, CreateRunInput, Environment,
|
|
1
|
+
import type { ActionInput, ActionResult, CoverageGapInput, CoverageGapResult, CreateRunInput, Environment, PublishableEnvironmentDefinition, EnvironmentIdentity, EnvironmentPage, EnvironmentPageOptions, EnvironmentRun, EnvironmentSummary, EnvironmentVersion, EnvironmentVersionSummary, FinishRunInput, SealedRun, StepPage, StepPageOptions } from "./types.js";
|
|
2
2
|
/** Connection and retry options for {@link createEnvironmentClient}. */
|
|
3
3
|
export interface EnvironmentClientOptions {
|
|
4
4
|
/** Project service key sent as a bearer token; server-side only. */
|
|
@@ -37,7 +37,7 @@ export declare class EnvironmentClient {
|
|
|
37
37
|
/** Reads one environment and its immutable version summaries. */
|
|
38
38
|
getEnvironment(id: string): Promise<Environment>;
|
|
39
39
|
/** Publishes an immutable definition; this non-idempotent registry write is not retried. */
|
|
40
|
-
publishVersion(environmentId: string, definition:
|
|
40
|
+
publishVersion(environmentId: string, definition: PublishableEnvironmentDefinition): Promise<EnvironmentVersionSummary>;
|
|
41
41
|
/** Reads a full immutable environment version and generated action catalog. */
|
|
42
42
|
getVersion(id: string): Promise<EnvironmentVersion>;
|
|
43
43
|
/** Creates or recovers one fresh isolated world using a stable idempotency key. */
|
|
@@ -170,12 +170,44 @@ export interface EnvironmentDefinition {
|
|
|
170
170
|
/** Caller-owned immutable metadata. */
|
|
171
171
|
metadata?: Record<string, JsonValue>;
|
|
172
172
|
}
|
|
173
|
+
/** The extendable legacy name remains V1. Publication and runs select their
|
|
174
|
+
* explicit version; provider context is validated by the authoritative server. */
|
|
175
|
+
export type EnvironmentDefinitionV1 = EnvironmentDefinition;
|
|
176
|
+
/** One synthetic Gmail principal and its world-state collection bindings. */
|
|
177
|
+
export interface GmailProviderInstance {
|
|
178
|
+
/** Stable instance key referenced by attempt provider selection. */
|
|
179
|
+
providerInstanceKey: string;
|
|
180
|
+
/** Provider discriminator for the V2 Gmail slice. */
|
|
181
|
+
providerId: "google.gmail";
|
|
182
|
+
/** Synthetic principal UUID, canonicalized to lowercase by Hue. */
|
|
183
|
+
syntheticPrincipalId: string;
|
|
184
|
+
/** Versioned mapping from Gmail concepts to authored-world collections. */
|
|
185
|
+
configuration: {
|
|
186
|
+
/** Gmail mailbox configuration discriminator. */
|
|
187
|
+
kind: "gmail_mailbox/v1";
|
|
188
|
+
/** Collection containing synthetic messages. */
|
|
189
|
+
messagesCollection: string;
|
|
190
|
+
/** Collection containing synthetic drafts. */
|
|
191
|
+
draftsCollection: string;
|
|
192
|
+
/** Synthetic mailbox address. */
|
|
193
|
+
mailboxAddress: string;
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/** V2 authored world with immutable provider-instance bindings. */
|
|
197
|
+
export interface EnvironmentDefinitionV2 extends Omit<EnvironmentDefinition, "schemaVersion"> {
|
|
198
|
+
/** Definition schema discriminator. */
|
|
199
|
+
schemaVersion: 2;
|
|
200
|
+
/** Provider instances available to a strict attempt profile. */
|
|
201
|
+
providerInstances: GmailProviderInstance[];
|
|
202
|
+
}
|
|
203
|
+
/** Definition accepted by immutable environment publication. */
|
|
204
|
+
export type PublishableEnvironmentDefinition = EnvironmentDefinitionV1 | EnvironmentDefinitionV2;
|
|
173
205
|
/** Full immutable environment version and its generated action catalog. */
|
|
174
206
|
export interface EnvironmentVersion extends EnvironmentVersionSummary {
|
|
175
207
|
/** Owning environment identity. */
|
|
176
208
|
environmentId: string;
|
|
177
209
|
/** Stored, defaulted authored definition. */
|
|
178
|
-
definition:
|
|
210
|
+
definition: PublishableEnvironmentDefinition;
|
|
179
211
|
/** Generated agent-visible actions. */
|
|
180
212
|
actions: ActionDefinition[];
|
|
181
213
|
}
|
package/dist/evals/client.d.ts
CHANGED
|
@@ -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 { CaseWrite, CompleteExecution, Completion, Dataset, DatasetCase, DatasetVersion, EvaluationItem, EvaluationRun, EnvironmentEvidenceSnapshot, Execution, Experiment, ExperimentCase, ExperimentItem, Identity, JsonValue, JudgeBudget, JudgeJob, Page, PageOptions, RegistryPageOptions, Result, ResultSummary, Scorer, ScorerDefinition, ScorerVersion, SimulationMcpCapability, StartExecution, Subject, StoredResult } from "./types.js";
|
|
3
|
+
import type { CaseWrite, CompleteExecution, Completion, Dataset, DatasetCase, DatasetVersion, EvaluationItem, EvaluationRun, 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";
|
|
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. */
|
|
@@ -176,6 +176,35 @@ export declare class EvaluationClient {
|
|
|
176
176
|
}>;
|
|
177
177
|
/** Reads the project's hosted judge budget and admission controls. */
|
|
178
178
|
getJudgeBudget(): Promise<JudgeBudget>;
|
|
179
|
+
/** Register or refresh the fixed local agent key and revision. */
|
|
180
|
+
registerLocalAgent(input: LocalAgentRegistration): Promise<RegisteredLocalAgent>;
|
|
181
|
+
/** Claim a queued run for this agent and durable worker identity. */
|
|
182
|
+
claimLocalAgentRun(input: {
|
|
183
|
+
agentId: string;
|
|
184
|
+
workerId: string;
|
|
185
|
+
}): Promise<LocalAgentClaim | null>;
|
|
186
|
+
/** Refresh the lease of a claimed local run. */
|
|
187
|
+
heartbeatLocalAgentRun(input: {
|
|
188
|
+
runId: string;
|
|
189
|
+
workerId: string;
|
|
190
|
+
}): Promise<{
|
|
191
|
+
/** Queue-run identity. */
|
|
192
|
+
runId: string;
|
|
193
|
+
/** The worker claim remains active. */
|
|
194
|
+
active: true;
|
|
195
|
+
}>;
|
|
196
|
+
/** Report acknowledged completion or an execution requiring attention. */
|
|
197
|
+
completeLocalAgentRun(input: {
|
|
198
|
+
runId: string;
|
|
199
|
+
workerId: string;
|
|
200
|
+
state: "completed" | "attention";
|
|
201
|
+
failureType?: string;
|
|
202
|
+
}): Promise<{
|
|
203
|
+
/** Queue-run identity. */
|
|
204
|
+
runId: string;
|
|
205
|
+
/** Acknowledged terminal queue state. */
|
|
206
|
+
state: "completed" | "attention";
|
|
207
|
+
}>;
|
|
179
208
|
/** Creates the legacy execution-scoped generic MCP capability for one world. */
|
|
180
209
|
createSimulationMcpCapability(input: {
|
|
181
210
|
runId: string;
|
package/dist/evals/client.js
CHANGED
|
@@ -309,6 +309,22 @@ export class EvaluationClient {
|
|
|
309
309
|
getJudgeBudget() {
|
|
310
310
|
return this.request("GET", "/judge-budget");
|
|
311
311
|
}
|
|
312
|
+
/** Register or refresh the fixed local agent key and revision. */
|
|
313
|
+
registerLocalAgent(input) {
|
|
314
|
+
return this.request("POST", "/local-agent-worker/register", input);
|
|
315
|
+
}
|
|
316
|
+
/** Claim a queued run for this agent and durable worker identity. */
|
|
317
|
+
claimLocalAgentRun(input) {
|
|
318
|
+
return this.request("POST", "/local-agent-worker/claim", input);
|
|
319
|
+
}
|
|
320
|
+
/** Refresh the lease of a claimed local run. */
|
|
321
|
+
heartbeatLocalAgentRun(input) {
|
|
322
|
+
return this.request("POST", "/local-agent-worker/runs/heartbeat", input);
|
|
323
|
+
}
|
|
324
|
+
/** Report acknowledged completion or an execution requiring attention. */
|
|
325
|
+
completeLocalAgentRun(input) {
|
|
326
|
+
return this.request("POST", "/local-agent-worker/runs/complete", input);
|
|
327
|
+
}
|
|
312
328
|
/** Creates the legacy execution-scoped generic MCP capability for one world. */
|
|
313
329
|
createSimulationMcpCapability(input) {
|
|
314
330
|
return this.request("POST", "/local-agent-worker/mcp-capability", input);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { HueClient } from "../client.js";
|
|
2
|
+
import type { EnvironmentClient } from "../environment/client.js";
|
|
3
|
+
import { type EnvironmentTool } from "../environment/tools.js";
|
|
4
|
+
import type { HueSpan } from "../types.js";
|
|
5
|
+
import { type ActualAgentManifestInputV2, type AttemptBaselineV2, type AttemptConnectionBundleV2, type RequestedAttemptProviderV2, type SurfaceBindingV2 } from "./attempt.js";
|
|
6
|
+
import type { EvaluationClient } from "./client.js";
|
|
7
|
+
import type { ExperimentCase, JsonValue, SimulationMcpCapability } from "./types.js";
|
|
8
|
+
export type McpSurfaceKeyV2 = Extract<SurfaceBindingV2["surfaceKey"], `${string}/mcp`>;
|
|
9
|
+
export type ActualAgentManifestResolverV2 = ActualAgentManifestInputV2 | ((context: {
|
|
10
|
+
config: JsonValue;
|
|
11
|
+
item: ExperimentCase;
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
}) => ActualAgentManifestInputV2 | Promise<ActualAgentManifestInputV2>);
|
|
14
|
+
export interface ProviderAttemptOptionsV2 {
|
|
15
|
+
actualAgentManifest?: ActualAgentManifestResolverV2;
|
|
16
|
+
requestedProviders?: RequestedAttemptProviderV2[];
|
|
17
|
+
mcpSurface?: {
|
|
18
|
+
providerInstanceKey: string;
|
|
19
|
+
surfaceKey: McpSurfaceKeyV2;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export type RequestedAttemptV2 = {
|
|
23
|
+
actualAgentManifest: ActualAgentManifestResolverV2;
|
|
24
|
+
requestedProviders: RequestedAttemptProviderV2[];
|
|
25
|
+
mcpSurface: {
|
|
26
|
+
providerInstanceKey: string;
|
|
27
|
+
surfaceKey: McpSurfaceKeyV2;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export type PinnedAttemptV2 = RequestedAttemptV2 & {
|
|
31
|
+
expectedAgentManifestDigest: AttemptBaselineV2["expectedAgentManifestDigest"];
|
|
32
|
+
};
|
|
33
|
+
export declare function requestedAttemptV2(options: ProviderAttemptOptionsV2): RequestedAttemptV2 | undefined;
|
|
34
|
+
export declare function pinRequestedAttemptV2(requested: RequestedAttemptV2, config: JsonValue): PinnedAttemptV2;
|
|
35
|
+
export type EnvironmentTargetProgress = {
|
|
36
|
+
type: "world_created" | "target_started" | "world_sealed";
|
|
37
|
+
environmentRunId: string;
|
|
38
|
+
} | {
|
|
39
|
+
type: "attempt_prepared";
|
|
40
|
+
environmentRunId: string;
|
|
41
|
+
bindingId: string;
|
|
42
|
+
status: "ready" | "environment_incomplete";
|
|
43
|
+
findingCodes: string[];
|
|
44
|
+
executionManifestDigest?: AttemptConnectionBundleV2["parity"]["executionManifestDigest"];
|
|
45
|
+
};
|
|
46
|
+
export interface EnvironmentTargetContext {
|
|
47
|
+
config: JsonValue;
|
|
48
|
+
item: ExperimentCase;
|
|
49
|
+
executionId: string;
|
|
50
|
+
environmentRunId: string;
|
|
51
|
+
trace: {
|
|
52
|
+
traceId: string;
|
|
53
|
+
spanId: string;
|
|
54
|
+
};
|
|
55
|
+
tools: Record<string, EnvironmentTool>;
|
|
56
|
+
mcp: SimulationMcpCapability;
|
|
57
|
+
connectionBundle?: AttemptConnectionBundleV2;
|
|
58
|
+
signal?: AbortSignal;
|
|
59
|
+
}
|
|
60
|
+
interface RunnerTargetContext {
|
|
61
|
+
config: JsonValue;
|
|
62
|
+
item: ExperimentCase;
|
|
63
|
+
executionId: string;
|
|
64
|
+
span: HueSpan;
|
|
65
|
+
}
|
|
66
|
+
export interface RunEnvironmentTargetOptions {
|
|
67
|
+
client: EvaluationClient;
|
|
68
|
+
environmentClient: EnvironmentClient;
|
|
69
|
+
hue: HueClient;
|
|
70
|
+
inputs: JsonValue;
|
|
71
|
+
context: RunnerTargetContext;
|
|
72
|
+
requested?: PinnedAttemptV2;
|
|
73
|
+
maxSteps?: number;
|
|
74
|
+
ttlSeconds?: number;
|
|
75
|
+
signal?: AbortSignal;
|
|
76
|
+
onProgress?(event: EnvironmentTargetProgress): void | Promise<void>;
|
|
77
|
+
target(inputs: JsonValue, context: EnvironmentTargetContext): JsonValue | undefined | Promise<JsonValue | undefined>;
|
|
78
|
+
}
|
|
79
|
+
/** One authoritative environment/provider lifecycle shared by direct simulations and
|
|
80
|
+
* outbound local workers. Credential-bearing connections stay in this call frame and
|
|
81
|
+
* are never returned to either runner's checkpoint state.
|
|
82
|
+
*/
|
|
83
|
+
export declare function runEnvironmentTarget(options: RunEnvironmentTargetOptions): Promise<JsonValue | undefined>;
|
|
84
|
+
export {};
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { bindEnvironmentTools } from "../environment/tools.js";
|
|
3
|
+
import { actualAgentManifestV2, attemptBaselineV2, projectMcpConnectionV2, requestedAttemptProvidersV2, validateAttemptConnectionBundleV2, } from "./attempt.js";
|
|
4
|
+
import { TargetCancelledError, TargetOutcomeUncertainError } from "./runner.js";
|
|
5
|
+
export function requestedAttemptV2(options) {
|
|
6
|
+
const requested = options.requestedProviders !== undefined;
|
|
7
|
+
const selected = options.mcpSurface !== undefined;
|
|
8
|
+
if (!requested && !selected) {
|
|
9
|
+
if (options.actualAgentManifest !== undefined)
|
|
10
|
+
throw new TypeError("actualAgentManifest requires requestedProviders and mcpSurface");
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
if (!requested || !selected)
|
|
14
|
+
throw new TypeError("requestedProviders and mcpSurface must be supplied together");
|
|
15
|
+
const requestedProviders = requestedAttemptProvidersV2.parse(options.requestedProviders);
|
|
16
|
+
const mcpSurface = options.mcpSurface;
|
|
17
|
+
const provider = requestedProviders.find((candidate) => candidate.providerInstanceKey === mcpSurface.providerInstanceKey);
|
|
18
|
+
if (!provider?.surfaceKeys.includes(mcpSurface.surfaceKey))
|
|
19
|
+
throw new TypeError("mcpSurface must identify an exactly requested MCP surface");
|
|
20
|
+
const actualAgentManifest = typeof options.actualAgentManifest === "function"
|
|
21
|
+
? options.actualAgentManifest
|
|
22
|
+
: actualAgentManifestV2.parse(options.actualAgentManifest);
|
|
23
|
+
return {
|
|
24
|
+
actualAgentManifest,
|
|
25
|
+
requestedProviders,
|
|
26
|
+
mcpSurface: { ...mcpSurface },
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function pinRequestedAttemptV2(requested, config) {
|
|
30
|
+
if (!config || typeof config !== "object" || Array.isArray(config))
|
|
31
|
+
throw new TypeError("Provider-profile simulations require an immutable V2 attempt baseline");
|
|
32
|
+
const source = config;
|
|
33
|
+
const baseline = attemptBaselineV2.safeParse(source.attemptBaselineV2);
|
|
34
|
+
if (!baseline.success) {
|
|
35
|
+
if (source.attemptBaselineV2 === undefined && source.attemptBaselineV1 !== undefined)
|
|
36
|
+
throw new TypeError("Legacy V1 attempts require a fresh experiment with a V2 baseline");
|
|
37
|
+
if (source.attemptBaselineV2 !== undefined)
|
|
38
|
+
throw new TypeError("The immutable V2 attempt baseline is invalid");
|
|
39
|
+
throw new TypeError("Provider-profile simulations require an immutable V2 attempt baseline");
|
|
40
|
+
}
|
|
41
|
+
return { ...requested, expectedAgentManifestDigest: baseline.data.expectedAgentManifestDigest };
|
|
42
|
+
}
|
|
43
|
+
/** Confirm a seal from the authoritative run after a lost acknowledgement. */
|
|
44
|
+
async function seal(client, runId, executionId, status) {
|
|
45
|
+
try {
|
|
46
|
+
await client.finishRun(runId, {
|
|
47
|
+
idempotencyKey: `execution:${executionId}:${status}`,
|
|
48
|
+
status,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const recovered = await client.getRun(runId).catch(() => undefined);
|
|
53
|
+
if (recovered?.status !== status)
|
|
54
|
+
throw new TargetOutcomeUncertainError(executionId, { cause: error });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** One authoritative environment/provider lifecycle shared by direct simulations and
|
|
58
|
+
* outbound local workers. Credential-bearing connections stay in this call frame and
|
|
59
|
+
* are never returned to either runner's checkpoint state.
|
|
60
|
+
*/
|
|
61
|
+
export async function runEnvironmentTarget(options) {
|
|
62
|
+
const { context } = options;
|
|
63
|
+
const environmentVersionId = context.item.environmentVersionId;
|
|
64
|
+
if (!environmentVersionId)
|
|
65
|
+
throw new Error("The simulation case has no pinned environment version");
|
|
66
|
+
const run = await options.environmentClient.createRun({
|
|
67
|
+
idempotencyKey: `execution:${context.executionId}`,
|
|
68
|
+
environmentVersionId,
|
|
69
|
+
executionId: context.executionId,
|
|
70
|
+
maxSteps: options.maxSteps,
|
|
71
|
+
ttlSeconds: options.ttlSeconds,
|
|
72
|
+
});
|
|
73
|
+
const progress = (event) => options.onProgress?.(event);
|
|
74
|
+
let finalized = false;
|
|
75
|
+
try {
|
|
76
|
+
await progress({ type: "world_created", environmentRunId: run.id });
|
|
77
|
+
if (options.signal?.aborted)
|
|
78
|
+
throw new TargetCancelledError();
|
|
79
|
+
const tools = bindEnvironmentTools({
|
|
80
|
+
hue: options.hue,
|
|
81
|
+
client: options.environmentClient,
|
|
82
|
+
run,
|
|
83
|
+
parentContext: context.span.context,
|
|
84
|
+
});
|
|
85
|
+
let connectionBundle;
|
|
86
|
+
let mcp;
|
|
87
|
+
if (options.requested) {
|
|
88
|
+
const actualManifest = actualAgentManifestV2.parse(typeof options.requested.actualAgentManifest === "function"
|
|
89
|
+
? await options.requested.actualAgentManifest({
|
|
90
|
+
config: structuredClone(context.config),
|
|
91
|
+
item: structuredClone(context.item),
|
|
92
|
+
signal: options.signal,
|
|
93
|
+
})
|
|
94
|
+
: options.requested.actualAgentManifest);
|
|
95
|
+
let prepared;
|
|
96
|
+
try {
|
|
97
|
+
prepared = await options.client.prepareAttempt({
|
|
98
|
+
schemaVersion: 2,
|
|
99
|
+
idempotencyKey: randomUUID(),
|
|
100
|
+
executionId: context.executionId,
|
|
101
|
+
environmentRunId: run.id,
|
|
102
|
+
expectedAgentManifestDigest: options.requested.expectedAgentManifestDigest,
|
|
103
|
+
actualManifest,
|
|
104
|
+
requestedProviders: options.requested.requestedProviders,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
// A transport failure or malformed credential-bearing response may follow a
|
|
109
|
+
// committed decision. The runner's running checkpoint prevents reacquisition
|
|
110
|
+
// and target replay on resume.
|
|
111
|
+
throw new TargetOutcomeUncertainError(context.executionId, { cause: error });
|
|
112
|
+
}
|
|
113
|
+
await progress({
|
|
114
|
+
type: "attempt_prepared",
|
|
115
|
+
environmentRunId: run.id,
|
|
116
|
+
bindingId: prepared.status === "ready" ? prepared.bundle.bindingId : prepared.bindingId,
|
|
117
|
+
status: prepared.status,
|
|
118
|
+
findingCodes: prepared.preflightReport.findings.map((finding) => finding.code),
|
|
119
|
+
...(prepared.status === "ready"
|
|
120
|
+
? { executionManifestDigest: prepared.bundle.parity.executionManifestDigest }
|
|
121
|
+
: {}),
|
|
122
|
+
});
|
|
123
|
+
if (prepared.status === "environment_incomplete") {
|
|
124
|
+
await seal(options.environmentClient, run.id, context.executionId, "completed");
|
|
125
|
+
finalized = true;
|
|
126
|
+
await Promise.resolve(progress({ type: "world_sealed", environmentRunId: run.id })).catch(() => undefined);
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
connectionBundle = validateAttemptConnectionBundleV2(prepared.bundle, {
|
|
130
|
+
requireFresh: true,
|
|
131
|
+
});
|
|
132
|
+
const projected = projectMcpConnectionV2(connectionBundle, options.requested.mcpSurface.providerInstanceKey);
|
|
133
|
+
if (!projected)
|
|
134
|
+
throw new TypeError("The prepared attempt has no selected MCP surface");
|
|
135
|
+
mcp = projected;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
mcp = await options.client.createSimulationMcpCapability({
|
|
139
|
+
runId: run.id,
|
|
140
|
+
executionId: context.executionId,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if (options.signal?.aborted)
|
|
144
|
+
throw new TargetCancelledError();
|
|
145
|
+
await progress({ type: "target_started", environmentRunId: run.id });
|
|
146
|
+
const output = await options.target(options.inputs, {
|
|
147
|
+
config: context.config,
|
|
148
|
+
item: context.item,
|
|
149
|
+
executionId: context.executionId,
|
|
150
|
+
environmentRunId: run.id,
|
|
151
|
+
trace: { traceId: context.span.traceId, spanId: context.span.spanId },
|
|
152
|
+
tools,
|
|
153
|
+
mcp,
|
|
154
|
+
...(connectionBundle ? { connectionBundle } : {}),
|
|
155
|
+
signal: options.signal,
|
|
156
|
+
});
|
|
157
|
+
await seal(options.environmentClient, run.id, context.executionId, "completed");
|
|
158
|
+
finalized = true;
|
|
159
|
+
await Promise.resolve(progress({ type: "world_sealed", environmentRunId: run.id })).catch(() => undefined);
|
|
160
|
+
return output;
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (error instanceof TargetOutcomeUncertainError || finalized)
|
|
164
|
+
throw error;
|
|
165
|
+
let environmentIncomplete;
|
|
166
|
+
try {
|
|
167
|
+
environmentIncomplete =
|
|
168
|
+
(await options.environmentClient.getRun(run.id)).validity === "environment_incomplete";
|
|
169
|
+
}
|
|
170
|
+
catch (inspectionError) {
|
|
171
|
+
throw new TargetOutcomeUncertainError(context.executionId, {
|
|
172
|
+
cause: new AggregateError([error, inspectionError]),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
if (environmentIncomplete) {
|
|
176
|
+
try {
|
|
177
|
+
await seal(options.environmentClient, run.id, context.executionId, "completed");
|
|
178
|
+
}
|
|
179
|
+
catch (finalizationError) {
|
|
180
|
+
throw new TargetOutcomeUncertainError(context.executionId, {
|
|
181
|
+
cause: new AggregateError([error, finalizationError]),
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
finalized = true;
|
|
185
|
+
await Promise.resolve(progress({ type: "world_sealed", environmentRunId: run.id })).catch(() => undefined);
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
await seal(options.environmentClient, run.id, context.executionId, "abandoned");
|
|
190
|
+
}
|
|
191
|
+
catch (finalizationError) {
|
|
192
|
+
throw new TargetOutcomeUncertainError(context.executionId, {
|
|
193
|
+
cause: new AggregateError([error, finalizationError]),
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
await Promise.resolve(progress({ type: "world_sealed", environmentRunId: run.id })).catch(() => undefined);
|
|
197
|
+
if (options.signal?.aborted && !(error instanceof TargetCancelledError))
|
|
198
|
+
throw new TargetCancelledError();
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
}
|